diff --git a/jedi/inference/compiled/mixed.py b/jedi/inference/compiled/mixed.py index 3087914c..8152c78a 100644 --- a/jedi/inference/compiled/mixed.py +++ b/jedi/inference/compiled/mixed.py @@ -13,10 +13,11 @@ from jedi import settings 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 +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 +from jedi.inference.cache import inference_state_function_cache, \ + inference_state_method_cache from jedi.inference.compiled.getattr_static import getattr_static from jedi.inference.compiled.access import compiled_objects_cache, \ ALLOWED_GETITEM_TYPES, get_api_type @@ -58,8 +59,13 @@ class MixedObject(ValueWrapper): # should be very precise, especially for stuff like `partial`. return self.compiled_object.get_signatures() + @inference_state_method_cache(default=NO_VALUES) def py__call__(self, arguments): - return (to_stub(self._wrapped_value) or self._wrapped_value).py__call__(arguments) + # Fallback to the wrapped value if to stub returns no values. + values = to_stub(self._wrapped_value) + if not values:# or self in values: + values = self._wrapped_value + return values.py__call__(arguments) def get_safe_value(self, default=_sentinel): if default is _sentinel: diff --git a/test/test_inference/test_mixed.py b/test/test_inference/test_mixed.py index 5940fe6c..e9873c87 100644 --- a/test/test_inference/test_mixed.py +++ b/test/test_inference/test_mixed.py @@ -1,7 +1,42 @@ +import sys +if sys.version_info > (3, 5): + from typing import Generic, TypeVar, List + +import pytest + import jedi +def interpreter(code, namespace, *args, **kwargs): + return jedi.Interpreter(code, [namespace], *args, **kwargs) + + def test_on_code(): from functools import wraps - i = jedi.Interpreter("wraps.__code__", [{'wraps':wraps}]) + i = interpreter("wraps.__code__", {'wraps': wraps}) assert i.goto_definitions() + + +@pytest.mark.skipif('sys.version_info < (3,5)') +def test_generics(): + # Used to raise a recursion error + T = TypeVar('T') + + class Stack(Generic[T]): + def __init__(self): + self.items = [] # type: List[T] + + def push(self, item): + self.items.append(item) + + def pop(self): + # type: () -> T + return self.items.pop() + + class StackWrapper(): + def __init__(self): + self.stack = Stack() + self.stack.push(1) + + s = StackWrapper() + print(interpreter('s.stack.pop().', locals()).completions())