mirror of
https://github.com/python/cpython.git
synced 2026-05-06 12:49:07 -04:00
[3.13] gh-138891: fix star-unpack in get_annotations (GH-138951) (#146491)
(cherry picked from commit c6be6e4537)
Co-authored-by: Christoph Walcher <christoph-wa@gmx.de>
This commit is contained in:
+4
-2
@@ -160,6 +160,7 @@ import builtins
|
||||
from keyword import iskeyword
|
||||
from operator import attrgetter
|
||||
from collections import namedtuple, OrderedDict
|
||||
from typing import _rewrite_star_unpack
|
||||
from weakref import ref as make_weakref
|
||||
|
||||
# Create constants for the compiler flags in Include/code.h
|
||||
@@ -288,8 +289,9 @@ def get_annotations(obj, *, globals=None, locals=None, eval_str=False):
|
||||
if type_params := getattr(obj, "__type_params__", ()):
|
||||
locals = {param.__name__: param for param in type_params} | locals
|
||||
|
||||
return_value = {key:
|
||||
value if not isinstance(value, str) else eval(value, globals, locals)
|
||||
return_value = {
|
||||
key: value if not isinstance(value, str)
|
||||
else eval(_rewrite_star_unpack(value), globals, locals)
|
||||
for key, value in ann.items() }
|
||||
return return_value
|
||||
|
||||
|
||||
@@ -1859,6 +1859,10 @@ class TestClassesAndFunctions(unittest.TestCase):
|
||||
self.assertEqual(inspect.get_annotations(isa2, eval_str=True), {})
|
||||
self.assertEqual(inspect.get_annotations(isa2, eval_str=False), {})
|
||||
|
||||
def f(*args: *tuple[int, ...]): ...
|
||||
self.assertEqual(inspect.get_annotations(f, eval_str=True),
|
||||
{'args': (*tuple[int, ...],)[0]})
|
||||
|
||||
def times_three(fn):
|
||||
@functools.wraps(fn)
|
||||
def wrapper(a, b):
|
||||
|
||||
+11
-8
@@ -1024,15 +1024,8 @@ class ForwardRef(_Final, _root=True):
|
||||
if not isinstance(arg, str):
|
||||
raise TypeError(f"Forward reference must be a string -- got {arg!r}")
|
||||
|
||||
# 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:
|
||||
code = compile(arg_to_compile, '<string>', 'eval')
|
||||
code = compile(_rewrite_star_unpack(arg), '<string>', 'eval')
|
||||
except SyntaxError:
|
||||
raise SyntaxError(f"Forward reference must be an expression -- got {arg!r}")
|
||||
|
||||
@@ -1119,6 +1112,16 @@ class ForwardRef(_Final, _root=True):
|
||||
return f'ForwardRef({self.__forward_arg__!r}{module_repr})'
|
||||
|
||||
|
||||
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 _is_unpacked_typevartuple(x: Any) -> bool:
|
||||
return ((not isinstance(x, type)) and
|
||||
getattr(x, '__typing_is_unpacked_typevartuple__', False))
|
||||
|
||||
@@ -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``
|
||||
Reference in New Issue
Block a user