From 95cec459a8b3a485665a46ead4661e92de9ce5db Mon Sep 17 00:00:00 2001 From: Peter Law Date: Sat, 7 Mar 2020 17:06:22 +0000 Subject: [PATCH] Extract nested function for common pattern This slightly simplifies the code, as well as providing a place to put an explanation of what the moved block of code does. --- jedi/inference/gradual/annotation.py | 94 ++++++++++++++++------------ 1 file changed, 55 insertions(+), 39 deletions(-) diff --git a/jedi/inference/gradual/annotation.py b/jedi/inference/gradual/annotation.py index 932067f5..9f291425 100644 --- a/jedi/inference/gradual/annotation.py +++ b/jedi/inference/gradual/annotation.py @@ -356,6 +356,56 @@ def _infer_type_vars(annotation_value, value_set, is_class_value=False): type_var_dict = {} annotation_name = annotation_value.py__name__() + def merge_pairwise_generics(annotation_generics, actual_generics): + """ + Given iterables of the generics immediately within an annotation and + within an argument's type, match up the generic parameters with the + corrsponding actual types. + + For example, given the following code: + + def values(mapping: Mapping[K, V]) -> List[V]: ... + + for val in values({1: 'a'}): + val + + Then in this function we are given `K` & `V` and `int` & `str` in order + to determine that `K` is `int and `V` is `str`. + + Parameters + ---------- + + `annotation_generics`: an ordered collection of the immediately nested + generic parameters within the annotation being considered. In the + above example, the caller would be analysing `Mapping[K, V]` and + would give us an iterable yileding representations of the parameters + `K` and then `V`. + + `actual_generics`: an ordered collection of the immediately nested + generic parameters within the type of the argument being considered. + These need to be the parameters at the level of the type for which + the annotation generics are given, rather than of the actual type of + the parameter. + In the above example, the caller would be analysing `{1: 'a'}`. The + caller must have already determined that this is an instance of a + type which implements `Mapping` and should pass us an iterable over + representations of the type parameters to that `Mapping` + implementation (here, `int` and `str` in that order). + """ + + for annotation_generics_set, actual_generic_set in zip(annotation_generics, actual_generics): + for nested_annotation_value in annotation_generics_set: + _merge_type_var_dicts( + type_var_dict, + _infer_type_vars( + nested_annotation_value, + actual_generic_set, + # This is a note to ourselves that we have already + # converted the instance representation to its class. + is_class_value=True, + ), + ) + if isinstance(annotation_value, TypeVar): if not is_class_value: return {annotation_name: value_set.py__class__()} @@ -371,19 +421,8 @@ def _infer_type_vars(annotation_value, value_set, is_class_value=False): annotation_generics = annotation_value.get_generics() actual_generics = element.get_generics() - for annotation_generics_set, actual_generic_set in zip(annotation_generics, actual_generics): - for nested_annotation_value in annotation_generics_set: - _merge_type_var_dicts( - type_var_dict, - _infer_type_vars( - nested_annotation_value, - actual_generic_set, - # This is a note to ourselves that we - # have already converted the instance - # representation to its class. - is_class_value=True, - ), - ) + merge_pairwise_generics(annotation_generics, actual_generics) + else: for nested_annotation_value in given[0]: _merge_type_var_dicts( @@ -431,19 +470,8 @@ def _infer_type_vars(annotation_value, value_set, is_class_value=False): ) else: actual_generics = py_class.get_generics() - for annotation_generics_set, actual_generic_set in zip(annotation_generics, actual_generics): - for nested_annotation_value in annotation_generics_set: - _merge_type_var_dicts( - type_var_dict, - _infer_type_vars( - nested_annotation_value, - actual_generic_set, - # This is a note to ourselves that we - # have already converted the instance - # representation to its class. - is_class_value=True, - ), - ) + merge_pairwise_generics(annotation_generics, actual_generics) + elif isinstance(annotation_value, GenericClass): if annotation_name == 'Iterable' and not is_class_value: given = annotation_value.get_generics() @@ -478,19 +506,7 @@ def _infer_type_vars(annotation_value, value_set, is_class_value=False): annotation_generics = annotation_value.get_generics() actual_generics = parent_class.get_generics() - for annotation_generics_set, actual_generic_set in zip(annotation_generics, actual_generics): - for nested_annotation_value in annotation_generics_set: - _merge_type_var_dicts( - type_var_dict, - _infer_type_vars( - nested_annotation_value, - actual_generic_set, - # This is a note to ourselves that we - # have already converted the instance - # representation to its class. - is_class_value=True, - ), - ) + merge_pairwise_generics(annotation_generics, actual_generics) break