diff --git a/jedi/inference/gradual/conversion.py b/jedi/inference/gradual/conversion.py index 8f6917ef..58c2522b 100644 --- a/jedi/inference/gradual/conversion.py +++ b/jedi/inference/gradual/conversion.py @@ -59,28 +59,23 @@ def _try_stub_to_python_names(names, prefer_stub_to_compiled=False): yield name continue - name_list = name.get_qualified_names() - if name_list is None: - values = NO_VALUES - else: - values = _infer_from_stub( - module_context, - name_list[:-1], - ignore_compiled=prefer_stub_to_compiled, - ) - if values and name_list: - new_names = values.goto(name_list[-1]) - for new_name in new_names: - yield new_name - if new_names: + if name.api_type == 'module': + values = convert_values(name.infer(), ignore_compiled=prefer_stub_to_compiled) + if values: + for v in values: + yield v.name continue - elif values: - for c in values: - yield c.name - continue - # This is the part where if we haven't found anything, just return the - # stub name. - yield name + else: + v = name.get_defining_qualified_value() + if v is not None: + converted = _stub_to_python_value_set(v, ignore_compiled=prefer_stub_to_compiled) + if converted: + converted_names = converted.goto(name.get_public_name()) + if converted_names: + for n in converted_names: + yield n + continue + yield name def _load_stub_module(module): diff --git a/jedi/inference/names.py b/jedi/inference/names.py index c246bb98..842a45eb 100644 --- a/jedi/inference/names.py +++ b/jedi/inference/names.py @@ -79,6 +79,13 @@ class AbstractNameDefinition(object): def api_type(self): return self.parent_context.api_type + def get_defining_qualified_value(self): + """ + Returns either None or the value that is public and qualified. Won't + return a function, because a name in a function is never public. + """ + return None + class AbstractArbitraryName(AbstractNameDefinition): """ @@ -124,6 +131,15 @@ class AbstractTreeName(AbstractNameDefinition): return None return parent_names + (self.tree_name.value,) + def get_defining_qualified_value(self): + if self.is_import(): + raise 1 + elif self.parent_context: + values = self.parent_context.name.infer() + if len(values) == 1: + return next(iter(values)) + return None + def goto(self): context = self.parent_context name = self.tree_name diff --git a/jedi/inference/value/instance.py b/jedi/inference/value/instance.py index 8071c20a..8fbbebec 100644 --- a/jedi/inference/value/instance.py +++ b/jedi/inference/value/instance.py @@ -509,6 +509,9 @@ class SelfName(TreeNameDefinition): def parent_context(self): return self._instance.create_instance_context(self.class_context, self.tree_name) + def get_defining_qualified_value(self): + return self._instance + class LazyInstanceClassName(NameWrapper): def __init__(self, instance, class_member_name): @@ -524,6 +527,9 @@ class LazyInstanceClassName(NameWrapper): def get_signatures(self): return self.infer().get_signatures() + def get_defining_qualified_value(self): + return self._instance + class InstanceClassFilter(AbstractFilter): """ diff --git a/jedi/inference/value/klass.py b/jedi/inference/value/klass.py index aede4222..24c4236e 100644 --- a/jedi/inference/value/klass.py +++ b/jedi/inference/value/klass.py @@ -74,6 +74,10 @@ class ClassName(TreeNameDefinition): else: yield result_value + @property + def defining_qualified_value(self): + return self._class_value + class ClassFilter(ParserTreeFilter): def __init__(self, class_value, node_context=None, until_position=None, diff --git a/test/test_api/test_classes.py b/test/test_api/test_classes.py index 5f160b81..2711c269 100644 --- a/test/test_api/test_classes.py +++ b/test/test_api/test_classes.py @@ -437,16 +437,17 @@ def test_import(names): assert n.name == 'os' assert n.type == 'module' - n = nms[2].goto()[0] - assert n.name == 'path' - assert n.type == 'module' + nms = nms[2].goto() + assert nms + assert all(n.type == 'module' for n in nms) + assert 'posixpath' in {n.name for n in nms} nms = names('import os.path', references=True) n = nms[0].goto()[0] assert n.name == 'os' assert n.type == 'module' n = nms[1].goto()[0] - # This is very special, normally the name doesn't chance, but since + # This is very special, normally the name doesn't change, but since # os.path is a sys.modules hack, it does. assert n.name in ('macpath', 'ntpath', 'posixpath', 'os2emxpath') assert n.type == 'module' diff --git a/test/test_api/test_completion.py b/test/test_api/test_completion.py index db078293..0057ae83 100644 --- a/test/test_api/test_completion.py +++ b/test/test_api/test_completion.py @@ -152,6 +152,12 @@ def test_method_doc_with_signature(Script): assert c.docstring() == 'writelines(lines: Iterable[AnyStr]) -> None' +def test_method_doc_with_signature2(Script): + code = 'f = open("")\nf.writelines' + d, = Script(code).goto() + assert d.docstring() == 'writelines(lines: Iterable[AnyStr]) -> None' + + def test_with_stmt_error_recovery(Script): assert Script('with open('') as foo: foo.\na').complete(line=1)