From b77fa58058f8db1930590708326cdba995c2f309 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Sat, 19 Nov 2016 02:24:34 +0100 Subject: [PATCH] Fix most usage tests. --- jedi/api/__init__.py | 24 ++++++++-------- jedi/api/usages.py | 19 ++++++------- jedi/evaluate/dynamic.py | 21 ++++++++------ jedi/evaluate/imports.py | 59 +++++++++++++++++++++++----------------- 4 files changed, 68 insertions(+), 55 deletions(-) diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index 14bbf9c4..f7868d94 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -34,6 +34,7 @@ from jedi.evaluate.param import try_iter_content from jedi.evaluate.helpers import get_module_names from jedi.evaluate.sys_path import get_venv_path from jedi.evaluate.iterable import unpack_tuple_to_dict +from jedi.evaluate.filters import TreeNameDefinition # Jedi uses lots and lots of recursion. By setting this a little bit higher, we # can remove some "maximum recursion depth" errors. @@ -256,8 +257,8 @@ class Script(object): try: module_node = self._get_module_node() user_stmt = module_node.get_statement_for_position(self._pos) - definitions = self._goto() - if not definitions and isinstance(user_stmt, tree.Import): + definition_names = self._goto() + if not definition_names 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. @@ -265,22 +266,23 @@ class Script(object): if name is None: # Must be syntax return [] - definitions = [name] + self._evaluator + definition_names = [TreeNameDefinition(self._get_module(), name)] - if not definitions: + if not definition_names: # Without a definition for a name we cannot find references. return [] - if not isinstance(user_stmt, tree.Import): + if not isinstance(user_stmt, tree.Import) and False: # import case is looked at with add_import_name option - definitions = usages.usages_add_import_modules(self._evaluator, - definitions) + definition_names = usages.usages_add_import_modules(self._evaluator, + definition_names) - module = set([d.get_parent_until() for d in definitions]) - module.add(module_node) - names = usages.usages(self._evaluator, definitions, module) + modules = set([d.get_root_context() for d in definition_names]) + modules.add(self._get_module()) + names = usages.usages(self._evaluator, definition_names, modules) - for d in set(definitions): + for d in set(definition_names): names.append(classes.Definition(self._evaluator, d)) finally: settings.dynamic_flow_information = temp diff --git a/jedi/api/usages.py b/jedi/api/usages.py index 3907ccb6..b469b77a 100644 --- a/jedi/api/usages.py +++ b/jedi/api/usages.py @@ -2,6 +2,7 @@ from jedi._compatibility import unicode from jedi.api import classes from jedi.parser import tree from jedi.evaluate import imports +from jedi.evaluate.filters import TreeNameDefinition def usages(evaluator, definition_names, mods): @@ -14,23 +15,20 @@ def usages(evaluator, definition_names, mods): """ result = [] for d in definitions: - module = d.get_parent_until() + module = d.get_root_context() result.append((module, d.start_pos)) return result - search_name = unicode(list(definition_names)[0]) + search_name = list(definition_names)[0].string_name compare_definitions = compare_array(definition_names) - mods |= set([d.get_parent_until() for d in definition_names]) + mods = mods | set([d.get_root_context() 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) + for name_node in m.module_node.used_names.get(search_name, []): + context = evaluator.create_context(m, name_node) + result = evaluator.goto(context, name_node) if [c for c in compare_array(result) if c in compare_definitions]: + name = TreeNameDefinition(context, name_node) definitions.append(classes.Definition(evaluator, name)) # Previous definitions might be imports, so include them # (because goto might return that import name). @@ -42,6 +40,7 @@ def usages_add_import_modules(evaluator, definitions): """ Adds the modules of the imports """ new = set() for d in definitions: + print(d) imp_or_stmt = d.get_definition() if isinstance(imp_or_stmt, tree.Import): s = imports.ImportWrapper(context, d) diff --git a/jedi/evaluate/dynamic.py b/jedi/evaluate/dynamic.py index 500deab9..2c7beeb4 100644 --- a/jedi/evaluate/dynamic.py +++ b/jedi/evaluate/dynamic.py @@ -74,7 +74,12 @@ def search_params(evaluator, parent_context, funcdef): evaluator.dynamic_params_depth += 1 try: debug.dbg('Dynamic param search in %s.', funcdef.name.value, color='MAGENTA') - function_executions = _search_function_executions(evaluator, funcdef) + module_context = parent_context.get_root_context() + function_executions = _search_function_executions( + evaluator, + module_context, + funcdef + ) if function_executions: zipped_params = zip(*( function_execution.get_params() @@ -92,15 +97,15 @@ def search_params(evaluator, parent_context, funcdef): @memoize_default([], evaluator_is_first_arg=True) @to_list -def _search_function_executions(evaluator, funcdef): +def _search_function_executions(evaluator, module_context, funcdef): """ Returns a list of param names. """ from jedi.evaluate import representation as er - def get_possible_nodes(module_node, func_name): + def get_possible_nodes(module_context, func_name): try: - names = module_node.used_names[func_name] + names = module_context.module_node.used_names[func_name] except KeyError: return @@ -110,7 +115,6 @@ def _search_function_executions(evaluator, funcdef): if trailer.type == 'trailer' and bracket == '(': yield name, trailer - current_module_node = funcdef.get_parent_until() func_name = unicode(funcdef.name) compare_node = funcdef if func_name == '__init__': @@ -122,10 +126,9 @@ def _search_function_executions(evaluator, funcdef): found_executions = False i = 0 - for module_node in imports.get_module_nodes_containing_name( - evaluator, [current_module_node], func_name): - module_context = er.ModuleContext(evaluator, module_node) - for name, trailer in get_possible_nodes(module_node, func_name): + for for_mod_context in imports.get_modules_containing_name( + evaluator, [module_context], func_name): + for name, trailer in get_possible_nodes(for_mod_context, func_name): i += 1 # This is a simple way to stop Jedi's dynamic param recursion diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index f95ab2cf..5bc6c164 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -455,9 +455,12 @@ def _load_module(evaluator, path=None, source=None, sys_path=None, parent_module sys_path = evaluator.sys_path cached = load_parser(path) - module = load(source) if cached is None else cached.module + module_node = load(source) if cached is None else cached.module + if isinstance(module_node, compiled.CompiledObject): + return module_node + from jedi.evaluate.representation import ModuleContext - return ModuleContext(evaluator, module) + return ModuleContext(evaluator, module_node) def add_module(evaluator, module_name, module): @@ -469,18 +472,22 @@ def add_module(evaluator, module_name, module): evaluator.modules[module_name] = module -def get_module_nodes_containing_name(evaluator, module_nodes, name): +def get_modules_containing_name(evaluator, modules, name): """ Search a name in the directories of modules. """ + from jedi.evaluate import representation as er + def check_python_file(path): try: - return parser_cache[path].parser.module + parser_cache_item = parser_cache[path] except KeyError: try: return check_fs(path) except IOError: return None + else: + return er.ModuleContext(evaluator, parser_cache_item.parser.module) def check_fs(path): with open(path, 'rb') as f: @@ -492,27 +499,29 @@ def get_module_nodes_containing_name(evaluator, module_nodes, name): return module # skip non python modules - module_nodes = set(m for m in module_nodes if not isinstance(m, compiled.CompiledObject)) - mod_paths = set() - for m in module_nodes: - mod_paths.add(m.path) + used_mod_paths = set() + for m in modules: + used_mod_paths.add(m.py__file__()) yield m - if settings.dynamic_params_for_other_modules: - paths = set(settings.additional_dynamic_modules) - for p in mod_paths: - if p is not None: - # We need abspath, because the seetings paths might not already - # have been converted to absolute paths. - d = os.path.dirname(os.path.abspath(p)) - for entry in os.listdir(d): - if entry not in mod_paths: - if entry.endswith('.py'): - paths.add(d + os.path.sep + entry) + if not settings.dynamic_params_for_other_modules: + return - for p in sorted(paths): - # make testing easier, sort it - same results on every interpreter - c = check_python_file(p) - if c is not None and c not in module_nodes and not isinstance(c, compiled.CompiledObject): - continue # TODO REENABLE - yield c.module_node + paths = set(settings.additional_dynamic_modules) + for p in used_mod_paths: + if p is not None: + # We need abspath, because the seetings paths might not already + # have been converted to absolute paths. + d = os.path.dirname(os.path.abspath(p)) + for file_name in os.listdir(d): + path = os.path.join(d, file_name) + if path not in used_mod_paths and path not in paths: + if file_name.endswith('.py'): + paths.add(path) + + # Sort here to make issues less random. + for p in sorted(paths): + # make testing easier, sort it - same results on every interpreter + m = check_python_file(p) + if m is not None and not isinstance(m, compiled.CompiledObject): + yield m