forked from VimPlug/jedi
Add a py__name__ call to modules. This makes listing the qualified names of modules possible (in combination with the module_name_cache). Fixes #519.
This commit is contained in:
@@ -107,10 +107,15 @@ class Script(object):
|
|||||||
self._grammar = load_grammar('grammar%s.%s' % sys.version_info[:2])
|
self._grammar = load_grammar('grammar%s.%s' % sys.version_info[:2])
|
||||||
self._user_context = UserContext(self.source, self._pos)
|
self._user_context = UserContext(self.source, self._pos)
|
||||||
self._parser = UserContextParser(self._grammar, self.source, path,
|
self._parser = UserContextParser(self._grammar, self.source, path,
|
||||||
self._pos, self._user_context)
|
self._pos, self._user_context,
|
||||||
|
self._parsed_callback)
|
||||||
self._evaluator = Evaluator(self._grammar)
|
self._evaluator = Evaluator(self._grammar)
|
||||||
debug.speed('init')
|
debug.speed('init')
|
||||||
|
|
||||||
|
def _parsed_callback(self, parser):
|
||||||
|
module = er.wrap(self._evaluator, parser.module)
|
||||||
|
self._evaluator.module_name_cache[module] = unicode(module.name)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def source_path(self):
|
def source_path(self):
|
||||||
"""
|
"""
|
||||||
@@ -135,12 +140,12 @@ class Script(object):
|
|||||||
def get_completions(user_stmt, bs):
|
def get_completions(user_stmt, bs):
|
||||||
# TODO this closure is ugly. it also doesn't work with
|
# TODO this closure is ugly. it also doesn't work with
|
||||||
# simple_complete (used for Interpreter), somehow redo.
|
# simple_complete (used for Interpreter), somehow redo.
|
||||||
module = self._parser.module()
|
module = self._evaluator.wrap(self._parser.module())
|
||||||
names, level, only_modules, unfinished_dotted = \
|
names, level, only_modules, unfinished_dotted = \
|
||||||
helpers.check_error_statements(module, self._pos)
|
helpers.check_error_statements(module, self._pos)
|
||||||
completion_names = []
|
completion_names = []
|
||||||
if names is not None:
|
if names is not None:
|
||||||
imp_names = [n for n in names if n.end_pos < self._pos]
|
imp_names = tuple(n for n in names if n.end_pos < self._pos)
|
||||||
i = imports.get_importer(self._evaluator, imp_names, module, level)
|
i = imports.get_importer(self._evaluator, imp_names, module, level)
|
||||||
completion_names = i.completion_names(self._evaluator, only_modules)
|
completion_names = i.completion_names(self._evaluator, only_modules)
|
||||||
|
|
||||||
@@ -586,7 +591,7 @@ class Interpreter(Script):
|
|||||||
# changing).
|
# changing).
|
||||||
self._parser = UserContextParser(self._grammar, self.source,
|
self._parser = UserContextParser(self._grammar, self.source,
|
||||||
self._orig_path, self._pos,
|
self._orig_path, self._pos,
|
||||||
self._user_context,
|
self._user_context, self._parsed_callback,
|
||||||
use_fast_parser=False)
|
use_fast_parser=False)
|
||||||
interpreter.add_namespaces_to_parser(self._evaluator, namespaces,
|
interpreter.add_namespaces_to_parser(self._evaluator, namespaces,
|
||||||
self._parser.module())
|
self._parser.module())
|
||||||
|
|||||||
@@ -162,15 +162,26 @@ class BaseDefinition(object):
|
|||||||
return string
|
return string
|
||||||
|
|
||||||
def _path(self):
|
def _path(self):
|
||||||
"""The module path."""
|
"""The path to a module/class/function definition."""
|
||||||
path = []
|
path = []
|
||||||
par = self._definition
|
par = self._definition
|
||||||
while par is not None:
|
while par is not None:
|
||||||
if isinstance(par, pr.Import):
|
if isinstance(par, pr.Import):
|
||||||
path += imports.ImportWrapper(self._evaluator, self._name).import_path
|
path += imports.ImportWrapper(self._evaluator, self._name).import_path
|
||||||
break
|
break
|
||||||
with common.ignored(AttributeError):
|
try:
|
||||||
path.insert(0, par.name)
|
name = par.name
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if isinstance(par, er.ModuleWrapper):
|
||||||
|
#module = er.wrap(self._evaluator, par)
|
||||||
|
# TODO just make the path dotted from the beginning, we
|
||||||
|
# shouldn't really split here.
|
||||||
|
path[0:0] = par.py__name__().split('.')
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
path.insert(0, unicode(name))
|
||||||
par = par.parent
|
par = par.parent
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|||||||
@@ -82,12 +82,18 @@ class Evaluator(object):
|
|||||||
def __init__(self, grammar):
|
def __init__(self, grammar):
|
||||||
self.grammar = grammar
|
self.grammar = grammar
|
||||||
self.memoize_cache = {} # for memoize decorators
|
self.memoize_cache = {} # for memoize decorators
|
||||||
|
# To memorize module names (that are assigned to modules by the import
|
||||||
|
# logic) -> a ``__name__`` is given.
|
||||||
|
self.module_name_cache = {}
|
||||||
self.import_cache = {} # like `sys.modules`.
|
self.import_cache = {} # like `sys.modules`.
|
||||||
self.compiled_cache = {} # see `compiled.create()`
|
self.compiled_cache = {} # see `compiled.create()`
|
||||||
self.recursion_detector = recursion.RecursionDetector()
|
self.recursion_detector = recursion.RecursionDetector()
|
||||||
self.execution_recursion_detector = recursion.ExecutionRecursionDetector()
|
self.execution_recursion_detector = recursion.ExecutionRecursionDetector()
|
||||||
self.analysis = []
|
self.analysis = []
|
||||||
|
|
||||||
|
def wrap(self, element):
|
||||||
|
return er.wrap(self, element)
|
||||||
|
|
||||||
def find_types(self, scope, name_str, position=None, search_global=False,
|
def find_types(self, scope, name_str, position=None, search_global=False,
|
||||||
is_goto=False):
|
is_goto=False):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class ModuleNotFound(Exception):
|
|||||||
|
|
||||||
def completion_names(evaluator, imp, pos):
|
def completion_names(evaluator, imp, pos):
|
||||||
name = imp.name_for_position(pos)
|
name = imp.name_for_position(pos)
|
||||||
module = imp.get_parent_until()
|
module = evaluator.wrap(imp.get_parent_until())
|
||||||
if name is None:
|
if name is None:
|
||||||
level = 0
|
level = 0
|
||||||
for node in imp.children:
|
for node in imp.children:
|
||||||
@@ -79,7 +79,7 @@ class ImportWrapper(pr.Base):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
module = self._import.get_parent_until()
|
module = self._evaluator.wrap(self._import.get_parent_until())
|
||||||
import_path = self._import.path_for_name(self._name)
|
import_path = self._import.path_for_name(self._name)
|
||||||
importer = get_importer(self._evaluator, tuple(import_path),
|
importer = get_importer(self._evaluator, tuple(import_path),
|
||||||
module, self._import.level)
|
module, self._import.level)
|
||||||
@@ -157,16 +157,25 @@ def get_importer(evaluator, import_path, module, level=0):
|
|||||||
Checks the evaluator caches first, which resembles the ``sys.modules``
|
Checks the evaluator caches first, which resembles the ``sys.modules``
|
||||||
cache and speeds up libraries like ``numpy``.
|
cache and speeds up libraries like ``numpy``.
|
||||||
"""
|
"""
|
||||||
import_path = tuple(import_path) # We use it as hash in the import cache.
|
if level:
|
||||||
if level != 0:
|
base = module.py__name__().split('.')
|
||||||
# Only absolute imports should be cached. Otherwise we have a mess.
|
if level > len(base):
|
||||||
# TODO Maybe calculate the absolute import and save it here?
|
# TODO add import error.
|
||||||
|
debug.warning('Attempted relative import beyond top-level package.')
|
||||||
|
# TODO this is just in the wrong place.
|
||||||
return _Importer(evaluator, import_path, module, level)
|
return _Importer(evaluator, import_path, module, level)
|
||||||
|
else:
|
||||||
|
# Here we basically rewrite the level to 0.
|
||||||
|
import_path = tuple(base) + import_path
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
check_import_path = tuple(unicode(i) for i in import_path)
|
||||||
try:
|
try:
|
||||||
return evaluator.import_cache[import_path]
|
return evaluator.import_cache[check_import_path]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
importer = _Importer(evaluator, import_path, module, level)
|
importer = _Importer(evaluator, import_path, module, level=0)
|
||||||
evaluator.import_cache[import_path] = importer
|
evaluator.import_cache[check_import_path] = importer
|
||||||
return importer
|
return importer
|
||||||
|
|
||||||
|
|
||||||
@@ -280,10 +289,11 @@ class _Importer(object):
|
|||||||
else:
|
else:
|
||||||
sys_path_mod = list(get_sys_path())
|
sys_path_mod = list(get_sys_path())
|
||||||
|
|
||||||
from jedi.evaluate.representation import ModuleWrapper
|
|
||||||
module, rest = self._follow_sys_path(sys_path_mod)
|
module, rest = self._follow_sys_path(sys_path_mod)
|
||||||
if isinstance(module, pr.Module):
|
if isinstance(module, pr.Module):
|
||||||
return ModuleWrapper(self._evaluator, module), rest
|
# TODO this looks strange. do we really need to check and should
|
||||||
|
# this transformation happen here?
|
||||||
|
return self._evaluator.wrap(module), rest
|
||||||
return module, rest
|
return module, rest
|
||||||
|
|
||||||
def namespace_packages(self, found_path, import_path):
|
def namespace_packages(self, found_path, import_path):
|
||||||
@@ -375,6 +385,11 @@ class _Importer(object):
|
|||||||
path = current_namespace[1]
|
path = current_namespace[1]
|
||||||
is_package_directory = current_namespace[2]
|
is_package_directory = current_namespace[2]
|
||||||
|
|
||||||
|
module_names = list(self.str_import_path)
|
||||||
|
for _ in rest:
|
||||||
|
module_names.pop()
|
||||||
|
module_name = '.'.join(module_names)
|
||||||
|
|
||||||
f = None
|
f = None
|
||||||
if is_package_directory or current_namespace[0]:
|
if is_package_directory or current_namespace[0]:
|
||||||
# is a directory module
|
# is a directory module
|
||||||
@@ -393,9 +408,11 @@ class _Importer(object):
|
|||||||
else:
|
else:
|
||||||
source = current_namespace[0].read()
|
source = current_namespace[0].read()
|
||||||
current_namespace[0].close()
|
current_namespace[0].close()
|
||||||
return _load_module(self._evaluator, path, source, sys_path=sys_path), rest
|
return _load_module(self._evaluator, path, source,
|
||||||
|
sys_path=sys_path, module_name=module_name), rest
|
||||||
else:
|
else:
|
||||||
return _load_module(self._evaluator, name=path, sys_path=sys_path), rest
|
return _load_module(self._evaluator, name=path,
|
||||||
|
sys_path=sys_path, module_name=module_name), rest
|
||||||
|
|
||||||
def _generate_name(self, name):
|
def _generate_name(self, name):
|
||||||
return helpers.FakeName(name, parent=self.module)
|
return helpers.FakeName(name, parent=self.module)
|
||||||
@@ -482,13 +499,13 @@ class _Importer(object):
|
|||||||
'__init__.py')
|
'__init__.py')
|
||||||
if os.path.exists(rel_path):
|
if os.path.exists(rel_path):
|
||||||
module = _load_module(self._evaluator, rel_path)
|
module = _load_module(self._evaluator, rel_path)
|
||||||
module = er.wrap(self._evaluator, module)
|
module = self._evaluator.wrap(module)
|
||||||
for names_dict in module.names_dicts(search_global=False):
|
for names_dict in module.names_dicts(search_global=False):
|
||||||
names += chain.from_iterable(names_dict.values())
|
names += chain.from_iterable(names_dict.values())
|
||||||
return names
|
return names
|
||||||
|
|
||||||
|
|
||||||
def _load_module(evaluator, path=None, source=None, name=None, sys_path=None):
|
def _load_module(evaluator, path=None, source=None, name=None, sys_path=None, module_name=None):
|
||||||
def load(source):
|
def load(source):
|
||||||
dotted_path = path and compiled.dotted_from_fs_path(path, sys_path)
|
dotted_path = path and compiled.dotted_from_fs_path(path, sys_path)
|
||||||
if path is not None and path.endswith('.py') \
|
if path is not None and path.endswith('.py') \
|
||||||
@@ -501,10 +518,15 @@ def _load_module(evaluator, path=None, source=None, name=None, sys_path=None):
|
|||||||
p = path or name
|
p = path or name
|
||||||
p = fast.FastParser(evaluator.grammar, common.source_to_unicode(source), p)
|
p = fast.FastParser(evaluator.grammar, common.source_to_unicode(source), p)
|
||||||
cache.save_parser(path, name, p)
|
cache.save_parser(path, name, p)
|
||||||
|
|
||||||
return p.module
|
return p.module
|
||||||
|
|
||||||
cached = cache.load_parser(path, name)
|
cached = cache.load_parser(path, name)
|
||||||
return load(source) if cached is None else cached.module
|
module = load(source) if cached is None else cached.module
|
||||||
|
# TODO return mod instead of just something.
|
||||||
|
module = evaluator.wrap(module)
|
||||||
|
evaluator.module_name_cache[module] = module_name
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
def get_modules_containing_name(evaluator, mods, name):
|
def get_modules_containing_name(evaluator, mods, name):
|
||||||
|
|||||||
@@ -759,6 +759,9 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, pr.Module, Wrapper)):
|
|||||||
def name(self):
|
def name(self):
|
||||||
return helpers.FakeName(unicode(self.base.name), self, (1, 0))
|
return helpers.FakeName(unicode(self.base.name), self, (1, 0))
|
||||||
|
|
||||||
|
def py__name__(self):
|
||||||
|
return self._evaluator.module_name_cache[self]
|
||||||
|
|
||||||
@memoize_default()
|
@memoize_default()
|
||||||
def _sub_modules_dict(self):
|
def _sub_modules_dict(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -266,13 +266,14 @@ class UserContext(object):
|
|||||||
|
|
||||||
class UserContextParser(object):
|
class UserContextParser(object):
|
||||||
def __init__(self, grammar, source, path, position, user_context,
|
def __init__(self, grammar, source, path, position, user_context,
|
||||||
use_fast_parser=True):
|
parser_done_callback, use_fast_parser=True):
|
||||||
self._grammar = grammar
|
self._grammar = grammar
|
||||||
self._source = source
|
self._source = source
|
||||||
self._path = path and os.path.abspath(path)
|
self._path = path and os.path.abspath(path)
|
||||||
self._position = position
|
self._position = position
|
||||||
self._user_context = user_context
|
self._user_context = user_context
|
||||||
self._use_fast_parser = use_fast_parser
|
self._use_fast_parser = use_fast_parser
|
||||||
|
self._parser_done_callback = parser_done_callback
|
||||||
|
|
||||||
@cache.underscore_memoization
|
@cache.underscore_memoization
|
||||||
def _parser(self):
|
def _parser(self):
|
||||||
@@ -283,6 +284,7 @@ class UserContextParser(object):
|
|||||||
cache.save_parser(self._path, None, parser, pickling=False)
|
cache.save_parser(self._path, None, parser, pickling=False)
|
||||||
else:
|
else:
|
||||||
parser = Parser(self._grammar, self._source, self._path)
|
parser = Parser(self._grammar, self._source, self._path)
|
||||||
|
self._parser_done_callback(parser)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
@cache.underscore_memoization
|
@cache.underscore_memoization
|
||||||
|
|||||||
@@ -78,3 +78,14 @@ class TestFullDefinedName(TestCase):
|
|||||||
from os.path import join
|
from os.path import join
|
||||||
from os import path as opath
|
from os import path as opath
|
||||||
""", ['os', 'os.path', 'os.path.join', 'os.path'])
|
""", ['os', 'os.path', 'os.path.join', 'os.path'])
|
||||||
|
|
||||||
|
|
||||||
|
def test_sub_module():
|
||||||
|
"""
|
||||||
|
``full_name needs to check sys.path to actually find it's real path module
|
||||||
|
path.
|
||||||
|
"""
|
||||||
|
defs = jedi.Script('from jedi.api import classes; classes').goto_definitions()
|
||||||
|
assert [d.full_name for d in defs] == ['jedi.api.classes']
|
||||||
|
defs = jedi.Script('import jedi.api; jedi.api').goto_definitions()
|
||||||
|
assert [d.full_name for d in defs] == ['jedi.api']
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ def test_user_statement_on_import():
|
|||||||
" time)")
|
" time)")
|
||||||
|
|
||||||
for pos in [(2, 1), (2, 4)]:
|
for pos in [(2, 1), (2, 4)]:
|
||||||
p = UserContextParser(load_grammar(), s, None, pos, None).user_stmt()
|
p = UserContextParser(load_grammar(), s, None, pos, None, lambda x: 1).user_stmt()
|
||||||
assert isinstance(p, pt.Import)
|
assert isinstance(p, pt.Import)
|
||||||
assert [str(n) for n in p.get_defined_names()] == ['time']
|
assert [str(n) for n in p.get_defined_names()] == ['time']
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user