diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index 6aa11f2c..de1d24bb 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -204,11 +204,11 @@ class Script(object): scopes = list(self._prepare_goto(path, True)) except NotFoundError: scopes = [] - scope_generator = get_names_of_scope(self._evaluator, - self._parser.user_scope(), - self._pos) + scope_names_generator = get_names_of_scope(self._evaluator, + self._parser.user_scope(), + self._pos) completions = [] - for scope, name_list in scope_generator: + for scope, name_list in scope_names_generator: for c in name_list: completions.append((c, scope)) else: diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 5f385657..1ceccc2d 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -44,9 +44,11 @@ class NameFinder(object): if search_global: return get_names_of_scope(self._evaluator, self.scope, self.position) else: - if isinstance(self.scope, er.Instance): - return self.scope.scope_generator() - else: + try: + # Use scope generators if parts of it (e.g. sub classes or star + # imports). + gen = self.scope.scope_names_generator + except AttributeError: if isinstance(self.scope, er.Class): # classes are only available directly via chaining? # strange stuff... @@ -54,14 +56,16 @@ class NameFinder(object): else: names = _get_defined_names_for_position(self.scope, self.position) return iter([(self.scope, names)]) + else: + return gen() - def filter_name(self, scope_generator): + def filter_name(self, scope_names_generator): """ Filters all variables of a scope (which are defined in the - `scope_generator`), until the name fits. + `scope_names_generator`), until the name fits. """ result = [] - for nscope, name_list in scope_generator: + for nscope, name_list in scope_names_generator: break_scopes = [] if not isinstance(nscope, compiled.CompiledObject): # Here is the position stuff happening (sorting of variables). @@ -448,7 +452,7 @@ def get_names_of_scope(evaluator, scope, position=None, star_search=True, includ and scope.type() == 'class' and in_func_scope != scope): try: if isinstance(scope, er.Instance): - for g in scope.scope_generator(): + for g in scope.scope_names_generator(): yield g else: yield scope, _get_defined_names_for_position(scope, position, in_func_scope) diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index 43388e20..59e73a7b 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -178,7 +178,9 @@ class ImportWrapper(pr.Base): else: scopes = [scope] - scopes += remove_star_imports(self._evaluator, scope) + star_imports = remove_star_imports(self._evaluator, scope) + if star_imports: + scopes = [StarImportModule(scopes[0], star_imports)] # follow the rest of the import (not FS -> classes, functions) if len(rest) > 1 or rest and self.is_like_search: @@ -235,8 +237,33 @@ class NestedImportModule(pr.Module): return getattr(self._module, name) def __repr__(self): - return "<%s: %s>" % (self.__class__.__name__, - self._module) + return "<%s: %s>" % (self.__class__.__name__, self._module) + + +class StarImportModule(pr.Module): + """ + Used if a module contains star imports. + """ + def __init__(self, module, star_import_modules): + self._module = module + self.star_import_modules = star_import_modules + + def scope_names_generator(self): + yield self._module, self._module.get_defined_names() + for s in self.star_import_modules: + yield s, s.get_defined_names() + + def get_defined_names(self): + result = self._module.get_defined_names() + for m in self.star_import_modules: + result += m.get_defined_names() + return result + + def __getattr__(self, name): + return getattr(self._module, name) + + def __repr__(self): + return "<%s: %s>" % (self.__class__.__name__, self._module) def get_importer(evaluator, import_path, module, level=0): @@ -456,6 +483,8 @@ def remove_star_imports(evaluator, scope, ignored_modules=()): and follow these modules. """ + if isinstance(scope, StarImportModule): + return scope.star_import_modules modules = strip_imports(evaluator, (i for i in scope.get_imports() if i.star)) new = [] for m in modules: diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index 070e502f..e367c10a 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -149,7 +149,7 @@ class Instance(use_metaclass(CachedMetaClass, Executable)): names.append(InstanceElement(self._evaluator, self, var, True)) return names - def scope_generator(self): + def scope_names_generator(self): """ An Instance has two scopes: The scope with self names and the class scope. Instance variables have priority over the class scope.