def usages(self, additional_module_paths=()): """ Return :class:`classes.Definition` objects, which contain all names that point to the definition of the name under the cursor. This is very useful for refactoring (renaming), or to show all usages of a variable. .. todo:: Implement additional_module_paths :rtype: list of :class:`classes.Definition` """ temp, settings.dynamic_flow_information = \ settings.dynamic_flow_information, False try: user_stmt = self._parser.user_stmt() definitions = self._goto(add_import_name=True) if not definitions: # Without a definition for a name we cannot find references. return [] if not isinstance(user_stmt, pr.Import): # import case is looked at with add_import_name option definitions = usages.usages_add_import_modules( self._evaluator, definitions) module = set([d.get_parent_until() for d in definitions]) module.add(self._parser.module()) names = usages.usages(self._evaluator, definitions, module) for d in set(definitions): names.append(classes.Definition(self._evaluator, d)) finally: settings.dynamic_flow_information = temp return helpers.sorted_definitions(set(names))
def names(source=None, path=None, encoding='utf-8', all_scopes=False, definitions=True, references=False): """ Returns a list of `Definition` objects, containing name parts. This means you can call ``Definition.goto_assignments()`` and get the reference of a name. The parameters are the same as in :py:class:`Script`, except or the following ones: :param all_scopes: If True lists the names of all scopes instead of only the module namespace. :param definitions: If True lists the names that have been defined by a class, function or a statement (``a = b`` returns ``a``). :param references: If True lists all the names that are not listed by ``definitions=True``. E.g. ``a = b`` returns ``b``. """ def def_ref_filter(_def): is_def = _def.is_definition() return definitions and is_def or references and not is_def # Set line/column to a random position, because they don't matter. script = Script(source, line=1, column=0, path=path, encoding=encoding) defs = [classes.Definition(script._evaluator, name_part) for name_part in get_module_names(script._get_module(), all_scopes)] return sorted(filter(def_ref_filter, defs), key=lambda x: (x.line, x.column))
def goto_assignments(self, follow_imports=False): """ Return the first definition found, while optionally following imports. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :rtype: list of :class:`classes.Definition` """ def filter_follow_imports(names, check): for name in names: if check(name): for result in filter_follow_imports(name.goto(), check): yield result else: yield name tree_name = self._module_node.get_name_of_position(self._pos) if tree_name is None: return [] context = self._evaluator.create_context(self._get_module(), tree_name) names = list(self._evaluator.goto(context, tree_name)) if follow_imports: def check(name): return name.is_import() else: def check(name): return isinstance(name, imports.SubModuleName) names = filter_follow_imports(names, check) defs = [classes.Definition(self._evaluator, d) for d in set(names)] return helpers.sorted_definitions(defs)
def get_context(self, line=None, column=None): pos = (line, column) leaf = self._module_node.get_leaf_for_position(pos, include_prefixes=True) if leaf.start_pos > pos or leaf.type == 'endmarker': previous_leaf = leaf.get_previous_leaf() if previous_leaf is not None: leaf = previous_leaf module_context = self._get_module_context() n = tree.search_ancestor(leaf, 'funcdef', 'classdef') if n is not None and n.start_pos < pos <= n.children[-1].start_pos: # This is a bit of a special case. The context of a function/class # name/param/keyword is always it's parent context, not the # function itself. Catch all the cases here where we are before the # suite object, but still in the function. context = module_context.create_value(n).as_context() else: context = module_context.create_context(leaf) while context.name is None: context = context.parent_context # comprehensions definition = classes.Definition(self._inference_state, context.name) while definition.type != 'module': name = definition._name # TODO private access tree_name = name.tree_name if tree_name is not None: # Happens with lambdas. scope = tree_name.get_definition() if scope.start_pos[1] < column: break definition = definition.parent() return definition
def goto_definitions(self): """ Return the definitions of a the path under the cursor. goto function! This follows complicated paths and returns the end, not the first definition. The big difference between :meth:`goto_assignments` and :meth:`goto_definitions` is that :meth:`goto_assignments` doesn't follow imports and statements. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :rtype: list of :class:`classes.Definition` """ leaf = self._get_module().name_for_position(self._pos) if leaf is None: leaf = self._get_module().get_leaf_for_position(self._pos) if leaf is None: return [] definitions = helpers.evaluate_goto_definition(self._evaluator, leaf) names = [s.name for s in definitions] defs = [classes.Definition(self._evaluator, name) for name in names] # The additional set here allows the definitions to become unique in an # API sense. In the internals we want to separate more things than in # the API. return helpers.sorted_definitions(set(defs))
def goto_assignments(self, follow_imports=False): """ Return the first definition found, while optionally following imports. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :rtype: list of :class:`classes.Definition` """ def filter_follow_imports(names, check): for name in names: if check(name): for result in filter_follow_imports(name.goto(), check): yield result else: yield name names = self._goto() if follow_imports: def check(name): if isinstance(name, er.ModuleName): return False return name.api_type == 'module' else: def check(name): return isinstance(name, imports.SubModuleName) names = filter_follow_imports(names, check) defs = [classes.Definition(self._evaluator, d) for d in set(names)] return helpers.sorted_definitions(defs)
def test_keyword_full_name_should_be_none(): """issue #94""" # Using `from jedi.keywords import Keyword` here does NOT work # in Python 3. This is due to the import hack jedi using. Keyword = classes.keywords.Keyword d = classes.Definition(Evaluator(), Keyword('(', (0, 0))) assert d.full_name is None
def goto_assignments(self, follow_imports=False): """ Return the first definition found, while optionally following imports. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :rtype: list of :class:`classes.Definition` """ def filter_follow_imports(names): for name in names: definition = name.get_definition() if definition.type in ('import_name', 'import_from'): imp = imports.ImportWrapper(self._evaluator, name) for name in filter_follow_imports(imp.follow(is_goto=True)): yield name else: yield name names = self._goto() if follow_imports: names = filter_follow_imports(names) defs = [classes.Definition(self._evaluator, d) for d in set(names)] return helpers.sorted_definitions(defs)
def _goto(self, line, column, follow_imports=False, follow_builtin_imports=False, only_stubs=False, prefer_stubs=False): tree_name = self._module_node.get_name_of_position((line, column)) if tree_name is None: # Without a name we really just want to jump to the result e.g. # executed by `foo()`, if we the cursor is after `)`. return self.infer(line, column, only_stubs=only_stubs, prefer_stubs=prefer_stubs) name = self._get_module_context().create_name(tree_name) # Make it possible to goto the super class function/attribute # definitions, when they are overwritten. names = [] if name.tree_name.is_definition() and name.parent_context.is_class(): class_node = name.parent_context.tree_node class_value = self._get_module_context().create_value(class_node) mro = class_value.py__mro__() next(mro) # Ignore the first entry, because it's the class itself. for cls in mro: names = cls.goto(tree_name.value) if names: break if not names: names = list(name.goto()) if follow_imports: names = helpers.filter_follow_imports(names) names = convert_names( names, only_stubs=only_stubs, prefer_stubs=prefer_stubs, ) defs = [classes.Definition(self._inference_state, d) for d in set(names)] return helpers.sorted_definitions(defs)
def usages(evaluator, definition_names, mods): """ :param definitions: list of Name """ def compare_array(definitions): """ `definitions` are being compared by module/start_pos, because sometimes the id's of the objects change (e.g. executions). """ result = [] for d in definitions: module = d.get_parent_until() result.append((module, d.start_pos)) return result search_name = unicode(list(definition_names)[0]) compare_definitions = compare_array(definition_names) mods |= set([d.get_parent_until() for d in definition_names]) definitions = [] for m in imports.get_modules_containing_name(evaluator, mods, search_name): try: check_names = m.used_names[search_name] except KeyError: continue for name in check_names: result = evaluator.goto(name) if [c for c in compare_array(result) if c in compare_definitions]: definitions.append(classes.Definition(evaluator, name)) # Previous definitions might be imports, so include them # (because goto might return that import name). compare_definitions += compare_array([name]) return definitions
def _usages_in_module(include_builtins=True): tree_name = self._module_node.get_name_of_position(self._pos) if tree_name is None: # Must be syntax return [] names = usages.usages_in_module(self._get_module(), tree_name) definitions = [classes.Definition(self._evaluator, n) for n in names] if not include_builtins: definitions = [d for d in definitions if not d.in_builtin_module()] return helpers.sorted_definitions(definitions)
def usages(self, additional_module_paths=()): """ Return :class:`classes.Definition` objects, which contain all names that point to the definition of the name under the cursor. This is very useful for refactoring (renaming), or to show all usages of a variable. .. todo:: Implement additional_module_paths :rtype: list of :class:`classes.Definition` """ temp, settings.dynamic_flow_information = \ settings.dynamic_flow_information, False user_stmt = self._parser.user_stmt() definitions, search_name = self._goto(add_import_name=True) if isinstance(user_stmt, pr.Statement): c = user_stmt.expression_list()[0] if not isinstance(c, unicode) and self._pos < c.start_pos: # the search_name might be before `=` definitions = [ v for v in user_stmt.get_defined_names() if unicode(v.names[-1]) == search_name ] if not isinstance(user_stmt, pr.Import): # import case is looked at with add_import_name option definitions = usages.usages_add_import_modules( self._evaluator, definitions, search_name) module = set([d.get_parent_until() for d in definitions]) module.add(self._parser.module()) names = usages.usages(self._evaluator, definitions, search_name, module) for d in set(definitions): try: name_part = d.names[-1] except AttributeError: names.append(classes.Definition(self._evaluator, d)) else: names.append(classes.Definition(self._evaluator, name_part)) settings.dynamic_flow_information = temp return helpers.sorted_definitions(set(names))
def goto_assignments(self): """ Return the first definition found. Imports and statements aren't followed. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :rtype: list of :class:`classes.Definition` """ results = self._goto() d = [classes.Definition(self._evaluator, d) for d in set(results)] return helpers.sorted_definitions(d)
def _references(include_builtins=True): tree_name = self._module_node.get_name_of_position((line, column)) if tree_name is None: # Must be syntax return [] names = find_references(self._get_module_context(), tree_name) definitions = [classes.Definition(self._inference_state, n) for n in names] if not include_builtins: definitions = [d for d in definitions if not d.in_builtin_module()] return helpers.sorted_definitions(definitions)
def goto_definitions(self): """ Return the definitions of a the path under the cursor. goto function! This follows complicated paths and returns the end, not the first definition. The big difference between :meth:`goto_assignments` and :meth:`goto_definitions` is that :meth:`goto_assignments` doesn't follow imports and statements. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :rtype: list of :class:`classes.Definition` """ def resolve_import_paths(scopes): for s in scopes.copy(): if isinstance(s, imports.ImportPath): scopes.remove(s) scopes.update(resolve_import_paths(set(s.follow()))) return scopes user_stmt = self._parser.user_stmt_with_whitespace() goto_path = self._user_context.get_path_under_cursor() context = self._user_context.get_context() definitions = set() if next(context) in ('class', 'def'): definitions = set([self._parser.user_scope()]) else: # Fetch definition of callee, if there's no path otherwise. if not goto_path: (call, _) = helpers.func_call_and_param_index(user_stmt, self._pos) if call is not None: while call.next is not None: call = call.next # reset cursor position: (row, col) = call.name.end_pos pos = (row, max(col - 1, 0)) self._user_context = UserContext(self.source, pos) # then try to find the path again goto_path = self._user_context.get_path_under_cursor() if not definitions: if goto_path: definitions = set(self._prepare_goto(goto_path)) definitions = resolve_import_paths(definitions) d = set([ classes.Definition(self._evaluator, s) for s in definitions if s is not imports.ImportPath.GlobalNamespace ]) return helpers.sorted_definitions(d)
def _names(self, all_scopes=False, definitions=True, references=False): def def_ref_filter(_def): is_def = _def._name.tree_name.is_definition() return definitions and is_def or references and not is_def # Set line/column to a random position, because they don't matter. module_context = self._get_module_context() defs = [ classes.Definition(self._inference_state, module_context.create_name(name)) for name in get_module_names(self._module_node, all_scopes) ] return sorted(filter(def_ref_filter, defs), key=lambda x: (x.line, x.column))
def usages(evaluator, definition_names, mods): """ :param definitions: list of Name """ def resolve_names(definition_names): for name in definition_names: if name.api_type == 'module': found = False for context in name.infer(): found = True yield context.name if not found: yield name else: yield name def compare_array(definition_names): """ `definitions` are being compared by module/start_pos, because sometimes the id's of the objects change (e.g. executions). """ return [(name.get_root_context(), name.start_pos) for name in resolve_names(definition_names)] search_name = list(definition_names)[0].string_name compare_definitions = compare_array(definition_names) mods = mods | set([d.get_root_context() for d in definition_names]) definition_names = set(resolve_names(definition_names)) for m in imports.get_modules_containing_name(evaluator, mods, search_name): if isinstance(m, ModuleContext): for name_node in m.tree_node.used_names.get(search_name, []): context = evaluator.create_context(m, name_node) try: result = evaluator.goto(context, name_node) except (NotImplementedError, RecursionError) as err: logger.error(err) continue if any( compare_contexts(c1, c2) for c1 in compare_array(result) for c2 in compare_definitions): name = TreeNameDefinition(context, name_node) definition_names.add(name) # Previous definitions might be imports, so include them # (because goto might return that import name). compare_definitions += compare_array([name]) else: # compiled objects definition_names.add(m.name) return [classes.Definition(evaluator, n) for n in definition_names]
def goto_definitions(self): """ Return the definitions of a the path under the cursor. goto function! This follows complicated paths and returns the end, not the first definition. The big difference between :meth:`goto_assignments` and :meth:`goto_definitions` is that :meth:`goto_assignments` doesn't follow imports and statements. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :rtype: list of :class:`classes.Definition` """ def resolve_import_paths(scopes): for s in scopes.copy(): if isinstance(s, imports.ImportWrapper): scopes.remove(s) scopes.update(resolve_import_paths(set(s.follow()))) return scopes goto_path = self._user_context.get_path_under_cursor() context = self._user_context.get_context() definitions = set() if next(context) in ('class', 'def'): definitions = set( [self._evaluator.wrap(self._parser.user_scope())]) else: # Fetch definition of callee, if there's no path otherwise. if not goto_path: definitions = set(signature._definition for signature in self.call_signatures()) if re.match('\w[\w\d_]*$', goto_path) and not definitions: user_stmt = self._parser.user_stmt() if user_stmt is not None and user_stmt.type == 'expr_stmt': for name in user_stmt.get_defined_names(): if name.start_pos <= self._pos <= name.end_pos: # TODO scaning for a name and then using it should be # the default. definitions = set( self._evaluator.goto_definition(name)) if not definitions and goto_path: definitions = set(self._prepare_goto(goto_path)) definitions = resolve_import_paths(definitions) names = [s.name for s in definitions] defs = [classes.Definition(self._evaluator, name) for name in names] return helpers.sorted_definitions(set(defs))
def _goto(self, line, column, follow_imports=False, follow_builtin_imports=False, only_stubs=False, prefer_stubs=False): def filter_follow_imports(names): for name in names: if name.is_import(): new_names = list(filter_follow_imports(name.goto())) found_builtin = False if follow_builtin_imports: for new_name in new_names: if new_name.start_pos is None: found_builtin = True if found_builtin: yield name else: for new_name in new_names: yield new_name else: yield name tree_name = self._module_node.get_name_of_position((line, column)) if tree_name is None: # Without a name we really just want to jump to the result e.g. # executed by `foo()`, if we the cursor is after `)`. return self.infer(line, column, only_stubs=only_stubs, prefer_stubs=prefer_stubs) name = self._get_module_context().create_name(tree_name) names = list(name.goto()) if follow_imports: names = filter_follow_imports(names) names = convert_names( names, only_stubs=only_stubs, prefer_stubs=prefer_stubs, ) defs = [ classes.Definition(self._inference_state, d) for d in set(names) ] return helpers.sorted_definitions(defs)
def goto_assignments(self, follow_imports=False, follow_builtin_imports=False): """ Return the first definition found, while optionally following imports. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :param follow_imports: The goto call will follow imports. :param follow_builtin_imports: If follow_imports is True will decide if it follow builtin imports. :rtype: list of :class:`classes.Definition` """ def filter_follow_imports(names, check): for name in names: if check(name): new_names = list(filter_follow_imports(name.goto(), check)) found_builtin = False if follow_builtin_imports: for new_name in new_names: if new_name.start_pos is None: found_builtin = True if found_builtin: yield name else: for new_name in new_names: yield new_name else: yield name tree_name = self._module_node.get_name_of_position(self._pos) if tree_name is None: # Without a name we really just want to jump to the result e.g. # executed by `foo()`, if we the cursor is after `)`. return self.goto_definitions() context = self._evaluator.create_context(self._get_module(), tree_name) names = list(self._evaluator.goto(context, tree_name)) if follow_imports: names = filter_follow_imports(names, lambda name: name.is_import()) names = try_stub_to_actual_names(names, prefer_stub_to_compiled=True) defs = [classes.Definition(self._evaluator, d) for d in set(names)] return helpers.sorted_definitions(defs)
def usages(self, additional_module_paths=()): """ Return :class:`classes.Definition` objects, which contain all names that point to the definition of the name under the cursor. This is very useful for refactoring (renaming), or to show all usages of a variable. .. todo:: Implement additional_module_paths :rtype: list of :class:`classes.Definition` """ temp, settings.dynamic_flow_information = \ settings.dynamic_flow_information, False try: user_stmt = self._get_module().get_statement_for_position( self._pos) definitions = self._goto() if not definitions and isinstance(user_stmt, tree.Import): # For not defined imports (goto doesn't find something, we take # the name as a definition. This is enough, because every name # points to it. name = user_stmt.name_for_position(self._pos) if name is None: # Must be syntax return [] definitions = [name] if not definitions: # Without a definition for a name we cannot find references. return [] if not isinstance(user_stmt, tree.Import): # import case is looked at with add_import_name option definitions = usages.usages_add_import_modules( self._evaluator, definitions) module = set([d.get_parent_until() for d in definitions]) module.add(self._get_module()) names = usages.usages(self._evaluator, definitions, module) for d in set(definitions): names.append(classes.Definition(self._evaluator, d)) finally: settings.dynamic_flow_information = temp return helpers.sorted_definitions(set(names))
def _goto_assignments(self, follow_imports, follow_builtin_imports, only_stubs=False, prefer_stubs=False): def filter_follow_imports(names, check): for name in names: if check(name): new_names = list(filter_follow_imports(name.goto(), check)) found_builtin = False if follow_builtin_imports: for new_name in new_names: if new_name.start_pos is None: found_builtin = True if found_builtin: yield name else: for new_name in new_names: yield new_name else: yield name tree_name = self._module_node.get_name_of_position(self._pos) if tree_name is None: # Without a name we really just want to jump to the result e.g. # executed by `foo()`, if we the cursor is after `)`. return self.goto_definitions(only_stubs=only_stubs, prefer_stubs=prefer_stubs) context = self._evaluator.create_context(self._get_module(), tree_name) names = list(self._evaluator.goto(context, tree_name)) if follow_imports: names = filter_follow_imports(names, lambda name: name.is_import()) names = convert_names( names, only_stubs=only_stubs, prefer_stubs=prefer_stubs, ) defs = [classes.Definition(self._evaluator, d) for d in set(names)] return helpers.sorted_definitions(defs)
def usages(self, additional_module_paths=()): """ Return :class:`classes.Definition` objects, which contain all names that point to the definition of the name under the cursor. This is very useful for refactoring (renaming), or to show all usages of a variable. .. todo:: Implement additional_module_paths :rtype: list of :class:`classes.Definition` """ tree_name = self._module_node.get_name_of_position(self._pos) if tree_name is None: # Must be syntax return [] names = usages.usages(self._get_module(), tree_name) definitions = [classes.Definition(self._evaluator, n) for n in names] return helpers.sorted_definitions(definitions)
def help(self, line=None, column=None): """ Works like goto and returns a list of Definition objects. Returns additional definitions for keywords and operators. The additional definitions are of ``Definition(...).type == 'keyword'``. These definitions do not have a lot of value apart from their docstring attribute, which contains the output of Python's ``help()`` function. :rtype: list of :class:`classes.Definition` """ definitions = self.goto(line, column, follow_imports=True) if definitions: return definitions leaf = self._module_node.get_leaf_for_position((line, column)) if leaf.type in ('keyword', 'operator', 'error_leaf'): reserved = self._grammar._pgen_grammar.reserved_syntax_strings.keys() if leaf.value in reserved: name = KeywordName(self._inference_state, leaf.value) return [classes.Definition(self._inference_state, name)] return []
def _goto_definitions(self, only_stubs=False, prefer_stubs=False): leaf = self._module_node.get_name_of_position(self._pos) if leaf is None: leaf = self._module_node.get_leaf_for_position(self._pos) if leaf is None: return [] context = self._evaluator.create_context(self._get_module(), leaf) contexts = helpers.evaluate_goto_definition(self._evaluator, context, leaf) contexts = convert_contexts( contexts, only_stubs=only_stubs, prefer_stubs=prefer_stubs, ) defs = [classes.Definition(self._evaluator, c.name) for c in contexts] # The additional set here allows the definitions to become unique in an # API sense. In the internals we want to separate more things than in # the API. return helpers.sorted_definitions(set(defs))
def _infer(self, line, column, only_stubs=False, prefer_stubs=False): pos = line, column leaf = self._module_node.get_name_of_position(pos) if leaf is None: leaf = self._module_node.get_leaf_for_position(pos) if leaf is None or leaf.type == 'string': return [] context = self._get_module_context().create_context(leaf) values = helpers.infer(self._inference_state, context, leaf) values = convert_values( values, only_stubs=only_stubs, prefer_stubs=prefer_stubs, ) defs = [classes.Definition(self._inference_state, c.name) for c in values] # The additional set here allows the definitions to become unique in an # API sense. In the internals we want to separate more things than in # the API. return helpers.sorted_definitions(set(defs))
def goto_assignments(self, follow_imports=False): """ Return the first definition found, while optionally following imports. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :rtype: list of :class:`classes.Definition` """ def filter_follow_imports(names): for name in names: if isinstance(name, (imports.ImportName, TreeNameDefinition)): for context in name.infer(): yield context.name else: yield name names = self._goto() if follow_imports: names = filter_follow_imports(names) defs = [classes.Definition(self._evaluator, d) for d in set(names)] return helpers.sorted_definitions(defs)
def check_call_for_usage(call): stmt = call.parent while not isinstance(stmt.parent, pr.IsScope): stmt = stmt.parent # New definition, call cannot be a part of stmt if len(call.name) == 1 and call.execution is None \ and call.name in stmt.get_defined_names(): # Class params are not definitions (like function params). They # are super classes, that need to be resolved. if not (isinstance(stmt, pr.Param) and isinstance(stmt.parent, pr.Class)): return follow = [] # There might be multiple search_name's in one call_path call_path = list(call.generate_call_path()) for i, name in enumerate(call_path): # name is `pr.NamePart`. if u(name) == search_name: follow.append(call_path[:i + 1]) for call_path in follow: follow_res, search = evaluator.goto(call.parent, call_path) # names can change (getattr stuff), therefore filter names that # don't match `search`. # TODO add something like that in the future - for now usages are # completely broken anyway. #follow_res = [r for r in follow_res if str(r) == search] #print search.start_pos,search_name.start_pos #print follow_res, search, search_name, [(r, r.start_pos) for r in follow_res] follow_res = usages_add_import_modules(evaluator, follow_res, search) compare_follow_res = compare_array(follow_res) # compare to see if they match if any(r in compare_definitions for r in compare_follow_res): yield classes.Definition(evaluator, search)
def usages(evaluator, definitions, search_name, mods): def compare_array(definitions): """ `definitions` are being compared by module/start_pos, because sometimes the id's of the objects change (e.g. executions). """ result = [] for d in definitions: module = d.get_parent_until() result.append((module, d.start_pos)) return result def check_call_for_usage(call): stmt = call.parent while not isinstance(stmt.parent, pr.IsScope): stmt = stmt.parent # New definition, call cannot be a part of stmt if len(call.name) == 1 and call.execution is None \ and call.name in stmt.get_defined_names(): # Class params are not definitions (like function params). They # are super classes, that need to be resolved. if not (isinstance(stmt, pr.Param) and isinstance(stmt.parent, pr.Class)): return follow = [] # There might be multiple search_name's in one call_path call_path = list(call.generate_call_path()) for i, name in enumerate(call_path): # name is `pr.NamePart`. if u(name) == search_name: follow.append(call_path[:i + 1]) for call_path in follow: follow_res, search = evaluator.goto(call.parent, call_path) # names can change (getattr stuff), therefore filter names that # don't match `search`. # TODO add something like that in the future - for now usages are # completely broken anyway. #follow_res = [r for r in follow_res if str(r) == search] #print search.start_pos,search_name.start_pos #print follow_res, search, search_name, [(r, r.start_pos) for r in follow_res] follow_res = usages_add_import_modules(evaluator, follow_res, search) compare_follow_res = compare_array(follow_res) # compare to see if they match if any(r in compare_definitions for r in compare_follow_res): yield classes.Definition(evaluator, search) if not definitions: return set() compare_definitions = compare_array(definitions) mods |= set([d.get_parent_until() for d in definitions]) names = [] for m in imports.get_modules_containing_name(mods, search_name): try: stmts = m.used_names[search_name] except KeyError: continue for stmt in stmts: if isinstance(stmt, pr.Import): count = 0 imps = [] for i in stmt.get_all_import_names(): for name_part in i.names: count += 1 if unicode(name_part) == search_name: imps.append((count, name_part)) for used_count, name_part in imps: i = imports.ImportWrapper(evaluator, stmt, kill_count=count - used_count, nested_resolve=True) f = i.follow(is_goto=True) if set(f) & set(definitions): names.append(classes.Definition(evaluator, name_part)) else: for call in helpers.scan_statement_for_calls( stmt, search_name, assignment_details=True): names += check_call_for_usage(call) return names