diff --git a/jedi/inference/gradual/typing.py b/jedi/inference/gradual/typing.py index add28177..e0ebcb96 100644 --- a/jedi/inference/gradual/typing.py +++ b/jedi/inference/gradual/typing.py @@ -145,6 +145,24 @@ class TypingClassWithIndex(BaseTypingClassWithGenerics): generics_manager ) + def infer_type_vars(self, value_set): + annotation_generics = self.get_generics() + + if not annotation_generics: + return {} + + annotation_name = self.py__name__() + if annotation_name == 'Optional': + # Optional[T] is equivalent to Union[T, None]. In Jedi unions + # are represented by members within a ValueSet, so we extract + # the T from the Optional[T] by removing the None value. + none = builtin_from_name(self.inference_state, u'None') + return annotation_generics[0].infer_type_vars( + value_set.filter(lambda x: x != none), + ) + + return {} + class ProxyTypingValue(BaseTypingValue): index_class = TypingClassWithIndex diff --git a/test/completion/pep0484_generic_parameters.py b/test/completion/pep0484_generic_parameters.py index 3898b17f..cbbc4537 100644 --- a/test/completion/pep0484_generic_parameters.py +++ b/test/completion/pep0484_generic_parameters.py @@ -6,6 +6,7 @@ from typing import ( Iterable, List, Mapping, + Optional, Tuple, Type, TypeVar, @@ -19,7 +20,11 @@ T_co = TypeVar('T_co', covariant=True) V = TypeVar('V') +just_float = 42. # type: float +optional_float = 42. # type: Optional[float] list_of_ints = [42] # type: List[int] +list_of_floats = [42.] # type: List[float] +list_of_optional_floats = [x or None for x in list_of_floats] # type: List[Optional[float]] list_of_ints_and_strs = [42, 'abc'] # type: List[Union[int, str]] # Test that simple parameters are handled @@ -47,6 +52,50 @@ for z in list_t_to_list_t(list_of_ints_and_strs): list_of_int_type = [int] # type: List[Type[int]] # Test that nested parameters are handled +def list_optional_t_to_list_t(the_list: List[Optional[T]]) -> List[T]: + return [x for x in the_list if x is not None] + + +for xa in list_optional_t_to_list_t(list_of_optional_floats): + #? float() + xa + +# Under covariance rules this is strictly incorrect (because List is mutable, +# the function would be allowed to put `None`s into our List[float], which would +# be bad), however we don't expect jedi to enforce that. +for xa1 in list_optional_t_to_list_t(list_of_floats): + #? float() + xa1 + + +def optional_t_to_list_t(x: Optional[T]) -> List[T]: + return [x] if x is not None else [] + + +for xb in optional_t_to_list_t(optional_float): + #? float() + xb + + +for xb2 in optional_t_to_list_t(just_float): + #? float() + xb2 + + +def optional_list_t_to_list_t(x: Optional[List[T]]) -> List[T]: + return x if x is not None else [] + + +optional_list_float = None # type: Optional[List[float]] +for xc in optional_list_t_to_list_t(optional_list_float): + #? float() + xc + +for xc2 in optional_list_t_to_list_t(list_of_floats): + #? float() + xc2 + + def list_type_t_to_list_t(the_list: List[Type[T]]) -> List[T]: return [x() for x in the_list]