mirror of
https://github.com/python/cpython.git
synced 2025-11-24 12:20:42 +00:00
[3.14] gh-138891: fix star-unpack in get_annotations (GH-138951) (#140384)
gh-138891: fix star-unpack in get_annotations (GH-138951)
(cherry picked from commit c6be6e4537)
Co-authored-by: Christoph Walcher <christoph-wa@gmx.de>
This commit is contained in:
parent
8e93f6e203
commit
62f44dda1c
3 changed files with 21 additions and 9 deletions
|
|
@ -241,15 +241,8 @@ class ForwardRef:
|
||||||
if self.__code__ is not None:
|
if self.__code__ is not None:
|
||||||
return self.__code__
|
return self.__code__
|
||||||
arg = self.__forward_arg__
|
arg = self.__forward_arg__
|
||||||
# If we do `def f(*args: *Ts)`, then we'll have `arg = '*Ts'`.
|
|
||||||
# Unfortunately, this isn't a valid expression on its own, so we
|
|
||||||
# do the unpacking manually.
|
|
||||||
if arg.startswith("*"):
|
|
||||||
arg_to_compile = f"({arg},)[0]" # E.g. (*Ts,)[0] or (*tuple[int, int],)[0]
|
|
||||||
else:
|
|
||||||
arg_to_compile = arg
|
|
||||||
try:
|
try:
|
||||||
self.__code__ = compile(arg_to_compile, "<string>", "eval")
|
self.__code__ = compile(_rewrite_star_unpack(arg), "<string>", "eval")
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
raise SyntaxError(f"Forward reference must be an expression -- got {arg!r}")
|
raise SyntaxError(f"Forward reference must be an expression -- got {arg!r}")
|
||||||
return self.__code__
|
return self.__code__
|
||||||
|
|
@ -1025,7 +1018,8 @@ def get_annotations(
|
||||||
locals = {param.__name__: param for param in type_params} | locals
|
locals = {param.__name__: param for param in type_params} | locals
|
||||||
|
|
||||||
return_value = {
|
return_value = {
|
||||||
key: value if not isinstance(value, str) else eval(value, globals, locals)
|
key: value if not isinstance(value, str)
|
||||||
|
else eval(_rewrite_star_unpack(value), globals, locals)
|
||||||
for key, value in ann.items()
|
for key, value in ann.items()
|
||||||
}
|
}
|
||||||
return return_value
|
return return_value
|
||||||
|
|
@ -1062,6 +1056,16 @@ def annotations_to_string(annotations):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _rewrite_star_unpack(arg):
|
||||||
|
"""If the given argument annotation expression is a star unpack e.g. `'*Ts'`
|
||||||
|
rewrite it to a valid expression.
|
||||||
|
"""
|
||||||
|
if arg.startswith("*"):
|
||||||
|
return f"({arg},)[0]" # E.g. (*Ts,)[0] or (*tuple[int, int],)[0]
|
||||||
|
else:
|
||||||
|
return arg
|
||||||
|
|
||||||
|
|
||||||
def _get_and_call_annotate(obj, format):
|
def _get_and_call_annotate(obj, format):
|
||||||
"""Get the __annotate__ function and call it.
|
"""Get the __annotate__ function and call it.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -787,6 +787,12 @@ class TestGetAnnotations(unittest.TestCase):
|
||||||
self.assertEqual(get_annotations(isa2, eval_str=True), {})
|
self.assertEqual(get_annotations(isa2, eval_str=True), {})
|
||||||
self.assertEqual(get_annotations(isa2, eval_str=False), {})
|
self.assertEqual(get_annotations(isa2, eval_str=False), {})
|
||||||
|
|
||||||
|
def test_stringized_annotations_with_star_unpack(self):
|
||||||
|
def f(*args: *tuple[int, ...]): ...
|
||||||
|
self.assertEqual(get_annotations(f, eval_str=True),
|
||||||
|
{'args': (*tuple[int, ...],)[0]})
|
||||||
|
|
||||||
|
|
||||||
def test_stringized_annotations_on_wrapper(self):
|
def test_stringized_annotations_on_wrapper(self):
|
||||||
isa = inspect_stringized_annotations
|
isa = inspect_stringized_annotations
|
||||||
wrapped = times_three(isa.function)
|
wrapped = times_three(isa.function)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix ``SyntaxError`` when ``inspect.get_annotations(f, eval_str=True)`` is
|
||||||
|
called on a function annotated with a :pep:`646` ``star_expression``
|
||||||
Loading…
Add table
Add a link
Reference in a new issue