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

Lines changed: 32 additions & 27 deletions
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

Lines changed: 12 additions & 1 deletion
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__:
Lines changed: 1 addition & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy