From a8401f692332b63720bce2ce8b3789ceb902b20a Mon Sep 17 00:00:00 2001 From: immerrr Date: Thu, 16 Aug 2018 11:19:46 +0300 Subject: [PATCH 1/3] Add failing test for jedi.api.names(..., all_scopes=False) --- test/test_api/test_defined_names.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/test_api/test_defined_names.py b/test/test_api/test_defined_names.py index 81b7c5b6..8fc7d46b 100644 --- a/test/test_api/test_defined_names.py +++ b/test/test_api/test_defined_names.py @@ -79,6 +79,22 @@ class TestDefinedNames(TestCase): self.assert_definition_names(subsubdefs, ['L3', 'f']) self.assert_definition_names(subsubdefs[0].defined_names(), ['f']) + def test_class_fields_with_all_scopes_false(self): + definitions = self.check_defined_names(""" + from module import f + g = f(f) + class C: + h = g + + def foo(x=a): + bar = x + return bar + """, ['f', 'g', 'C', 'foo']) + C_subdefs = definitions[-2].defined_names() + foo_subdefs = definitions[-1].defined_names() + self.assert_definition_names(C_subdefs, ['h']) + self.assert_definition_names(foo_subdefs, ['x', 'bar']) + def test_follow_imports(environment): # github issue #344 From 1e8674b51c7d60355d52135f76527a717046fcce Mon Sep 17 00:00:00 2001 From: immerrr Date: Thu, 18 Oct 2018 13:53:47 +0300 Subject: [PATCH 2/3] get_module_names: fix "all_scopes=False" handling Previously, names defined within the scope of first-level classes or functions were returned. --- jedi/evaluate/helpers.py | 26 +++++++++++++++++++++++--- jedi/parser_utils.py | 8 ++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/jedi/evaluate/helpers.py b/jedi/evaluate/helpers.py index c94a1fbe..1b0a7f2a 100644 --- a/jedi/evaluate/helpers.py +++ b/jedi/evaluate/helpers.py @@ -8,7 +8,7 @@ from contextlib import contextmanager from parso.python import tree from jedi._compatibility import unicode -from jedi.parser_utils import get_parent_scope +from jedi.parser_utils import get_parent_scope, is_name_of_func_or_class_def def is_stdlib_path(path): @@ -166,13 +166,33 @@ def get_module_names(module, all_scopes): Returns a dictionary with name parts as keys and their call paths as values. """ - names = chain.from_iterable(module.get_used_names().values()) + names = list(chain.from_iterable(module.get_used_names().values())) if not all_scopes: # We have to filter all the names that don't have the module as a # parent_scope. There's None as a parent, because nodes in the module # node have the parent module and not suite as all the others. # Therefore it's important to catch that case. - names = [n for n in names if get_parent_scope(n).parent in (module, None)] + + def is_module_scope_name(name): + parent_scope = get_parent_scope(name) + if is_name_of_func_or_class_def(name, parent_scope): + # XXX: In syntax tree function- and class-name nodes are immediate children of + # their respective class-definition or function-definition nodes. Technically, + # get_parent_scope(...) for them should return the parent of the definition node, + # because + # + # def foo(...): pass + # + # is equivalent to + # + # foo = lambda(...): None + # + # but that would be a big change that could break type inference, whereas for now + # this discrepancy looks like only a problem for "get_module_names". + parent_scope = parent_scope.parent + return parent_scope in (module, None) + + names = [n for n in names if is_module_scope_name(n)] return names diff --git a/jedi/parser_utils.py b/jedi/parser_utils.py index bb033ed0..6b4edab7 100644 --- a/jedi/parser_utils.py +++ b/jedi/parser_utils.py @@ -234,6 +234,14 @@ def is_scope(node): return node.type in ('file_input', 'classdef', 'funcdef', 'lambdef', 'comp_for') +def is_name_of_func_or_class_def(name_node, func_or_class_def_node): + """Return True if name_node is the name of the func_or_class_def_node.""" + return ( + name_node.parent is func_or_class_def_node and + name_node.type == 'name' and + func_or_class_def_node.type in ('classdef', 'funcdef')) + + def get_parent_scope(node, include_flows=False): """ Returns the underlying scope. From 3f5ac0cf56b756f03565d78db71f8b9c4b149de6 Mon Sep 17 00:00:00 2001 From: immerrr Date: Thu, 18 Oct 2018 14:20:26 +0300 Subject: [PATCH 3/3] test_param_docstring: use all_scopes=True to ensure param is extracted --- test/test_api/test_classes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_api/test_classes.py b/test/test_api/test_classes.py index d320966a..1e4f188e 100644 --- a/test/test_api/test_classes.py +++ b/test/test_api/test_classes.py @@ -99,7 +99,7 @@ def test_function_call_signature_in_doc(Script): def test_param_docstring(): - param = jedi.names("def test(parameter): pass")[1] + param = jedi.names("def test(parameter): pass", all_scopes=True)[1] assert param.name == 'parameter' assert param.docstring() == ''