From bade4e661facd01c60eb89ec5699427baab32f1e Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Mon, 3 Jun 2019 20:28:04 +0200 Subject: [PATCH] Some changes to get stubs working better for mixed objects --- jedi/evaluate/compiled/mixed.py | 21 +++++++++++---------- jedi/evaluate/gradual/conversion.py | 15 +++++++++++++-- test/test_api/test_interpreter.py | 6 ++++++ 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/jedi/evaluate/compiled/mixed.py b/jedi/evaluate/compiled/mixed.py index d9041122..f520ede5 100644 --- a/jedi/evaluate/compiled/mixed.py +++ b/jedi/evaluate/compiled/mixed.py @@ -19,6 +19,7 @@ from jedi.evaluate.helpers import execute_evaluated from jedi.evaluate.compiled.getattr_static import getattr_static from jedi.evaluate.compiled.access import compiled_objects_cache from jedi.evaluate.compiled.context import create_cached_compiled_object +from jedi.evaluate.gradual.conversion import to_stub class MixedObject(object): @@ -83,9 +84,7 @@ class MixedName(compiled.CompiledName): access_handle = self.parent_context.access_handle # TODO use logic from compiled.CompiledObjectFilter access_handle = access_handle.getattr(self.string_name, default=None) - return ContextSet([ - _create(self._evaluator, access_handle, parent_context=self.parent_context) - ]) + return _create(self._evaluator, access_handle, parent_context=self.parent_context) @property def api_type(self): @@ -198,7 +197,7 @@ def _create(evaluator, access_handle, parent_context, *args): result = _find_syntax_node_name(evaluator, access_handle) if result is None: - return compiled_object + return ContextSet([compiled_object]) module_node, tree_node, file_io, code_lines = result @@ -228,9 +227,11 @@ def _create(evaluator, access_handle, parent_context, *args): # Is an instance, not a class. tree_context, = execute_evaluated(tree_context) - return MixedObject( - evaluator, - parent_context, - compiled_object, - tree_context=tree_context - ) + return ContextSet({ + MixedObject( + evaluator, + parent_context, + compiled_object, + tree_context=c, + ) for c in to_stub(tree_context) or [tree_context] + }) diff --git a/jedi/evaluate/gradual/conversion.py b/jedi/evaluate/gradual/conversion.py index f057d74e..ec368459 100644 --- a/jedi/evaluate/gradual/conversion.py +++ b/jedi/evaluate/gradual/conversion.py @@ -102,10 +102,10 @@ def _load_stub_module(module): def name_to_stub(name): - return ContextSet.from_sets(_to_stub(c) for c in name.infer()) + return ContextSet.from_sets(to_stub(c) for c in name.infer()) -def _to_stub(context): +def to_stub(context): if context.is_stub(): return ContextSet([context]) @@ -118,6 +118,13 @@ def _to_stub(context): if stub_module is None or qualified_names is None: return NO_CONTEXTS + was_bound_method = context.is_bound_method() + if was_bound_method: + # Infer the object first. We can infer the method later. + method_name = qualified_names[-1] + qualified_names = qualified_names[:-1] + was_instance = True + stub_contexts = ContextSet([stub_module]) for name in qualified_names: stub_contexts = stub_contexts.py__getattribute__(name) @@ -128,4 +135,8 @@ def _to_stub(context): for c in stub_contexts if c.is_class() ) + if was_bound_method: + # Now that the instance has been properly created, we can simply get + # the method. + stub_contexts = stub_contexts.py__getattribute__(method_name) return stub_contexts diff --git a/test/test_api/test_interpreter.py b/test/test_api/test_interpreter.py index ac99515b..e39dd299 100644 --- a/test/test_api/test_interpreter.py +++ b/test/test_api/test_interpreter.py @@ -358,6 +358,12 @@ def test_name_not_findable(): assert jedi.Interpreter("X.NOT_FINDA", [locals()]).completions() +def test_stubs_working(): + from multiprocessing import cpu_count + defs = jedi.Interpreter("cpu_count()", [locals()]).goto_definitions() + assert [d.name for d in defs] == ['int'] + + def test_sys_path_docstring(): # Was an issue in #1298 import jedi s = jedi.Interpreter("from sys import path\npath", line=2, column=4, namespaces=[locals()])