Skip to content

Commit c6be6e4

Browse files
authored
gh-138891: fix star-unpack in get_annotations (#138951)
1 parent d467f24 commit c6be6e4

File tree

3 files changed

+21
-9
lines changed

3 files changed

+21
-9
lines changed

Lib/annotationlib.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -241,15 +241,8 @@ def __forward_code__(self):
241241
if self.__code__ is not None:
242242
return self.__code__
243243
arg = self.__forward_arg__
244-
# If we do `def f(*args: *Ts)`, then we'll have `arg = '*Ts'`.
245-
# Unfortunately, this isn't a valid expression on its own, so we
246-
# do the unpacking manually.
247-
if arg.startswith("*"):
248-
arg_to_compile = f"({arg},)[0]" # E.g. (*Ts,)[0] or (*tuple[int, int],)[0]
249-
else:
250-
arg_to_compile = arg
251244
try:
252-
self.__code__ = compile(arg_to_compile, "<string>", "eval")
245+
self.__code__ = compile(_rewrite_star_unpack(arg), "<string>", "eval")
253246
except SyntaxError:
254247
raise SyntaxError(f"Forward reference must be an expression -- got {arg!r}")
255248
return self.__code__
@@ -1025,7 +1018,8 @@ def get_annotations(
10251018
locals = {param.__name__: param for param in type_params} | locals
10261019

10271020
return_value = {
1028-
key: value if not isinstance(value, str) else eval(value, globals, locals)
1021+
key: value if not isinstance(value, str)
1022+
else eval(_rewrite_star_unpack(value), globals, locals)
10291023
for key, value in ann.items()
10301024
}
10311025
return return_value
@@ -1062,6 +1056,16 @@ def annotations_to_string(annotations):
10621056
}
10631057

10641058

1059+
def _rewrite_star_unpack(arg):
1060+
"""If the given argument annotation expression is a star unpack e.g. `'*Ts'`
1061+
rewrite it to a valid expression.
1062+
"""
1063+
if arg.startswith("*"):
1064+
return f"({arg},)[0]" # E.g. (*Ts,)[0] or (*tuple[int, int],)[0]
1065+
else:
1066+
return arg
1067+
1068+
10651069
def _get_and_call_annotate(obj, format):
10661070
"""Get the __annotate__ function and call it.
10671071

Lib/test/test_annotationlib.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,12 @@ def test_stringized_annotations_in_empty_module(self):
787787
self.assertEqual(get_annotations(isa2, eval_str=True), {})
788788
self.assertEqual(get_annotations(isa2, eval_str=False), {})
789789

790+
def test_stringized_annotations_with_star_unpack(self):
791+
def f(*args: *tuple[int, ...]): ...
792+
self.assertEqual(get_annotations(f, eval_str=True),
793+
{'args': (*tuple[int, ...],)[0]})
794+
795+
790796
def test_stringized_annotations_on_wrapper(self):
791797
isa = inspect_stringized_annotations
792798
wrapped = times_three(isa.function)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix ``SyntaxError`` when ``inspect.get_annotations(f, eval_str=True)`` is
2+
called on a function annotated with a :pep:`646` ``star_expression``

0 commit comments

Comments
 (0)