diff --git a/jedi/inference/compiled/access.py b/jedi/inference/compiled/access.py index 45a49a61..64d5cbbe 100644 --- a/jedi/inference/compiled/access.py +++ b/jedi/inference/compiled/access.py @@ -315,6 +315,9 @@ class DirectObjectAccess(object): def is_class(self): return inspect.isclass(self._obj) + def is_function(self): + return inspect.isfunction(self._obj) or inspect.ismethod(self._obj) + def is_module(self): return inspect.ismodule(self._obj) diff --git a/jedi/inference/compiled/mixed.py b/jedi/inference/compiled/mixed.py index 3a889345..4c2785b7 100644 --- a/jedi/inference/compiled/mixed.py +++ b/jedi/inference/compiled/mixed.py @@ -14,7 +14,6 @@ from jedi.inference import compiled from jedi.cache import underscore_memoization from jedi.file_io import FileIO from jedi.inference.base_value import ValueSet, ValueWrapper, NO_VALUES -from jedi.inference.helpers import SimpleGetItemNotFound from jedi.inference.value import ModuleValue from jedi.inference.cache import inference_state_function_cache, \ inference_state_method_cache @@ -50,6 +49,9 @@ class MixedObject(ValueWrapper): self.compiled_object = compiled_object self.access_handle = compiled_object.access_handle + def get_tree_value(self): + return self._wrapped_value + def get_filters(self, *args, **kwargs): yield MixedObjectFilter(self.inference_state, self) @@ -76,7 +78,7 @@ class MixedObject(ValueWrapper): python_object = self.compiled_object.access_handle.access._obj if type(python_object) in ALLOWED_GETITEM_TYPES: return self.compiled_object.py__simple_getitem__(index) - raise SimpleGetItemNotFound + return self._wrapped_value.py__simple_getitem__(index) def _as_context(self): if self.parent_context is None: @@ -137,6 +139,17 @@ class MixedName(compiled.CompiledName): default=None ) assert len(access_paths) + tree_value = self._parent_value.get_tree_value() + if tree_value.is_instance() or tree_value.is_class(): + from jedi.inference.compiled.value import _create_from_name + tree_values = tree_value.py__getattribute__(self.string_name) + compiled_object = _create_from_name( + self._inference_state, + self._parent_value.compiled_object, + self.string_name + ) + if compiled_object.is_function(): + return ValueSet({MixedObject(compiled_object, v) for v in tree_values}) values = [None] for access in access_paths: values = ValueSet.from_sets(access_to_value(v, access) for v in values) @@ -310,7 +323,6 @@ def _create(inference_state, access_handle, parent_context, *args): # Python's TypeVar('foo').__module__ will be typing. return ValueSet({compiled_object}) module_context = parent_context.get_root_context() - tree_values = ValueSet({module_context.create_value(tree_node)}) if tree_node.type == 'classdef': if not access_handle.is_class(): diff --git a/jedi/inference/compiled/value.py b/jedi/inference/compiled/value.py index a2d67a74..df84d4cf 100644 --- a/jedi/inference/compiled/value.py +++ b/jedi/inference/compiled/value.py @@ -95,6 +95,9 @@ class CompiledObject(Value): def is_class(self): return self.access_handle.is_class() + def is_function(self): + return self.access_handle.is_function() + def is_module(self): return self.access_handle.is_module() diff --git a/test/test_inference/test_mixed.py b/test/test_inference/test_mixed.py index 968a1a66..83276b2e 100644 --- a/test/test_inference/test_mixed.py +++ b/test/test_inference/test_mixed.py @@ -42,10 +42,12 @@ def test_generics_without_definition(): @pytest.mark.parametrize( 'code, expected', [ - ('Foo.method()', 'int'), - ('Foo.method()', 'int'), + ('Foo().method()', 'str'), + ('Foo.method()', 'str'), + ('foo.method()', 'str'), ('Foo().read()', 'str'), ('Foo.read()', 'str'), + ('foo.read()', 'str'), ] ) def test_generics_methods(code, expected, class_findable): @@ -63,6 +65,8 @@ def test_generics_methods(code, expected, class_findable): def transform(self) -> int: return 42 + foo = Foo() + defs = jedi.Interpreter(code, [locals()]).infer() if class_findable: def_, = defs