Skip to content

Commit 2d526cd

Browse files
GH-103629: Update Unpack's repr in compliance with PEP 692 (#104048)
1 parent a679c3d commit 2d526cd

File tree

3 files changed

+45
-28
lines changed

3 files changed

+45
-28
lines changed

Lib/test/test_typing.py

+32-27
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,11 @@ def test_cannot_be_called(self):
880880
with self.assertRaises(TypeError):
881881
Unpack()
882882

883+
def test_usage_with_kwargs(self):
884+
Movie = TypedDict('Movie', {'name': str, 'year': int})
885+
def foo(**kwargs: Unpack[Movie]): ...
886+
self.assertEqual(repr(foo.__annotations__['kwargs']),
887+
f"typing.Unpack[{__name__}.Movie]")
883888

884889
class TypeVarTupleTests(BaseTestCase):
885890

@@ -1050,14 +1055,14 @@ class G2(Generic[Unpack[Ts]]): pass
10501055

10511056
self.assertEqual(repr(Ts), 'Ts')
10521057

1053-
self.assertEqual(repr((*Ts,)[0]), '*Ts')
1054-
self.assertEqual(repr(Unpack[Ts]), '*Ts')
1058+
self.assertEqual(repr((*Ts,)[0]), 'typing.Unpack[Ts]')
1059+
self.assertEqual(repr(Unpack[Ts]), 'typing.Unpack[Ts]')
10551060

1056-
self.assertEqual(repr(tuple[*Ts]), 'tuple[*Ts]')
1057-
self.assertEqual(repr(Tuple[Unpack[Ts]]), 'typing.Tuple[*Ts]')
1061+
self.assertEqual(repr(tuple[*Ts]), 'tuple[typing.Unpack[Ts]]')
1062+
self.assertEqual(repr(Tuple[Unpack[Ts]]), 'typing.Tuple[typing.Unpack[Ts]]')
10581063

1059-
self.assertEqual(repr(*tuple[*Ts]), '*tuple[*Ts]')
1060-
self.assertEqual(repr(Unpack[Tuple[Unpack[Ts]]]), '*typing.Tuple[*Ts]')
1064+
self.assertEqual(repr(*tuple[*Ts]), '*tuple[typing.Unpack[Ts]]')
1065+
self.assertEqual(repr(Unpack[Tuple[Unpack[Ts]]]), 'typing.Unpack[typing.Tuple[typing.Unpack[Ts]]]')
10611066

10621067
def test_variadic_class_repr_is_correct(self):
10631068
Ts = TypeVarTuple('Ts')
@@ -1074,86 +1079,86 @@ class B(Generic[Unpack[Ts]]): pass
10741079
self.assertEndsWith(repr(A[*tuple[int, ...]]),
10751080
'A[*tuple[int, ...]]')
10761081
self.assertEndsWith(repr(B[Unpack[Tuple[int, ...]]]),
1077-
'B[*typing.Tuple[int, ...]]')
1082+
'B[typing.Unpack[typing.Tuple[int, ...]]]')
10781083

10791084
self.assertEndsWith(repr(A[float, *tuple[int, ...]]),
10801085
'A[float, *tuple[int, ...]]')
10811086
self.assertEndsWith(repr(A[float, Unpack[Tuple[int, ...]]]),
1082-
'A[float, *typing.Tuple[int, ...]]')
1087+
'A[float, typing.Unpack[typing.Tuple[int, ...]]]')
10831088

10841089
self.assertEndsWith(repr(A[*tuple[int, ...], str]),
10851090
'A[*tuple[int, ...], str]')
10861091
self.assertEndsWith(repr(B[Unpack[Tuple[int, ...]], str]),
1087-
'B[*typing.Tuple[int, ...], str]')
1092+
'B[typing.Unpack[typing.Tuple[int, ...]], str]')
10881093

10891094
self.assertEndsWith(repr(A[float, *tuple[int, ...], str]),
10901095
'A[float, *tuple[int, ...], str]')
10911096
self.assertEndsWith(repr(B[float, Unpack[Tuple[int, ...]], str]),
1092-
'B[float, *typing.Tuple[int, ...], str]')
1097+
'B[float, typing.Unpack[typing.Tuple[int, ...]], str]')
10931098

10941099
def test_variadic_class_alias_repr_is_correct(self):
10951100
Ts = TypeVarTuple('Ts')
10961101
class A(Generic[Unpack[Ts]]): pass
10971102

10981103
B = A[*Ts]
1099-
self.assertEndsWith(repr(B), 'A[*Ts]')
1104+
self.assertEndsWith(repr(B), 'A[typing.Unpack[Ts]]')
11001105
self.assertEndsWith(repr(B[()]), 'A[()]')
11011106
self.assertEndsWith(repr(B[float]), 'A[float]')
11021107
self.assertEndsWith(repr(B[float, str]), 'A[float, str]')
11031108

11041109
C = A[Unpack[Ts]]
1105-
self.assertEndsWith(repr(C), 'A[*Ts]')
1110+
self.assertEndsWith(repr(C), 'A[typing.Unpack[Ts]]')
11061111
self.assertEndsWith(repr(C[()]), 'A[()]')
11071112
self.assertEndsWith(repr(C[float]), 'A[float]')
11081113
self.assertEndsWith(repr(C[float, str]), 'A[float, str]')
11091114

11101115
D = A[*Ts, int]
1111-
self.assertEndsWith(repr(D), 'A[*Ts, int]')
1116+
self.assertEndsWith(repr(D), 'A[typing.Unpack[Ts], int]')
11121117
self.assertEndsWith(repr(D[()]), 'A[int]')
11131118
self.assertEndsWith(repr(D[float]), 'A[float, int]')
11141119
self.assertEndsWith(repr(D[float, str]), 'A[float, str, int]')
11151120

11161121
E = A[Unpack[Ts], int]
1117-
self.assertEndsWith(repr(E), 'A[*Ts, int]')
1122+
self.assertEndsWith(repr(E), 'A[typing.Unpack[Ts], int]')
11181123
self.assertEndsWith(repr(E[()]), 'A[int]')
11191124
self.assertEndsWith(repr(E[float]), 'A[float, int]')
11201125
self.assertEndsWith(repr(E[float, str]), 'A[float, str, int]')
11211126

11221127
F = A[int, *Ts]
1123-
self.assertEndsWith(repr(F), 'A[int, *Ts]')
1128+
self.assertEndsWith(repr(F), 'A[int, typing.Unpack[Ts]]')
11241129
self.assertEndsWith(repr(F[()]), 'A[int]')
11251130
self.assertEndsWith(repr(F[float]), 'A[int, float]')
11261131
self.assertEndsWith(repr(F[float, str]), 'A[int, float, str]')
11271132

11281133
G = A[int, Unpack[Ts]]
1129-
self.assertEndsWith(repr(G), 'A[int, *Ts]')
1134+
self.assertEndsWith(repr(G), 'A[int, typing.Unpack[Ts]]')
11301135
self.assertEndsWith(repr(G[()]), 'A[int]')
11311136
self.assertEndsWith(repr(G[float]), 'A[int, float]')
11321137
self.assertEndsWith(repr(G[float, str]), 'A[int, float, str]')
11331138

11341139
H = A[int, *Ts, str]
1135-
self.assertEndsWith(repr(H), 'A[int, *Ts, str]')
1140+
self.assertEndsWith(repr(H), 'A[int, typing.Unpack[Ts], str]')
11361141
self.assertEndsWith(repr(H[()]), 'A[int, str]')
11371142
self.assertEndsWith(repr(H[float]), 'A[int, float, str]')
11381143
self.assertEndsWith(repr(H[float, str]), 'A[int, float, str, str]')
11391144

11401145
I = A[int, Unpack[Ts], str]
1141-
self.assertEndsWith(repr(I), 'A[int, *Ts, str]')
1146+
self.assertEndsWith(repr(I), 'A[int, typing.Unpack[Ts], str]')
11421147
self.assertEndsWith(repr(I[()]), 'A[int, str]')
11431148
self.assertEndsWith(repr(I[float]), 'A[int, float, str]')
11441149
self.assertEndsWith(repr(I[float, str]), 'A[int, float, str, str]')
11451150

11461151
J = A[*Ts, *tuple[str, ...]]
1147-
self.assertEndsWith(repr(J), 'A[*Ts, *tuple[str, ...]]')
1152+
self.assertEndsWith(repr(J), 'A[typing.Unpack[Ts], *tuple[str, ...]]')
11481153
self.assertEndsWith(repr(J[()]), 'A[*tuple[str, ...]]')
11491154
self.assertEndsWith(repr(J[float]), 'A[float, *tuple[str, ...]]')
11501155
self.assertEndsWith(repr(J[float, str]), 'A[float, str, *tuple[str, ...]]')
11511156

11521157
K = A[Unpack[Ts], Unpack[Tuple[str, ...]]]
1153-
self.assertEndsWith(repr(K), 'A[*Ts, *typing.Tuple[str, ...]]')
1154-
self.assertEndsWith(repr(K[()]), 'A[*typing.Tuple[str, ...]]')
1155-
self.assertEndsWith(repr(K[float]), 'A[float, *typing.Tuple[str, ...]]')
1156-
self.assertEndsWith(repr(K[float, str]), 'A[float, str, *typing.Tuple[str, ...]]')
1158+
self.assertEndsWith(repr(K), 'A[typing.Unpack[Ts], typing.Unpack[typing.Tuple[str, ...]]]')
1159+
self.assertEndsWith(repr(K[()]), 'A[typing.Unpack[typing.Tuple[str, ...]]]')
1160+
self.assertEndsWith(repr(K[float]), 'A[float, typing.Unpack[typing.Tuple[str, ...]]]')
1161+
self.assertEndsWith(repr(K[float, str]), 'A[float, str, typing.Unpack[typing.Tuple[str, ...]]]')
11571162

11581163
def test_cannot_subclass(self):
11591164
with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE):
@@ -1171,9 +1176,9 @@ class C(type(Unpack[Ts])): pass
11711176
with self.assertRaisesRegex(TypeError,
11721177
r'Cannot subclass typing\.Unpack'):
11731178
class C(Unpack): pass
1174-
with self.assertRaisesRegex(TypeError, r'Cannot subclass \*Ts'):
1179+
with self.assertRaisesRegex(TypeError, r'Cannot subclass typing.Unpack\[Ts\]'):
11751180
class C(*Ts): pass
1176-
with self.assertRaisesRegex(TypeError, r'Cannot subclass \*Ts'):
1181+
with self.assertRaisesRegex(TypeError, r'Cannot subclass typing.Unpack\[Ts\]'):
11771182
class C(Unpack[Ts]): pass
11781183

11791184
def test_variadic_class_args_are_correct(self):
@@ -4108,13 +4113,13 @@ class TsP(Generic[*Ts, P]):
41084113
MyCallable[[int], bool]: "MyCallable[[int], bool]",
41094114
MyCallable[[int, str], bool]: "MyCallable[[int, str], bool]",
41104115
MyCallable[[int, list[int]], bool]: "MyCallable[[int, list[int]], bool]",
4111-
MyCallable[Concatenate[*Ts, P], T]: "MyCallable[typing.Concatenate[*Ts, ~P], ~T]",
4116+
MyCallable[Concatenate[*Ts, P], T]: "MyCallable[typing.Concatenate[typing.Unpack[Ts], ~P], ~T]",
41124117

41134118
DoubleSpec[P2, P, T]: "DoubleSpec[~P2, ~P, ~T]",
41144119
DoubleSpec[[int], [str], bool]: "DoubleSpec[[int], [str], bool]",
41154120
DoubleSpec[[int, int], [str, str], bool]: "DoubleSpec[[int, int], [str, str], bool]",
41164121

4117-
TsP[*Ts, P]: "TsP[*Ts, ~P]",
4122+
TsP[*Ts, P]: "TsP[typing.Unpack[Ts], ~P]",
41184123
TsP[int, str, list[int], []]: "TsP[int, str, list[int], []]",
41194124
TsP[int, [str, list[int]]]: "TsP[int, [str, list[int]]]",
41204125

Lib/typing.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -1753,6 +1753,17 @@ class Bar(Generic[Unpack[Ts]]): ...
17531753
Foo[*tuple[int, str]]
17541754
class Bar(Generic[*Ts]): ...
17551755
1756+
The operator can also be used along with a `TypedDict` to annotate
1757+
`**kwargs` in a function signature. For instance:
1758+
1759+
class Movie(TypedDict):
1760+
name: str
1761+
year: int
1762+
1763+
# This function expects two keyword arguments - *name* of type `str` and
1764+
# *year* of type `int`.
1765+
def foo(**kwargs: Unpack[Movie]): ...
1766+
17561767
Note that there is only some runtime checking of this operator. Not
17571768
everything the runtime allows may be accepted by static type checkers.
17581769
@@ -1767,7 +1778,7 @@ class _UnpackGenericAlias(_GenericAlias, _root=True):
17671778
def __repr__(self):
17681779
# `Unpack` only takes one argument, so __args__ should contain only
17691780
# a single item.
1770-
return '*' + repr(self.__args__[0])
1781+
return f'typing.Unpack[{_type_repr(self.__args__[0])}]'
17711782

17721783
def __getitem__(self, args):
17731784
if self.__typing_is_unpacked_typevartuple__:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Update the ``repr`` of :class:`typing.Unpack` according to :pep:`692`.

0 commit comments

Comments
 (0)