Support passing values through decorators from factories

This builds on the approach taken in https://github.com/davidhalter/jedi/pull/1613
but applies it to type vars themselves so that their type var
nature is preserved when a function returns Callable[[T], T] and
the T has an upper bound.
This commit is contained in:
Peter Law
2020-06-14 18:17:09 +01:00
parent 2d0258db1a
commit 5184d0cb9c
3 changed files with 59 additions and 3 deletions

View File

@@ -14,7 +14,7 @@ from jedi.inference.cache import inference_state_method_cache
from jedi.inference.base_value import ValueSet, NO_VALUES
from jedi.inference.gradual.base import DefineGenericBaseClass, GenericClass
from jedi.inference.gradual.generics import TupleGenericManager
from jedi.inference.gradual.type_var import TypeVar
from jedi.inference.gradual.type_var import TypeVar, TypeVarWrapper
from jedi.inference.helpers import is_string
from jedi.inference.compiled import builtin_from_name
from jedi.inference.param import get_executed_param_names
@@ -281,7 +281,8 @@ def infer_return_for_callable(arguments, param_values, result_values):
return ValueSet.from_sets(
v.define_generics(all_type_vars)
if isinstance(v, (DefineGenericBaseClass, TypeVar)) else ValueSet({v})
if isinstance(v, (DefineGenericBaseClass, TypeVar, TypeVarWrapper))
else ValueSet({v})
for v in result_values
).execute_annotation()

View File

@@ -102,7 +102,12 @@ class TypeVar(BaseTypingValue):
else:
if found:
return found
return self._get_classes() or ValueSet({self})
classes = self._get_classes()
if not classes:
return ValueSet({self})
return ValueSet(TypeVarWrapper(cls, self) for cls in classes)
def execute_annotation(self):
return self._get_classes().execute_annotation()
@@ -122,6 +127,15 @@ class TypeVar(BaseTypingValue):
return '<%s: %s>' % (self.__class__.__name__, self.py__name__())
class TypeVarWrapper(ValueWrapper):
def __init__(self, wrapped_value, original_value):
super(TypeVarWrapper, self).__init__(wrapped_value)
self._original_value = original_value
def define_generics(self, type_var_dict):
return self._original_value.define_generics(type_var_dict)
class TypeWrapper(ValueWrapper):
def __init__(self, wrapped_value, original_value):
super(TypeWrapper, self).__init__(wrapped_value)

View File

@@ -250,6 +250,28 @@ is_decorated_by_class_decorator_factory(the_par
)
def decorator_factory_plain() -> Callable[[T], T]:
pass
#? Callable()
decorator_factory_plain()
#?
decorator_factory_plain()()
#? int()
decorator_factory_plain()(42)
is_decorated_by_plain_factory = decorator_factory_plain()(will_be_decorated)
#? will_be_decorated
is_decorated_by_plain_factory
#? ['the_param=']
is_decorated_by_plain_factory(the_par
)
class class_decorator_factory_bound_callable:
def __call__(self, func: TCallable) -> TCallable:
...
@@ -270,6 +292,25 @@ is_decorated_by_class_bound_factory(the_par
)
def decorator_factory_bound_callable() -> Callable[[TCallable], TCallable]:
pass
#? Callable()
decorator_factory_bound_callable()
#? Callable()
decorator_factory_bound_callable()()
is_decorated_by_bound_factory = decorator_factory_bound_callable()(will_be_decorated)
#? will_be_decorated
is_decorated_by_bound_factory
#? ['the_param=']
is_decorated_by_bound_factory(the_par
)
class That(Generic[T]):
def __init__(self, items: List[Tuple[str, T]]) -> None:
pass