Skip to content

Commit 29ff9da

Browse files
gh-101015: Fix typing.get_type_hints with unpacked *tuple (PEP 646) (GH-101031)
(cherry picked from commit 807d6b5) Co-authored-by: Nikita Sobolev <[email protected]>
1 parent 69d12d8 commit 29ff9da

File tree

3 files changed

+36
-1
lines changed

3 files changed

+36
-1
lines changed

Lib/test/test_typing.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,6 +1222,36 @@ class D(Generic[Unpack[Ts]]): pass
12221222
self.assertIs(D[T].__origin__, D)
12231223
self.assertIs(D[Unpack[Ts]].__origin__, D)
12241224

1225+
def test_get_type_hints_on_unpack_args(self):
1226+
Ts = TypeVarTuple('Ts')
1227+
1228+
def func1(*args: *Ts): pass
1229+
self.assertEqual(gth(func1), {'args': Unpack[Ts]})
1230+
1231+
def func2(*args: *tuple[int, str]): pass
1232+
self.assertEqual(gth(func2), {'args': Unpack[tuple[int, str]]})
1233+
1234+
class CustomVariadic(Generic[*Ts]): pass
1235+
1236+
def func3(*args: *CustomVariadic[int, str]): pass
1237+
self.assertEqual(gth(func3), {'args': Unpack[CustomVariadic[int, str]]})
1238+
1239+
def test_get_type_hints_on_unpack_args_string(self):
1240+
Ts = TypeVarTuple('Ts')
1241+
1242+
def func1(*args: '*Ts'): pass
1243+
self.assertEqual(gth(func1, localns={'Ts': Ts}),
1244+
{'args': Unpack[Ts]})
1245+
1246+
def func2(*args: '*tuple[int, str]'): pass
1247+
self.assertEqual(gth(func2), {'args': Unpack[tuple[int, str]]})
1248+
1249+
class CustomVariadic(Generic[*Ts]): pass
1250+
1251+
def func3(*args: '*CustomVariadic[int, str]'): pass
1252+
self.assertEqual(gth(func3, localns={'CustomVariadic': CustomVariadic}),
1253+
{'args': Unpack[CustomVariadic[int, str]]})
1254+
12251255
def test_tuple_args_are_correct(self):
12261256
Ts = TypeVarTuple('Ts')
12271257

Lib/typing.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,10 +363,13 @@ def _eval_type(t, globalns, localns, recursive_guard=frozenset()):
363363
ForwardRef(arg) if isinstance(arg, str) else arg
364364
for arg in t.__args__
365365
)
366+
is_unpacked = t.__unpacked__
366367
if _should_unflatten_callable_args(t, args):
367368
t = t.__origin__[(args[:-1], args[-1])]
368369
else:
369370
t = t.__origin__[args]
371+
if is_unpacked:
372+
t = Unpack[t]
370373
ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__)
371374
if ev_args == t.__args__:
372375
return t
@@ -820,7 +823,7 @@ def __init__(self, arg, is_argument=True, module=None, *, is_class=False):
820823
# Unfortunately, this isn't a valid expression on its own, so we
821824
# do the unpacking manually.
822825
if arg[0] == '*':
823-
arg_to_compile = f'({arg},)[0]' # E.g. (*Ts,)[0]
826+
arg_to_compile = f'({arg},)[0]' # E.g. (*Ts,)[0] or (*tuple[int, int],)[0]
824827
else:
825828
arg_to_compile = arg
826829
try:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :func:`typing.get_type_hints` on ``'*tuple[...]'`` and ``*tuple[...]``.
2+
It must not drop the ``Unpack`` part.

0 commit comments

Comments
 (0)