mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-07 22:44:27 +08:00
Handle generics appearing within any quoted annotations
This hoists the solution added for return-type annotations to also apply for input annotations so they work too.
This commit is contained in:
@@ -196,6 +196,32 @@ def py__annotations__(funcdef):
|
|||||||
return dct
|
return dct
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_forward_references(context, all_annotations):
|
||||||
|
def resolve(node):
|
||||||
|
if node is None or node.type != 'string':
|
||||||
|
return node
|
||||||
|
|
||||||
|
node = _get_forward_reference_node(
|
||||||
|
context,
|
||||||
|
context.inference_state.compiled_subprocess.safe_literal_eval(
|
||||||
|
node.value,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
if node is None:
|
||||||
|
# There was a string, but it's not a valid annotation
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 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 node.
|
||||||
|
node = node.children[0]
|
||||||
|
|
||||||
|
return node
|
||||||
|
|
||||||
|
return {name: resolve(node) for name, node in all_annotations.items()}
|
||||||
|
|
||||||
|
|
||||||
@inference_state_method_cache()
|
@inference_state_method_cache()
|
||||||
def infer_return_types(function, arguments):
|
def infer_return_types(function, arguments):
|
||||||
"""
|
"""
|
||||||
@@ -203,7 +229,10 @@ def infer_return_types(function, arguments):
|
|||||||
according to type annotations.
|
according to type annotations.
|
||||||
"""
|
"""
|
||||||
context = function.get_default_param_context()
|
context = function.get_default_param_context()
|
||||||
all_annotations = py__annotations__(function.tree_node)
|
all_annotations = resolve_forward_references(
|
||||||
|
context,
|
||||||
|
py__annotations__(function.tree_node),
|
||||||
|
)
|
||||||
annotation = all_annotations.get("return", None)
|
annotation = all_annotations.get("return", None)
|
||||||
if annotation is None:
|
if annotation is None:
|
||||||
# If there is no Python 3-type annotation, look for an annotation
|
# If there is no Python 3-type annotation, look for an annotation
|
||||||
@@ -222,18 +251,6 @@ def infer_return_types(function, arguments):
|
|||||||
match.group(1).strip()
|
match.group(1).strip()
|
||||||
).execute_annotation()
|
).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)
|
unknown_type_vars = find_unknown_type_vars(context, annotation)
|
||||||
annotation_values = infer_annotation(context, annotation)
|
annotation_values = infer_annotation(context, annotation)
|
||||||
if not unknown_type_vars:
|
if not unknown_type_vars:
|
||||||
|
|||||||
@@ -61,9 +61,13 @@ def typed_bound_generic_passthrough(x: TList) -> TList:
|
|||||||
# Forward references are more likely with custom types, however this aims to
|
# 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
|
# test just the handling of the quoted type rather than any other part of the
|
||||||
# machinery.
|
# machinery.
|
||||||
def typed_quoted_generic_passthrough(x: T) -> 'List[T]':
|
def typed_quoted_return_generic_passthrough(x: T) -> 'List[T]':
|
||||||
return [x]
|
return [x]
|
||||||
|
|
||||||
|
def typed_quoted_input_generic_passthrough(x: 'Tuple[T]') -> T:
|
||||||
|
x
|
||||||
|
return x[0]
|
||||||
|
|
||||||
|
|
||||||
for a in untyped_passthrough(untyped_list_str):
|
for a in untyped_passthrough(untyped_list_str):
|
||||||
#? str()
|
#? str()
|
||||||
@@ -152,15 +156,22 @@ for q in typed_bound_generic_passthrough(typed_list_str):
|
|||||||
q
|
q
|
||||||
|
|
||||||
|
|
||||||
for r in typed_quoted_generic_passthrough("something"):
|
for r in typed_quoted_return_generic_passthrough("something"):
|
||||||
#? str()
|
#? str()
|
||||||
r
|
r
|
||||||
|
|
||||||
for s in typed_quoted_generic_passthrough(42):
|
for s in typed_quoted_return_generic_passthrough(42):
|
||||||
#? int()
|
#? int()
|
||||||
s
|
s
|
||||||
|
|
||||||
|
|
||||||
|
#? str()
|
||||||
|
typed_quoted_input_generic_passthrough(("something",))
|
||||||
|
|
||||||
|
#? int()
|
||||||
|
typed_quoted_input_generic_passthrough((42,))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CustomList(List):
|
class CustomList(List):
|
||||||
def get_first(self):
|
def get_first(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user