From 1f082b69d296ac93f0dc0ab8f083995764f6ae39 Mon Sep 17 00:00:00 2001 From: Peter Law Date: Sat, 13 Jun 2020 23:11:31 +0100 Subject: [PATCH 1/3] Handle passing functions and classes through a TypeVar This fixes #1425 and #1607 by persisting the original underlying function or class when we process a TypeVar they are passed into. --- jedi/inference/gradual/type_var.py | 20 ++++- .../pep0484_generic_passthroughs.py | 83 ++++++++++++++++++- 2 files changed, 100 insertions(+), 3 deletions(-) diff --git a/jedi/inference/gradual/type_var.py b/jedi/inference/gradual/type_var.py index 9b041df0..e5a127af 100644 --- a/jedi/inference/gradual/type_var.py +++ b/jedi/inference/gradual/type_var.py @@ -1,6 +1,6 @@ from jedi._compatibility import unicode, force_unicode from jedi import debug -from jedi.inference.base_value import ValueSet, NO_VALUES +from jedi.inference.base_value import ValueSet, NO_VALUES, ValueWrapper from jedi.inference.gradual.base import BaseTypingValue @@ -108,8 +108,24 @@ class TypeVar(BaseTypingValue): return self._get_classes().execute_annotation() def infer_type_vars(self, value_set): + def iterate(): + for v in value_set: + cls = v.py__class__() + if v.is_function() or v.is_class(): + cls = TypeWrapper(cls, v) + yield cls + annotation_name = self.py__name__() - return {annotation_name: value_set.py__class__()} + return {annotation_name: ValueSet(iterate())} def __repr__(self): return '<%s: %s>' % (self.__class__.__name__, self.py__name__()) + + +class TypeWrapper(ValueWrapper): + def __init__(self, wrapped_value, original_value): + super().__init__(wrapped_value) + self._original_value = original_value + + def execute_annotation(self): + return ValueSet({self._original_value}) diff --git a/test/completion/pep0484_generic_passthroughs.py b/test/completion/pep0484_generic_passthroughs.py index 4fccc5ed..26c03ba5 100644 --- a/test/completion/pep0484_generic_passthroughs.py +++ b/test/completion/pep0484_generic_passthroughs.py @@ -1,9 +1,22 @@ # python >= 3.4 -from typing import Any, Iterable, List, Sequence, Tuple, TypeVar, Union +from typing import ( + Any, + Callable, + Iterable, + List, + Sequence, + Tuple, + Type, + TypeVar, + Union, +) T = TypeVar('T') U = TypeVar('U') TList = TypeVar('TList', bound=List[Any]) +TType = TypeVar('TType', bound=Type) +TTypeAny = TypeVar('TTypeAny', bound=Type[Any]) +TCallable = TypeVar('TCallable', bound=Callable[..., Any]) untyped_list_str = ['abc', 'def'] typed_list_str = ['abc', 'def'] # type: List[str] @@ -40,6 +53,9 @@ def typed_fully_generic_passthrough(x: T) -> T: return x def typed_bound_generic_passthrough(x: TList) -> TList: + #? list() + x + return x @@ -109,6 +125,8 @@ for m in typed_variadic_tuple_generic_passthrough(variadic_tuple_str_int): #? str() int() m +#? float +typed_fully_generic_passthrough(float) for n in typed_fully_generic_passthrough(untyped_list_str): #? str() @@ -142,3 +160,66 @@ CustomList[str]().get_first() typed_fully_generic_passthrough(CustomList[str]())[0] #? typed_list_generic_passthrough(CustomList[str])[0] + + +def typed_bound_type_implicit_any_generic_passthrough(x: TType) -> TType: + #? Type() + x + return x + +def typed_bound_type_any_generic_passthrough(x: TTypeAny) -> TTypeAny: + # Should be Type(), though we don't get the handling of the nested argument + # to `Type[...]` quite right here. + x + return x + + +class MyClass: + pass + +def my_func(a: str, b: int) -> float: + pass + +#? MyClass +typed_fully_generic_passthrough(MyClass) + +#? MyClass() +typed_fully_generic_passthrough(MyClass()) + +#? my_func +typed_fully_generic_passthrough(my_func) + +#? CustomList() +typed_bound_generic_passthrough(CustomList[str]()) + +# should be list(), but we don't validate generic typevar upper bounds +#? int() +typed_bound_generic_passthrough(42) + +#? MyClass +typed_bound_type_implicit_any_generic_passthrough(MyClass) + +#? MyClass +typed_bound_type_any_generic_passthrough(MyClass) + +# should be Type(), but we don't validate generic typevar upper bounds +#? int() +typed_bound_type_implicit_any_generic_passthrough(42) + +# should be Type(), but we don't validate generic typevar upper bounds +#? int() +typed_bound_type_any_generic_passthrough(42) + + +def decorator(fn: TCallable) -> TCallable: + pass + + +def will_be_decorated(var: complex) -> float: + pass + + +is_decorated = decorator(will_be_decorated) + +#? will_be_decorated +is_decorated From 4f11f20e1d880fafcc48e45f002e71a06abd0d59 Mon Sep 17 00:00:00 2001 From: Peter Law Date: Sun, 14 Jun 2020 16:24:38 +0100 Subject: [PATCH 2/3] Add a signature check for decorated functions Specifically where the decorator is type annotated. --- test/completion/pep0484_generic_passthroughs.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/completion/pep0484_generic_passthroughs.py b/test/completion/pep0484_generic_passthroughs.py index 26c03ba5..ddde14de 100644 --- a/test/completion/pep0484_generic_passthroughs.py +++ b/test/completion/pep0484_generic_passthroughs.py @@ -215,7 +215,7 @@ def decorator(fn: TCallable) -> TCallable: pass -def will_be_decorated(var: complex) -> float: +def will_be_decorated(the_param: complex) -> float: pass @@ -223,3 +223,7 @@ is_decorated = decorator(will_be_decorated) #? will_be_decorated is_decorated + +#? ['the_param='] +is_decorated(the_para +) From 7e637c5e5ee3480da6f8675f04d823f31a40ead3 Mon Sep 17 00:00:00 2001 From: Peter Law Date: Sun, 14 Jun 2020 16:27:39 +0100 Subject: [PATCH 3/3] Python 2 compatible super() --- jedi/inference/gradual/type_var.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jedi/inference/gradual/type_var.py b/jedi/inference/gradual/type_var.py index e5a127af..502eaf1f 100644 --- a/jedi/inference/gradual/type_var.py +++ b/jedi/inference/gradual/type_var.py @@ -124,7 +124,7 @@ class TypeVar(BaseTypingValue): class TypeWrapper(ValueWrapper): def __init__(self, wrapped_value, original_value): - super().__init__(wrapped_value) + super(TypeWrapper, self).__init__(wrapped_value) self._original_value = original_value def execute_annotation(self):