diff --git a/jedi/api/classes.py b/jedi/api/classes.py index 1551a049..409d674e 100644 --- a/jedi/api/classes.py +++ b/jedi/api/classes.py @@ -301,6 +301,18 @@ class BaseDefinition(object): return '.'.join(path if path[0] else path[1:]) + def is_stub(self): + return all(c.is_stub() for c in self._name.infer()) + + def goto_stubs(self): + if self.is_stub(): + return [self] + + return [ + Definition(self._evaluator, d.stub_context.name) + for d in self._name.infer() if d.stub_context is not None + ] + def goto_assignments(self): if self._name.tree_name is None: return self diff --git a/jedi/evaluate/base_context.py b/jedi/evaluate/base_context.py index 4ba67669..4ee7aa35 100644 --- a/jedi/evaluate/base_context.py +++ b/jedi/evaluate/base_context.py @@ -102,6 +102,10 @@ class Context(HelperContextMixin, BaseContext): To be defined by subclasses. """ tree_node = None + stub_context = None + + def is_stub(self): + return False @property def api_type(self): diff --git a/jedi/evaluate/gradual/stub_context.py b/jedi/evaluate/gradual/stub_context.py index 9915ed14..e8e9408e 100644 --- a/jedi/evaluate/gradual/stub_context.py +++ b/jedi/evaluate/gradual/stub_context.py @@ -87,6 +87,9 @@ class StubMethodContext(StubFunctionContext): class _StubOnlyContextMixin(object): _add_non_stubs_in_filter = False + def is_stub(self): + return True + def _get_stub_only_filters(self, **filter_kwargs): return [StubOnlyFilter( self.evaluator, @@ -155,6 +158,9 @@ class _CompiledStubContext(ContextWrapper): super(_CompiledStubContext, self).__init__(stub_context) self._compiled_context = compiled_context + def is_stub(self): + return True + def py__doc__(self, include_call_signature=False): doc = self._compiled_context.py__doc__() if include_call_signature: diff --git a/test/test_evaluate/test_gradual/test_typeshed.py b/test/test_evaluate/test_gradual/test_typeshed.py index 440b4b13..dac50c75 100644 --- a/test/test_evaluate/test_gradual/test_typeshed.py +++ b/test/test_evaluate/test_gradual/test_typeshed.py @@ -152,12 +152,34 @@ def test_type_var(Script): assert def_.description == 'TypeVar = object()' -@pytest.mark.parametrize('code', ('import math', 'from math import cos')) -def test_math_is_stub(Script, code): +@pytest.mark.parametrize( + 'code, full_name', ( + ('import math', 'math'), + ('from math import cos', 'math.cos') + ) +) +def test_math_is_stub(Script, code, full_name): s = Script(code) cos, = s.goto_definitions() wanted = os.path.join('typeshed', 'stdlib', '2and3', 'math.pyi') assert cos.module_path.endswith(wanted) + assert cos.is_stub() is True + assert cos.goto_stubs() == [cos] + assert cos.full_name == full_name cos, = s.goto_assignments() assert cos.module_path.endswith(wanted) + assert cos.goto_stubs() == [cos] + assert cos.is_stub() is True + assert cos.full_name == full_name + + +def test_goto_stubs(Script): + s = Script('import os; os') + os_module, = s.goto_definitions() + assert os_module.full_name == 'os' + assert os_module.is_stub() is False + stub, = os_module.goto_stubs() + assert stub.is_stub() is True + + os_module, = s.goto_assignments()