From 3e36238da30a9561cb3ddfc2e250792129c1606c Mon Sep 17 00:00:00 2001 From: Andy Lee Date: Wed, 22 Mar 2017 10:04:34 -0700 Subject: [PATCH 1/2] Add test for cross-module usages --- .../import_tree_for_usages/__init__.py | 4 ++ test/test_api/import_tree_for_usages/a.py | 4 ++ test/test_api/import_tree_for_usages/b.py | 2 + test/test_api/test_usages.py | 44 ++++++++++++++++++- 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 test/test_api/import_tree_for_usages/__init__.py create mode 100644 test/test_api/import_tree_for_usages/a.py create mode 100644 test/test_api/import_tree_for_usages/b.py diff --git a/test/test_api/import_tree_for_usages/__init__.py b/test/test_api/import_tree_for_usages/__init__.py new file mode 100644 index 00000000..d2ae403e --- /dev/null +++ b/test/test_api/import_tree_for_usages/__init__.py @@ -0,0 +1,4 @@ +""" +An import tree, for testing usages. +""" + diff --git a/test/test_api/import_tree_for_usages/a.py b/test/test_api/import_tree_for_usages/a.py new file mode 100644 index 00000000..acc81838 --- /dev/null +++ b/test/test_api/import_tree_for_usages/a.py @@ -0,0 +1,4 @@ +from . import b + +def foo(): + b.bar() diff --git a/test/test_api/import_tree_for_usages/b.py b/test/test_api/import_tree_for_usages/b.py new file mode 100644 index 00000000..4371a5a1 --- /dev/null +++ b/test/test_api/import_tree_for_usages/b.py @@ -0,0 +1,2 @@ +def bar(): + pass diff --git a/test/test_api/test_usages.py b/test/test_api/test_usages.py index 1325e412..e1852aa0 100644 --- a/test/test_api/test_usages.py +++ b/test/test_api/test_usages.py @@ -1,6 +1,48 @@ import jedi - +import os.path def test_import_usage(): s = jedi.Script("from .. import foo", line=1, column=18, path="foo.py") assert [usage.line for usage in s.usages()] == [1] + + +def usages_with_additional_modules(script, additional_modules): + """ + Stripped down version of `jedi.api.Script.usages` that can take an + explicit set of additional modules. For use with `test_cross_module_usages`. + """ + + definition_names = jedi.api.usages.resolve_potential_imports(script._evaluator, + script._goto()) + modules = set([d.get_root_context() for d in definition_names]) + modules.add(script._get_module()) + for additional_module in additional_modules: + modules.add(additional_module._name.get_root_context()) + return jedi.api.usages.usages(script._evaluator, definition_names, modules) + + +def test_cross_module_usages(): + """ + This tests finding of usages between different modules. In + `jedi.api.usages.compare_contexts`, this exercises the case where + `c1 != c2`. This tests whether `jedi` can find the usage of + `import_tree_for_usages.b.bar` in `import_tree_for_usages.a` + """ + + def usages_script(): + source = 'import import_tree_for_usages.b; import_tree_for_usages.b.bar' + return jedi.api.Script(source=source, line=1, column=len(source), + sys_path=[os.path.dirname(os.path.abspath(__file__))]) + + def module_script(): + source = 'import import_tree_for_usages.a; import_tree_for_usages.a' + return jedi.api.Script(source=source, line=1, column=len(source), + sys_path=[os.path.dirname(os.path.abspath(__file__))]) + + module = module_script().goto_definitions()[0] + module_definition = module._name.get_root_context() + usages_list = usages_with_additional_modules(usages_script(), set([module])) + + assert any([elt for elt in usages_list if elt.module_name == 'a']), ( + "Did not find cross-module usage of :func:`b.bar` in :mod:`a`. Usages list was: {}" + .format(usages_list)) From 1624f6945e7201546c511710abfe66fbdb1e1336 Mon Sep 17 00:00:00 2001 From: Andy Lee Date: Wed, 22 Mar 2017 10:51:45 -0700 Subject: [PATCH 2/2] Fix `api.usages` so it finds cross-module usages --- jedi/api/usages.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/jedi/api/usages.py b/jedi/api/usages.py index 9bca83d5..868334e7 100644 --- a/jedi/api/usages.py +++ b/jedi/api/usages.py @@ -5,6 +5,10 @@ from jedi.evaluate.filters import TreeNameDefinition from jedi.evaluate.representation import ModuleContext +def compare_contexts(c1, c2): + return c1 == c2 or (c1[1] == c2[1] and c1[0].tree_node == c2[0].tree_node) + + def usages(evaluator, definition_names, mods): """ :param definitions: list of Name @@ -40,7 +44,9 @@ def usages(evaluator, definition_names, mods): for name_node in m.tree_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]: + 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