diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index 633a7a44..6f93f2e0 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -167,17 +167,17 @@ class ImportWrapper(pr.Base): if self.import_path: try: - scope, rest = self._importer.follow_file_system() + module, rest = self._importer.follow_file_system() except ModuleNotFound as e: analysis.add(self._evaluator, 'import-error', e.name_part) return [] if self.import_stmt.is_nested() and not self.nested_resolve: - scopes = [NestedImportModule(scope, self.import_stmt)] + scopes = [NestedImportModule(module, self.import_stmt)] else: - scopes = [scope] + scopes = [module] - star_imports = remove_star_imports(self._evaluator, scope) + star_imports = remove_star_imports(self._evaluator, module) if star_imports: scopes = [StarImportModule(scopes[0], star_imports)] @@ -190,7 +190,7 @@ class ImportWrapper(pr.Base): # ``os.path``, because it's a very important one in Python # that is being achieved by messing with ``sys.modules`` in # ``os``. - scopes = self._evaluator.follow_path(iter(rest), [scope], scope) + scopes = self._evaluator.follow_path(iter(rest), [module], module) elif rest: if is_goto: scopes = list(chain.from_iterable( @@ -251,7 +251,8 @@ class StarImportModule(pr.Module): self.star_import_modules = star_import_modules def scope_names_generator(self): - yield self._module, self._module.get_defined_names() + for module, names in self._module.scope_names_generator(): + yield module, names for s in self.star_import_modules: yield s, s.get_defined_names() @@ -360,7 +361,11 @@ class _Importer(object): else: sys_path_mod = list(get_sys_path()) - return self._follow_sys_path(sys_path_mod) + from jedi.evaluate.representation import ModuleWrapper + module, rest = self._follow_sys_path(sys_path_mod) + if isinstance(module, pr.Module): + return ModuleWrapper(self._evaluator, module), rest + return module, rest def namespace_packages(self, found_path, import_path): """ @@ -475,11 +480,6 @@ def follow_imports(evaluator, scopes): for s in scopes: if isinstance(s, pr.Import): for r in ImportWrapper(evaluator, s).follow(): - if isinstance(r, pr.Module) and not isinstance(r, - StarImportModule): - # TODO This check is strange and feels wrong somehow. Change it. - from jedi.evaluate import representation as er - r = er.ModuleWrapper(evaluator, r) result.append(r) else: result.append(s) diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index 288e18ee..9be55b3c 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -535,7 +535,7 @@ class FunctionExecution(Executable): return "<%s of %s>" % (type(self).__name__, self.base) -class ModuleWrapper(pr.Module): +class ModuleWrapper(use_metaclass(CachedMetaClass, pr.Module)): def __init__(self, evaluator, module): self._evaluator = evaluator self._module = module diff --git a/test/static_analysis/attribute_error.py b/test/static_analysis/attribute_error.py index 383d38a2..7ceb9397 100644 --- a/test/static_analysis/attribute_error.py +++ b/test/static_analysis/attribute_error.py @@ -100,3 +100,14 @@ string = '%s %s' % (1, 2) # Shouldn't raise an error, because `string` is really just a string, not an # array or something. string.upper + +# ----------------- +# imports +# ----------------- + +# Star imports and the like in modules should not cause attribute errors in +# this module. +import import_tree + +import_tree.a +import_tree.b diff --git a/test/static_analysis/import_tree/__init__.py b/test/static_analysis/import_tree/__init__.py new file mode 100644 index 00000000..cb485f13 --- /dev/null +++ b/test/static_analysis/import_tree/__init__.py @@ -0,0 +1,5 @@ +""" +Another import tree, this time not for completion, but static analysis. +""" + +from .a import * diff --git a/test/static_analysis/import_tree/a.py b/test/static_analysis/import_tree/a.py new file mode 100644 index 00000000..b02981cc --- /dev/null +++ b/test/static_analysis/import_tree/a.py @@ -0,0 +1 @@ +from . import b diff --git a/test/static_analysis/import_tree/b.py b/test/static_analysis/import_tree/b.py new file mode 100644 index 00000000..e69de29b