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.
This commit is contained in:
Dave Halter
2014-06-25 01:30:59 +02:00
parent 034a818863
commit 44238a9f92
4 changed files with 31 additions and 5 deletions

View File

@@ -73,8 +73,8 @@ class NameFinder(object):
gen = self.scope.scope_names_generator gen = self.scope.scope_names_generator
except AttributeError: except AttributeError:
if isinstance(self.scope, er.Class): if isinstance(self.scope, er.Class):
# classes are only available directly via chaining? # Classes don't have a positional limitation of their
# strange stuff... # state. It's the whole class.
names = self.scope.get_defined_names() names = self.scope.get_defined_names()
else: else:
names = _get_defined_names_for_position(self.scope, self.position) 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 self._last_filter_name_scope = name_list_scope
scope_txt = (self.scope if self.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, debug.dbg('finder.filter_name "%s" in (%s): %s@%s', self.name_str,
scope_txt, u(result), self.position) scope_txt, u(result), self.position)
return result return result

View File

@@ -186,6 +186,13 @@ class FakeStatement(pr.Statement):
self.set_expression_list(expression_list) 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): class FakeName(pr.Name):
def __init__(self, name_or_names, parent=None): def __init__(self, name_or_names, parent=None):
p = 0, 0 p = 0, 0

View File

@@ -11,6 +11,8 @@ So, why is there also a ``Class`` class here? Well, there are decorators and
they change classes in Python 3. they change classes in Python 3.
""" """
import copy import copy
import os
import pkgutil
from jedi._compatibility import use_metaclass, unicode from jedi._compatibility import use_metaclass, unicode
from jedi.parser import representation as pr from jedi.parser import representation as pr
@@ -539,6 +541,10 @@ class ModuleWrapper(pr.Module):
self._evaluator = evaluator self._evaluator = evaluator
self._module = module self._module = module
def scope_names_generator(self):
yield self, self.get_defined_names()
yield self, self._sub_modules()
@memoize_default() @memoize_default()
def get_defined_names(self): def get_defined_names(self):
names = ['__file__', '__package__', '__doc__', '__name__', '__version__'] names = ['__file__', '__package__', '__doc__', '__name__', '__version__']
@@ -547,6 +553,19 @@ class ModuleWrapper(pr.Module):
module_attributes = [helpers.FakeName(n, parent) for n in names] module_attributes = [helpers.FakeName(n, parent) for n in names]
return self._module.get_defined_names() + module_attributes 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): def __getattr__(self, name):
return getattr(self._module, name) return getattr(self._module, name)

View File

@@ -80,7 +80,7 @@ import module_not_exists
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 from import_tree import rename1
#< (0,8), ('rename1',3,0), ('rename2',4,20), ('rename2',6,0), (3,32), (7,32), (4,0) #< (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 from import_tree.rename1 import abc
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 from import_tree.rename1 import abc
#< (0, 32), #< (0, 32),