From 599a1c3ee1b6e21f552ec72d2d542363e2b88b17 Mon Sep 17 00:00:00 2001 From: Peter Law Date: Sun, 25 Jul 2021 15:29:30 +0100 Subject: [PATCH] Handle generics appearing within quoted return annotations This ensures that these quoted likely forwards references in return type annotations behave like their non-quoted equivalents. I suspect there may be other places which will need similar adjustments, which may mean that we should push the conversion a layer closer to the parsing (perhaps in `py__annotations__`?). One case I know that this doesn't solve (but which likely needs similar adjustment) is generics in return types of comment-style annotations. They're less likely and may not be worth supporting since all supported Python versions can use the in-syntax spelling for annotations at this point. --- jedi/inference/gradual/annotation.py | 12 ++++++++++++ test/completion/pep0484_generic_passthroughs.py | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/jedi/inference/gradual/annotation.py b/jedi/inference/gradual/annotation.py index b4caeb5e..9f53a267 100644 --- a/jedi/inference/gradual/annotation.py +++ b/jedi/inference/gradual/annotation.py @@ -222,6 +222,18 @@ def infer_return_types(function, arguments): match.group(1).strip() ).execute_annotation() + elif annotation.type == 'string': + annotation = _get_forward_reference_node( + context, + context.inference_state.compiled_subprocess.safe_literal_eval( + annotation.value, + ), + ) + # The forward reference tree has an additional root node ('eval_input') + # that we don't want. Extract the node we do want, that is equivalent to + # the nodes returned by `py__annotations__` for a non-quoted annotation. + annotation = annotation.children[0] + unknown_type_vars = find_unknown_type_vars(context, annotation) annotation_values = infer_annotation(context, annotation) if not unknown_type_vars: diff --git a/test/completion/pep0484_generic_passthroughs.py b/test/completion/pep0484_generic_passthroughs.py index 49b133a8..e250b1a4 100644 --- a/test/completion/pep0484_generic_passthroughs.py +++ b/test/completion/pep0484_generic_passthroughs.py @@ -58,6 +58,12 @@ def typed_bound_generic_passthrough(x: TList) -> TList: return x +# Forward references are more likely with custom types, however this aims to +# test just the handling of the quoted type rather than any other part of the +# machinery. +def typed_quoted_generic_passthrough(x: T) -> 'List[T]': + return [x] + for a in untyped_passthrough(untyped_list_str): #? str() @@ -146,6 +152,16 @@ for q in typed_bound_generic_passthrough(typed_list_str): q +for r in typed_quoted_generic_passthrough("something"): + #? str() + r + +for s in typed_quoted_generic_passthrough(42): + #? int() + s + + + class CustomList(List): def get_first(self): return self[0]