diff --git a/jedi/api.py b/jedi/api.py index 9dd49bad..ba2c3a8e 100644 --- a/jedi/api.py +++ b/jedi/api.py @@ -413,7 +413,7 @@ class Script(object): for d in defs: if isinstance(d.parent, pr.Import) \ and d.start_pos == (0, 0): - i = imports.ImportPath(d.parent).follow(is_goto=True) + i = imports.ImportPath(self._evaluator, d.parent).follow(is_goto=True) definitions.remove(d) definitions |= follow_inexistent_imports(i) return definitions @@ -556,7 +556,7 @@ class Script(object): context = self._module.get_context() just_from = next(context) == 'from' - i = imports.ImportPath(user_stmt, is_like_search, + i = imports.ImportPath(self._evaluator, user_stmt, is_like_search, kill_count=kill_count, direct_resolve=True, is_just_from=just_from) return i, cur_name_part diff --git a/jedi/api_classes.py b/jedi/api_classes.py index af9a4737..95162da2 100644 --- a/jedi/api_classes.py +++ b/jedi/api_classes.py @@ -409,7 +409,7 @@ class Completion(BaseDefinition): if self._definition.isinstance(pr.Statement): defs = evaluate.follow_statement(self._definition) elif self._definition.isinstance(pr.Import): - defs = imports.strip_imports([self._definition]) + defs = imports.strip_imports(self._evaluator, [self._definition]) else: return [self] diff --git a/jedi/dynamic.py b/jedi/dynamic.py index 3d59c403..36a06b94 100644 --- a/jedi/dynamic.py +++ b/jedi/dynamic.py @@ -504,7 +504,7 @@ def usages(definitions, search_name, mods): imps.append((count, name_part)) for used_count, name_part in imps: - i = imports.ImportPath(stmt, kill_count=count - used_count, + i = imports.ImportPath(_evaluator, stmt, kill_count=count - used_count, direct_resolve=True) f = i.follow(is_goto=True) if set(f) & set(definitions): @@ -521,7 +521,7 @@ def usages_add_import_modules(definitions, search_name): new = set() for d in definitions: if isinstance(d.parent, pr.Import): - s = imports.ImportPath(d.parent, direct_resolve=True) + s = imports.ImportPath(_evaluator, d.parent, direct_resolve=True) with common.ignored(IndexError): new.add(s.follow(is_goto=True)[0]) return set(definitions) | new diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 32766952..39bfd332 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -115,97 +115,96 @@ def get_defined_names_for_position(scope, position=None, start_scope=None): return names_new -def get_names_of_scope(scope, position=None, star_search=True, - include_builtin=True): - """ - Get all completions (names) possible for the current scope. - The star search option is only here to provide an optimization. Otherwise - the whole thing would probably start a little recursive madness. - - This function is used to include names from outer scopes. For example, - when the current scope is function: - - >>> from jedi.parser import Parser - >>> parser = Parser(''' - ... x = ['a', 'b', 'c'] - ... def func(): - ... y = None - ... ''') - >>> scope = parser.module.subscopes[0] - >>> scope - - - `get_names_of_scope` is a generator. First it yields names from - most inner scope. - - >>> pairs = list(get_names_of_scope(scope)) - >>> pairs[0] - (, []) - - Then it yield the names from one level outer scope. For this - example, this is the most outer scope. - - >>> pairs[1] - (, [, ]) - - Finally, it yields names from builtin, if `include_builtin` is - true (default). - - >>> pairs[2] #doctest: +ELLIPSIS - (, [, ...]) - - :rtype: [(pr.Scope, [pr.Name])] - :return: Return an generator that yields a pair of scope and names. - """ - in_func_scope = scope - non_flow = scope.get_parent_until(pr.Flow, reverse=True) - while scope: - if isinstance(scope, pr.SubModule) and scope.parent: - # we don't want submodules to report if we have modules. - scope = scope.parent - continue - # `pr.Class` is used, because the parent is never `Class`. - # Ignore the Flows, because the classes and functions care for that. - # InstanceElement of Class is ignored, if it is not the start scope. - if not (scope != non_flow and scope.isinstance(pr.Class) - or scope.isinstance(pr.Flow) - or scope.isinstance(er.Instance) - and non_flow.isinstance(er.Function)): - try: - if isinstance(scope, er.Instance): - for g in scope.scope_generator(): - yield g - else: - yield scope, get_defined_names_for_position(scope, - position, in_func_scope) - except StopIteration: - reraise(common.MultiLevelStopIteration, sys.exc_info()[2]) - if scope.isinstance(pr.ForFlow) and scope.is_list_comp: - # is a list comprehension - yield scope, scope.get_set_vars(is_internal_call=True) - - scope = scope.parent - # This is used, because subscopes (Flow scopes) would distort the - # results. - if scope and scope.isinstance(er.Function, pr.Function, er.Execution): - in_func_scope = scope - - # Add star imports. - if star_search: - for s in imports.remove_star_imports(non_flow.get_parent_until()): - for g in get_names_of_scope(s, star_search=False): - yield g - - # Add builtins to the global scope. - if include_builtin: - builtin_scope = builtin.Builtin.scope - yield builtin_scope, builtin_scope.get_defined_names() - - class Evaluator(object): def __init__(self): self.cache = None + def get_names_of_scope(self, scope, position=None, star_search=True, + include_builtin=True): + """ + Get all completions (names) possible for the current scope. + The star search option is only here to provide an optimization. Otherwise + the whole thing would probably start a little recursive madness. + + This function is used to include names from outer scopes. For example, + when the current scope is function: + + >>> from jedi.parser import Parser + >>> parser = Parser(''' + ... x = ['a', 'b', 'c'] + ... def func(): + ... y = None + ... ''') + >>> scope = parser.module.subscopes[0] + >>> scope + + + `get_names_of_scope` is a generator. First it yields names from + most inner scope. + + >>> pairs = list(Evaluator().get_names_of_scope(scope)) + >>> pairs[0] + (, []) + + Then it yield the names from one level outer scope. For this + example, this is the most outer scope. + + >>> pairs[1] + (, [, ]) + + Finally, it yields names from builtin, if `include_builtin` is + true (default). + + >>> pairs[2] #doctest: +ELLIPSIS + (, [, ...]) + + :rtype: [(pr.Scope, [pr.Name])] + :return: Return an generator that yields a pair of scope and names. + """ + in_func_scope = scope + non_flow = scope.get_parent_until(pr.Flow, reverse=True) + while scope: + if isinstance(scope, pr.SubModule) and scope.parent: + # we don't want submodules to report if we have modules. + scope = scope.parent + continue + # `pr.Class` is used, because the parent is never `Class`. + # Ignore the Flows, because the classes and functions care for that. + # InstanceElement of Class is ignored, if it is not the start scope. + if not (scope != non_flow and scope.isinstance(pr.Class) + or scope.isinstance(pr.Flow) + or scope.isinstance(er.Instance) + and non_flow.isinstance(er.Function)): + try: + if isinstance(scope, er.Instance): + for g in scope.scope_generator(): + yield g + else: + yield scope, get_defined_names_for_position(scope, + position, in_func_scope) + except StopIteration: + reraise(common.MultiLevelStopIteration, sys.exc_info()[2]) + if scope.isinstance(pr.ForFlow) and scope.is_list_comp: + # is a list comprehension + yield scope, scope.get_set_vars(is_internal_call=True) + + scope = scope.parent + # This is used, because subscopes (Flow scopes) would distort the + # results. + if scope and scope.isinstance(er.Function, pr.Function, er.Execution): + in_func_scope = scope + + # Add star imports. + if star_search: + for s in imports.remove_star_imports(self, non_flow.get_parent_until()): + for g in self.get_names_of_scope(s, star_search=False): + yield g + + # Add builtins to the global scope. + if include_builtin: + builtin_scope = builtin.Builtin.scope + yield builtin_scope, builtin_scope.get_defined_names() + def find_name(self, scope, name_str, position=None, search_global=False, is_goto=False, resolve_decorator=True): """ @@ -458,7 +457,7 @@ class Evaluator(object): return res_new if search_global: - scope_generator = get_names_of_scope(scope, position=position) + scope_generator = self.get_names_of_scope(scope, position=position) else: if isinstance(scope, er.Instance): scope_generator = scope.scope_generator() @@ -545,8 +544,8 @@ class Evaluator(object): if isinstance(call, pr.Lambda): result.append(er.Function(call)) # With things like params, these can also be functions... - elif isinstance(call, pr.Base) and call.isinstance(er.Function, - er.Class, er.Instance, dynamic.ArrayInstance): + elif isinstance(call, pr.Base) and call.isinstance( + er.Function, er.Class, er.Instance, dynamic.ArrayInstance): result.append(call) # The string tokens are just operations (+, -, etc.) elif not isinstance(call, (str, unicode)): @@ -596,7 +595,7 @@ class Evaluator(object): scopes = self.find_name(builtin.Builtin.scope, current.type_as_string()) # Make instances of those number/string objects. scopes = [er.Instance(s, (current.value,)) for s in scopes] - result = imports.strip_imports(scopes) + result = imports.strip_imports(self, scopes) return self.follow_paths(path, result, scope, position=position) @@ -657,7 +656,7 @@ class Evaluator(object): # This is the typical lookup while chaining things. if filter_private_variable(scope, call_scope, current): return [] - result = imports.strip_imports(self.find_name(scope, current, + result = imports.strip_imports(self, self.find_name(scope, current, position=position)) return self.follow_paths(path, set(result), call_scope, position=position) diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index 2c95e521..0221ec04 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -44,8 +44,9 @@ class ImportPath(pr.Base): GlobalNamespace = GlobalNamespace() - def __init__(self, import_stmt, is_like_search=False, kill_count=0, + def __init__(self, evaluator, import_stmt, is_like_search=False, kill_count=0, direct_resolve=False, is_just_from=False): + self._evaluator = evaluator self.import_stmt = import_stmt self.is_like_search = is_like_search self.direct_resolve = direct_resolve @@ -373,7 +374,7 @@ class ImportPath(pr.Base): return f.parser.module, rest -def strip_imports(scopes): +def strip_imports(evaluator, scopes): """ Here we strip the imports - they don't get resolved necessarily. Really used anymore? Merge with remove_star_imports? @@ -381,25 +382,25 @@ def strip_imports(scopes): result = [] for s in scopes: if isinstance(s, pr.Import): - result += ImportPath(s).follow() + result += ImportPath(evaluator, s).follow() else: result.append(s) return result @cache.cache_star_import -def remove_star_imports(scope, ignored_modules=()): +def remove_star_imports(evaluator, scope, ignored_modules=()): """ Check a module for star imports: >>> from module import * and follow these modules. """ - modules = strip_imports(i for i in scope.get_imports() if i.star) + modules = strip_imports(evaluator, (i for i in scope.get_imports() if i.star)) new = [] for m in modules: if m not in ignored_modules: - new += remove_star_imports(m, modules) + new += remove_star_imports(evaluator, m, modules) modules += new # Filter duplicate modules. diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index 11a3914b..9a4d3fb0 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -498,7 +498,7 @@ class Execution(Executable): debug.dbg('exec result: %s in %s' % (stmts, self)) - return imports.strip_imports(stmts) + return imports.strip_imports(self._evaluator, stmts) def _get_function_returns(self, func, evaluate_generator): """ A normal Function execution """