From cfa01d3ac5d1dbb62f8ce4a6ea4737e9b8e26fd4 Mon Sep 17 00:00:00 2001 From: Peter Law Date: Sun, 19 Apr 2020 14:10:03 +0100 Subject: [PATCH 1/8] Add handling of nested generic tuples --- jedi/inference/gradual/typing.py | 11 +++++++-- test/completion/pep0484_generic_mismatches.py | 19 +++++++++++++++ test/completion/pep0484_generic_parameters.py | 23 +++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/jedi/inference/gradual/typing.py b/jedi/inference/gradual/typing.py index ab0b449b..8e90f249 100644 --- a/jedi/inference/gradual/typing.py +++ b/jedi/inference/gradual/typing.py @@ -327,6 +327,9 @@ class Tuple(BaseTypingValueWithGenerics): from jedi.inference.gradual.annotation import merge_pairwise_generics, merge_type_var_dicts from jedi.inference.gradual.base import GenericClass + if is_class_value: + value_set = value_set.execute_annotation() + if self._is_homogenous(): # The parameter annotation is of the form `Tuple[T, ...]`, # so we treat the incoming tuple like a iterable sequence @@ -343,8 +346,12 @@ class Tuple(BaseTypingValueWithGenerics): type_var_dict = {} for element in value_set: - py_class = element.get_annotated_class_object() - if not isinstance(py_class, GenericClass): + if not is_class_value: + py_class = element.get_annotated_class_object() + + if not isinstance(py_class, GenericClass): + py_class = element + else: py_class = element merge_type_var_dicts( diff --git a/test/completion/pep0484_generic_mismatches.py b/test/completion/pep0484_generic_mismatches.py index 47add048..64721b8c 100644 --- a/test/completion/pep0484_generic_mismatches.py +++ b/test/completion/pep0484_generic_mismatches.py @@ -33,6 +33,7 @@ tpl_typed = ("2", 3) # type: Tuple[str, int] collection = {"a": 1} collection_typed = {"a": 1} # type: Dict[str, int] +list_of_ints = [42] # type: List[int] list_of_funcs = [foo] # type: List[Callable[[T], T]] custom_generic = CustomGeneric(123.45) @@ -323,3 +324,21 @@ x7 for a in list_t_to_list_t(12): #? a + + +def list_tuple_t_to_tuple_list_t(the_list: List[Tuple[T]]) -> Tuple[List[T], ...]: + return tuple(list(x) for x in the_list) + + +for b in list_tuple_t_to_tuple_list_t(list_of_ints): + #? + b[0] + + +def list_tuple_t_elipsis_to_tuple_list_t(the_list: List[Tuple[T, ...]]) -> Tuple[List[T], ...]: + return tuple(list(x) for x in the_list) + + +for b in list_tuple_t_to_tuple_list_t(list_of_ints): + #? + b[0] diff --git a/test/completion/pep0484_generic_parameters.py b/test/completion/pep0484_generic_parameters.py index 89572d99..41408215 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, + Tuple, Type, TypeVar, Union, @@ -59,6 +60,28 @@ for b in list_type_t_to_list_t(list_of_int_type): b +def list_tuple_t_to_tuple_list_t(the_list: List[Tuple[T]]) -> Tuple[List[T], ...]: + return tuple(list(x) for x in the_list) + + +list_of_int_tuples = [(x,) for x in list_of_ints] # type: List[Tuple[int]] + +for b in list_tuple_t_to_tuple_list_t(list_of_int_tuples): + #? int() + b[0] + + +def list_tuple_t_elipsis_to_tuple_list_t(the_list: List[Tuple[T, ...]]) -> Tuple[List[T], ...]: + return tuple(list(x) for x in the_list) + + +list_of_int_tuples = [tuple(list_of_ints)] # type: List[Tuple[int, ...]] + +for b in list_tuple_t_elipsis_to_tuple_list_t(list_of_int_tuples): + #? int() + b[0] + + def foo(x: T) -> T: return x From 72c52f5f15d3e08c8b4f13d532ba7d125112ae95 Mon Sep 17 00:00:00 2001 From: Peter Law Date: Sun, 19 Apr 2020 14:29:44 +0100 Subject: [PATCH 2/8] Add type match guard --- jedi/inference/gradual/typing.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jedi/inference/gradual/typing.py b/jedi/inference/gradual/typing.py index 8e90f249..85851fca 100644 --- a/jedi/inference/gradual/typing.py +++ b/jedi/inference/gradual/typing.py @@ -346,6 +346,10 @@ class Tuple(BaseTypingValueWithGenerics): type_var_dict = {} for element in value_set: + element_name = element.py__name__() + if element_name != 'tuple': + continue + if not is_class_value: py_class = element.get_annotated_class_object() From 343a10d491baad4b65dceafb019f5186728fa927 Mon Sep 17 00:00:00 2001 From: Peter Law Date: Sun, 19 Apr 2020 14:42:57 +0100 Subject: [PATCH 3/8] Drop redundant blank line --- jedi/inference/gradual/typing.py | 1 - 1 file changed, 1 deletion(-) diff --git a/jedi/inference/gradual/typing.py b/jedi/inference/gradual/typing.py index 85851fca..6a820093 100644 --- a/jedi/inference/gradual/typing.py +++ b/jedi/inference/gradual/typing.py @@ -352,7 +352,6 @@ class Tuple(BaseTypingValueWithGenerics): if not is_class_value: py_class = element.get_annotated_class_object() - if not isinstance(py_class, GenericClass): py_class = element else: From df951733cd99f302d1d819852f3c908163ce9bdd Mon Sep 17 00:00:00 2001 From: Peter Law Date: Fri, 24 Apr 2020 12:45:05 +0100 Subject: [PATCH 4/8] Rename variable to placate mypy --- test/completion/pep0484_generic_parameters.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/completion/pep0484_generic_parameters.py b/test/completion/pep0484_generic_parameters.py index 41408215..e0b07e4a 100644 --- a/test/completion/pep0484_generic_parameters.py +++ b/test/completion/pep0484_generic_parameters.py @@ -75,9 +75,9 @@ def list_tuple_t_elipsis_to_tuple_list_t(the_list: List[Tuple[T, ...]]) -> Tuple return tuple(list(x) for x in the_list) -list_of_int_tuples = [tuple(list_of_ints)] # type: List[Tuple[int, ...]] +list_of_int_tuple_elipsis = [tuple(list_of_ints)] # type: List[Tuple[int, ...]] -for b in list_tuple_t_elipsis_to_tuple_list_t(list_of_int_tuples): +for b in list_tuple_t_elipsis_to_tuple_list_t(list_of_int_tuple_elipsis): #? int() b[0] From ce1ac38cded325f36e1cc58421e94f20021c8f81 Mon Sep 17 00:00:00 2001 From: Peter Law Date: Fri, 24 Apr 2020 16:25:19 +0100 Subject: [PATCH 5/8] Implement get_annotated_class_object for Tuples --- jedi/inference/gradual/typing.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/jedi/inference/gradual/typing.py b/jedi/inference/gradual/typing.py index 6a820093..cdc6f1a9 100644 --- a/jedi/inference/gradual/typing.py +++ b/jedi/inference/gradual/typing.py @@ -136,6 +136,15 @@ class TypingValueWithIndex(BaseTypingValueWithGenerics): def gather_annotation_classes(self): return ValueSet.from_sets(self._generics_manager.to_tuple()) + def get_annotated_class_object(self): + if self._tree_name.value == 'Tuple': + return Tuple( + self.parent_context, + self._tree_name, + generics_manager=self._generics_manager, + ) + return super().get_annotated_class_object() + def _create_instance_with_generics(self, generics_manager): return TypingValueWithIndex( self.parent_context, @@ -222,7 +231,7 @@ class TypingClassValueWithIndex(_TypingClassMixin, TypingValueWithIndex): ) elif annotation_name == 'Tuple': - tuple_annotation, = self.execute_annotation() + tuple_annotation = self.get_annotated_class_object() return tuple_annotation.infer_type_vars(value_set, is_class_value) return type_var_dict From 891383f8dcfd0449829e9676068c64b38bd476e1 Mon Sep 17 00:00:00 2001 From: Peter Law Date: Fri, 24 Apr 2020 16:32:00 +0100 Subject: [PATCH 6/8] Use get_annotated_class_object over execute_annotation --- jedi/inference/gradual/typing.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/jedi/inference/gradual/typing.py b/jedi/inference/gradual/typing.py index cdc6f1a9..5fff9571 100644 --- a/jedi/inference/gradual/typing.py +++ b/jedi/inference/gradual/typing.py @@ -337,7 +337,11 @@ class Tuple(BaseTypingValueWithGenerics): from jedi.inference.gradual.base import GenericClass if is_class_value: - value_set = value_set.execute_annotation() + value_set = ValueSet([ + value.get_annotated_class_object() + for value in value_set + if value.py__name__() == 'Tuple' + ]) if self._is_homogenous(): # The parameter annotation is of the form `Tuple[T, ...]`, From c19c13e2c6f1726bf6b2e5aced1e6c42a78170c4 Mon Sep 17 00:00:00 2001 From: Peter Law Date: Fri, 24 Apr 2020 16:44:25 +0100 Subject: [PATCH 7/8] Apply tuple-only filtering to apply more broadly --- jedi/inference/gradual/typing.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jedi/inference/gradual/typing.py b/jedi/inference/gradual/typing.py index 5fff9571..66301d73 100644 --- a/jedi/inference/gradual/typing.py +++ b/jedi/inference/gradual/typing.py @@ -342,6 +342,10 @@ class Tuple(BaseTypingValueWithGenerics): for value in value_set if value.py__name__() == 'Tuple' ]) + else: + value_set = value_set.filter( + lambda x: x.py__name__() == 'tuple', + ) if self._is_homogenous(): # The parameter annotation is of the form `Tuple[T, ...]`, @@ -359,10 +363,6 @@ class Tuple(BaseTypingValueWithGenerics): type_var_dict = {} for element in value_set: - element_name = element.py__name__() - if element_name != 'tuple': - continue - if not is_class_value: py_class = element.get_annotated_class_object() if not isinstance(py_class, GenericClass): From 55facaaf3d36c1e896f721645a255461c63ba396 Mon Sep 17 00:00:00 2001 From: Peter Law Date: Sun, 26 Apr 2020 14:30:18 +0100 Subject: [PATCH 8/8] Switch back to using execute_annotation get_annotated_class_object is (sort-of) the inverse of execute_annotation, so adding a get_annotated_class_object to implement execute_annotation specifically for Tuples didn't make much sense. --- jedi/inference/gradual/typing.py | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/jedi/inference/gradual/typing.py b/jedi/inference/gradual/typing.py index 075dc0a2..189fe997 100644 --- a/jedi/inference/gradual/typing.py +++ b/jedi/inference/gradual/typing.py @@ -136,15 +136,6 @@ class TypingValueWithIndex(BaseTypingValueWithGenerics): def gather_annotation_classes(self): return ValueSet.from_sets(self._generics_manager.to_tuple()) - def get_annotated_class_object(self): - if self._tree_name.value == 'Tuple': - return Tuple( - self.parent_context, - self._tree_name, - generics_manager=self._generics_manager, - ) - return super().get_annotated_class_object() - def _create_instance_with_generics(self, generics_manager): return TypingValueWithIndex( self.parent_context, @@ -246,7 +237,7 @@ class TypingClassValueWithIndex(_TypingClassMixin, TypingValueWithIndex): ) elif annotation_name == 'Tuple': - tuple_annotation = self.get_annotated_class_object() + tuple_annotation, = self.execute_annotation() return tuple_annotation.infer_type_vars(value_set, is_class_value) return type_var_dict @@ -351,16 +342,17 @@ class Tuple(BaseTypingValueWithGenerics): from jedi.inference.gradual.annotation import merge_pairwise_generics, merge_type_var_dicts from jedi.inference.gradual.base import GenericClass + value_set = value_set.filter( + lambda x: x.py__name__().lower() == 'tuple', + ) + + # Somewhat unusually, this `infer_type_vars` method is on an instance + # representation of a type, rather than the annotation or class + # representation. This means that as a starting point, we need to + # convert the incoming values to their instance style if they're + # classes, rather than the reverse. if is_class_value: - value_set = ValueSet([ - value.get_annotated_class_object() - for value in value_set - if value.py__name__() == 'Tuple' - ]) - else: - value_set = value_set.filter( - lambda x: x.py__name__() == 'tuple', - ) + value_set = value_set.execute_annotation() if self._is_homogenous(): # The parameter annotation is of the form `Tuple[T, ...]`,