From 44238a9f923080cdc53c7573d8b96ecad95e3604 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Wed, 25 Jun 2014 01:30:59 +0200 Subject: [PATCH] submodules are automatically indexed without actually importing them. fixes #413. However, this is not a 100% correct Python behavior. Python behavior would be to follow ALL imports in all modules (recursively) and check if the module was imported. However, that's a lot of work, that would slow down autocompletion. For now it's better to have no false positives in flaking and to ignore a few attribute errors. --- jedi/evaluate/finder.py | 6 +++--- jedi/evaluate/helpers.py | 7 +++++++ jedi/evaluate/representation.py | 19 +++++++++++++++++++ test/completion/usages.py | 4 ++-- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 52a31032..100046f3 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -73,8 +73,8 @@ class NameFinder(object): gen = self.scope.scope_names_generator except AttributeError: if isinstance(self.scope, er.Class): - # classes are only available directly via chaining? - # strange stuff... + # Classes don't have a positional limitation of their + # state. It's the whole class. names = self.scope.get_defined_names() else: names = _get_defined_names_for_position(self.scope, self.position) @@ -118,7 +118,7 @@ class NameFinder(object): self._last_filter_name_scope = name_list_scope scope_txt = (self.scope if self.scope == name_list_scope - else ('%s-%s' % self.scope, name_list_scope)) + else '%s-%s' % (self.scope, name_list_scope)) debug.dbg('finder.filter_name "%s" in (%s): %s@%s', self.name_str, scope_txt, u(result), self.position) return result diff --git a/jedi/evaluate/helpers.py b/jedi/evaluate/helpers.py index 315a6842..7cb30eab 100644 --- a/jedi/evaluate/helpers.py +++ b/jedi/evaluate/helpers.py @@ -186,6 +186,13 @@ class FakeStatement(pr.Statement): self.set_expression_list(expression_list) +class FakeImport(pr.Import): + def __init__(self, name, parent): + p = 0, 0 + super(FakeImport, self).__init__(FakeSubModule, p, p, name) + self.parent = parent + + class FakeName(pr.Name): def __init__(self, name_or_names, parent=None): p = 0, 0 diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index 86561c12..ce6c561b 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -11,6 +11,8 @@ So, why is there also a ``Class`` class here? Well, there are decorators and they change classes in Python 3. """ import copy +import os +import pkgutil from jedi._compatibility import use_metaclass, unicode from jedi.parser import representation as pr @@ -539,6 +541,10 @@ class ModuleWrapper(pr.Module): self._evaluator = evaluator self._module = module + def scope_names_generator(self): + yield self, self.get_defined_names() + yield self, self._sub_modules() + @memoize_default() def get_defined_names(self): names = ['__file__', '__package__', '__doc__', '__name__', '__version__'] @@ -547,6 +553,19 @@ class ModuleWrapper(pr.Module): module_attributes = [helpers.FakeName(n, parent) for n in names] return self._module.get_defined_names() + module_attributes + @memoize_default() + def _sub_modules(self): + path = self._module.path + names = [] + if path.endswith(os.path.sep + '__init__.py'): + mods = pkgutil.iter_modules([os.path.dirname(path)]) + for module_loader, name, is_pkg in mods: + name = helpers.FakeName(name) + imp = helpers.FakeImport(name, self) + name.parent = imp + names.append(name) + return names + def __getattr__(self, name): return getattr(self._module, name) diff --git a/test/completion/usages.py b/test/completion/usages.py index 1132a1bc..2f944ea0 100644 --- a/test/completion/usages.py +++ b/test/completion/usages.py @@ -80,7 +80,7 @@ import module_not_exists module_not_exists -#< ('rename1', 1,0), (0,24), (3,0), (6,17), ('rename2', 4,5), (10,17), (13,17) +#< ('rename1', 1,0), (0,24), (3,0), (6,17), ('rename2', 4,5), (10,17), (13,17), ('imports', 68, 16) from import_tree import rename1 #< (0,8), ('rename1',3,0), ('rename2',4,20), ('rename2',6,0), (3,32), (7,32), (4,0) @@ -90,7 +90,7 @@ rename1.abc from import_tree.rename1 import abc abc -#< 20 ('rename1', 1,0), ('rename2', 4,5), (-10,24), (-7,0), (-4,17), (0,17), (3,17) +#< 20 ('rename1', 1,0), ('rename2', 4,5), (-10,24), (-7,0), (-4,17), (0,17), (3,17), ('imports', 68, 16) from import_tree.rename1 import abc #< (0, 32),