From 9c0efd5a67bea732f7bf62fab735580eebeec4ba Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Sat, 25 Jan 2020 01:07:20 +0100 Subject: [PATCH] Prepare a test for #1479 --- conftest.py | 4 ++++ jedi/api/interpreter.py | 11 ++++++---- jedi/inference/compiled/mixed.py | 5 +++-- test/conftest.py | 12 +++++++++++ test/test_api/test_interpreter.py | 2 +- test/test_inference/test_mixed.py | 36 +++++++++++++++++++++++++++---- 6 files changed, 59 insertions(+), 11 deletions(-) diff --git a/conftest.py b/conftest.py index 70df4cb8..6f4d2406 100644 --- a/conftest.py +++ b/conftest.py @@ -1,6 +1,7 @@ import tempfile import shutil import os +import sys from functools import partial import pytest @@ -16,6 +17,9 @@ collect_ignore = [ 'build/', 'test/examples', ] +if sys.version_info < (3, 5): + # Python 2 not supported syntax + collect_ignore.append('test/test_inference/test_mixed.py') # The following hooks (pytest_configure, pytest_unconfigure) are used diff --git a/jedi/api/interpreter.py b/jedi/api/interpreter.py index df5c2804..ac4aebd6 100644 --- a/jedi/api/interpreter.py +++ b/jedi/api/interpreter.py @@ -24,15 +24,18 @@ class MixedModuleContext(ModuleContext): super(MixedModuleContext, self).__init__(tree_module_value) self._namespace_objects = [NamespaceObject(n) for n in namespaces] + def _get_mixed_object(self, compiled_object): + return mixed.MixedObject( + compiled_object=compiled_object, + tree_value=self._value + ) + def get_filters(self, *args, **kwargs): for filter in self._value.as_context().get_filters(*args, **kwargs): yield filter for namespace_obj in self._namespace_objects: compiled_object = _create(self.inference_state, namespace_obj) - mixed_object = mixed.MixedObject( - compiled_object=compiled_object, - tree_value=self._value - ) + mixed_object = self._get_mixed_object(compiled_object) for filter in mixed_object.get_filters(*args, **kwargs): yield filter diff --git a/jedi/inference/compiled/mixed.py b/jedi/inference/compiled/mixed.py index f435e887..3a889345 100644 --- a/jedi/inference/compiled/mixed.py +++ b/jedi/inference/compiled/mixed.py @@ -84,9 +84,10 @@ class MixedObject(ValueWrapper): return MixedContext(self) def __repr__(self): - return '<%s: %s>' % ( + return '<%s: %s; %s>' % ( type(self).__name__, - self.access_handle.get_repr() + self.access_handle.get_repr(), + self._wrapped_value, ) diff --git a/test/conftest.py b/test/conftest.py index 64bc8be7..8fe64c38 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -13,6 +13,7 @@ from jedi.inference.compiled.value import create_from_access_path from jedi.inference.imports import _load_python_module from jedi.file_io import KnownContentFileIO from jedi.inference.base_value import ValueSet +from jedi.api.interpreter import MixedModuleContext # For interpreter tests sometimes the path of this directory is in the sys # path, which we definitely don't want. So just remove it globally. @@ -173,3 +174,14 @@ def module_injector(): inference_state.module_cache.add(names, ValueSet([v])) return module_injector + + +@pytest.fixture(params=[False, True]) +def class_findable(monkeypatch, request): + if not request.param: + monkeypatch.setattr( + MixedModuleContext, + '_get_mixed_object', + lambda self, compiled_object: compiled_object.as_context() + ) + return request.param diff --git a/test/test_api/test_interpreter.py b/test/test_api/test_interpreter.py index bd9d75f5..160639eb 100644 --- a/test/test_api/test_interpreter.py +++ b/test/test_api/test_interpreter.py @@ -18,7 +18,7 @@ else: eval(compile("""def exec_(source, global_map): exec source in global_map """, 'blub', 'exec')) -if py_version > 34: +if py_version > 35: import typing else: typing = None diff --git a/test/test_inference/test_mixed.py b/test/test_inference/test_mixed.py index 9460f9ae..968a1a66 100644 --- a/test/test_inference/test_mixed.py +++ b/test/test_inference/test_mixed.py @@ -1,6 +1,4 @@ -import sys -if sys.version_info > (3, 5): - from typing import Generic, TypeVar, List +from typing import Generic, TypeVar, List import pytest @@ -18,7 +16,6 @@ def test_on_code(): assert i.infer() -@pytest.mark.skipif('sys.version_info < (3,5)') def test_generics_without_definition(): # Used to raise a recursion error T = TypeVar('T') @@ -43,6 +40,37 @@ def test_generics_without_definition(): assert not interpreter('s.stack.pop().', locals()).complete() +@pytest.mark.parametrize( + 'code, expected', [ + ('Foo.method()', 'int'), + ('Foo.method()', 'int'), + ('Foo().read()', 'str'), + ('Foo.read()', 'str'), + ] +) +def test_generics_methods(code, expected, class_findable): + T = TypeVar("T") + + class Reader(Generic[T]): + @classmethod + def read(cls) -> T: + return cls() + + def method(self) -> T: + return 1 + + class Foo(Reader[str]): + def transform(self) -> int: + return 42 + + defs = jedi.Interpreter(code, [locals()]).infer() + if class_findable: + def_, = defs + assert def_.name == expected + else: + assert not defs + + def test_mixed_module_cache(): """Caused by #1479""" interpreter = jedi.Interpreter('jedi', [{'jedi': jedi}])