mirror of
https://github.com/python/cpython.git
synced 2025-09-27 10:50:04 +00:00
GH-103629: Update Unpack's repr in compliance with PEP 692 (#104048)
This commit is contained in:
parent
a679c3d58d
commit
2d526cd32f
3 changed files with 45 additions and 28 deletions
|
@ -880,6 +880,11 @@ class UnpackTests(BaseTestCase):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
Unpack()
|
Unpack()
|
||||||
|
|
||||||
|
def test_usage_with_kwargs(self):
|
||||||
|
Movie = TypedDict('Movie', {'name': str, 'year': int})
|
||||||
|
def foo(**kwargs: Unpack[Movie]): ...
|
||||||
|
self.assertEqual(repr(foo.__annotations__['kwargs']),
|
||||||
|
f"typing.Unpack[{__name__}.Movie]")
|
||||||
|
|
||||||
class TypeVarTupleTests(BaseTestCase):
|
class TypeVarTupleTests(BaseTestCase):
|
||||||
|
|
||||||
|
@ -1050,14 +1055,14 @@ class TypeVarTupleTests(BaseTestCase):
|
||||||
|
|
||||||
self.assertEqual(repr(Ts), 'Ts')
|
self.assertEqual(repr(Ts), 'Ts')
|
||||||
|
|
||||||
self.assertEqual(repr((*Ts,)[0]), '*Ts')
|
self.assertEqual(repr((*Ts,)[0]), 'typing.Unpack[Ts]')
|
||||||
self.assertEqual(repr(Unpack[Ts]), '*Ts')
|
self.assertEqual(repr(Unpack[Ts]), 'typing.Unpack[Ts]')
|
||||||
|
|
||||||
self.assertEqual(repr(tuple[*Ts]), 'tuple[*Ts]')
|
self.assertEqual(repr(tuple[*Ts]), 'tuple[typing.Unpack[Ts]]')
|
||||||
self.assertEqual(repr(Tuple[Unpack[Ts]]), 'typing.Tuple[*Ts]')
|
self.assertEqual(repr(Tuple[Unpack[Ts]]), 'typing.Tuple[typing.Unpack[Ts]]')
|
||||||
|
|
||||||
self.assertEqual(repr(*tuple[*Ts]), '*tuple[*Ts]')
|
self.assertEqual(repr(*tuple[*Ts]), '*tuple[typing.Unpack[Ts]]')
|
||||||
self.assertEqual(repr(Unpack[Tuple[Unpack[Ts]]]), '*typing.Tuple[*Ts]')
|
self.assertEqual(repr(Unpack[Tuple[Unpack[Ts]]]), 'typing.Unpack[typing.Tuple[typing.Unpack[Ts]]]')
|
||||||
|
|
||||||
def test_variadic_class_repr_is_correct(self):
|
def test_variadic_class_repr_is_correct(self):
|
||||||
Ts = TypeVarTuple('Ts')
|
Ts = TypeVarTuple('Ts')
|
||||||
|
@ -1074,86 +1079,86 @@ class TypeVarTupleTests(BaseTestCase):
|
||||||
self.assertEndsWith(repr(A[*tuple[int, ...]]),
|
self.assertEndsWith(repr(A[*tuple[int, ...]]),
|
||||||
'A[*tuple[int, ...]]')
|
'A[*tuple[int, ...]]')
|
||||||
self.assertEndsWith(repr(B[Unpack[Tuple[int, ...]]]),
|
self.assertEndsWith(repr(B[Unpack[Tuple[int, ...]]]),
|
||||||
'B[*typing.Tuple[int, ...]]')
|
'B[typing.Unpack[typing.Tuple[int, ...]]]')
|
||||||
|
|
||||||
self.assertEndsWith(repr(A[float, *tuple[int, ...]]),
|
self.assertEndsWith(repr(A[float, *tuple[int, ...]]),
|
||||||
'A[float, *tuple[int, ...]]')
|
'A[float, *tuple[int, ...]]')
|
||||||
self.assertEndsWith(repr(A[float, Unpack[Tuple[int, ...]]]),
|
self.assertEndsWith(repr(A[float, Unpack[Tuple[int, ...]]]),
|
||||||
'A[float, *typing.Tuple[int, ...]]')
|
'A[float, typing.Unpack[typing.Tuple[int, ...]]]')
|
||||||
|
|
||||||
self.assertEndsWith(repr(A[*tuple[int, ...], str]),
|
self.assertEndsWith(repr(A[*tuple[int, ...], str]),
|
||||||
'A[*tuple[int, ...], str]')
|
'A[*tuple[int, ...], str]')
|
||||||
self.assertEndsWith(repr(B[Unpack[Tuple[int, ...]], str]),
|
self.assertEndsWith(repr(B[Unpack[Tuple[int, ...]], str]),
|
||||||
'B[*typing.Tuple[int, ...], str]')
|
'B[typing.Unpack[typing.Tuple[int, ...]], str]')
|
||||||
|
|
||||||
self.assertEndsWith(repr(A[float, *tuple[int, ...], str]),
|
self.assertEndsWith(repr(A[float, *tuple[int, ...], str]),
|
||||||
'A[float, *tuple[int, ...], str]')
|
'A[float, *tuple[int, ...], str]')
|
||||||
self.assertEndsWith(repr(B[float, Unpack[Tuple[int, ...]], str]),
|
self.assertEndsWith(repr(B[float, Unpack[Tuple[int, ...]], str]),
|
||||||
'B[float, *typing.Tuple[int, ...], str]')
|
'B[float, typing.Unpack[typing.Tuple[int, ...]], str]')
|
||||||
|
|
||||||
def test_variadic_class_alias_repr_is_correct(self):
|
def test_variadic_class_alias_repr_is_correct(self):
|
||||||
Ts = TypeVarTuple('Ts')
|
Ts = TypeVarTuple('Ts')
|
||||||
class A(Generic[Unpack[Ts]]): pass
|
class A(Generic[Unpack[Ts]]): pass
|
||||||
|
|
||||||
B = A[*Ts]
|
B = A[*Ts]
|
||||||
self.assertEndsWith(repr(B), 'A[*Ts]')
|
self.assertEndsWith(repr(B), 'A[typing.Unpack[Ts]]')
|
||||||
self.assertEndsWith(repr(B[()]), 'A[()]')
|
self.assertEndsWith(repr(B[()]), 'A[()]')
|
||||||
self.assertEndsWith(repr(B[float]), 'A[float]')
|
self.assertEndsWith(repr(B[float]), 'A[float]')
|
||||||
self.assertEndsWith(repr(B[float, str]), 'A[float, str]')
|
self.assertEndsWith(repr(B[float, str]), 'A[float, str]')
|
||||||
|
|
||||||
C = A[Unpack[Ts]]
|
C = A[Unpack[Ts]]
|
||||||
self.assertEndsWith(repr(C), 'A[*Ts]')
|
self.assertEndsWith(repr(C), 'A[typing.Unpack[Ts]]')
|
||||||
self.assertEndsWith(repr(C[()]), 'A[()]')
|
self.assertEndsWith(repr(C[()]), 'A[()]')
|
||||||
self.assertEndsWith(repr(C[float]), 'A[float]')
|
self.assertEndsWith(repr(C[float]), 'A[float]')
|
||||||
self.assertEndsWith(repr(C[float, str]), 'A[float, str]')
|
self.assertEndsWith(repr(C[float, str]), 'A[float, str]')
|
||||||
|
|
||||||
D = A[*Ts, int]
|
D = A[*Ts, int]
|
||||||
self.assertEndsWith(repr(D), 'A[*Ts, int]')
|
self.assertEndsWith(repr(D), 'A[typing.Unpack[Ts], int]')
|
||||||
self.assertEndsWith(repr(D[()]), 'A[int]')
|
self.assertEndsWith(repr(D[()]), 'A[int]')
|
||||||
self.assertEndsWith(repr(D[float]), 'A[float, int]')
|
self.assertEndsWith(repr(D[float]), 'A[float, int]')
|
||||||
self.assertEndsWith(repr(D[float, str]), 'A[float, str, int]')
|
self.assertEndsWith(repr(D[float, str]), 'A[float, str, int]')
|
||||||
|
|
||||||
E = A[Unpack[Ts], int]
|
E = A[Unpack[Ts], int]
|
||||||
self.assertEndsWith(repr(E), 'A[*Ts, int]')
|
self.assertEndsWith(repr(E), 'A[typing.Unpack[Ts], int]')
|
||||||
self.assertEndsWith(repr(E[()]), 'A[int]')
|
self.assertEndsWith(repr(E[()]), 'A[int]')
|
||||||
self.assertEndsWith(repr(E[float]), 'A[float, int]')
|
self.assertEndsWith(repr(E[float]), 'A[float, int]')
|
||||||
self.assertEndsWith(repr(E[float, str]), 'A[float, str, int]')
|
self.assertEndsWith(repr(E[float, str]), 'A[float, str, int]')
|
||||||
|
|
||||||
F = A[int, *Ts]
|
F = A[int, *Ts]
|
||||||
self.assertEndsWith(repr(F), 'A[int, *Ts]')
|
self.assertEndsWith(repr(F), 'A[int, typing.Unpack[Ts]]')
|
||||||
self.assertEndsWith(repr(F[()]), 'A[int]')
|
self.assertEndsWith(repr(F[()]), 'A[int]')
|
||||||
self.assertEndsWith(repr(F[float]), 'A[int, float]')
|
self.assertEndsWith(repr(F[float]), 'A[int, float]')
|
||||||
self.assertEndsWith(repr(F[float, str]), 'A[int, float, str]')
|
self.assertEndsWith(repr(F[float, str]), 'A[int, float, str]')
|
||||||
|
|
||||||
G = A[int, Unpack[Ts]]
|
G = A[int, Unpack[Ts]]
|
||||||
self.assertEndsWith(repr(G), 'A[int, *Ts]')
|
self.assertEndsWith(repr(G), 'A[int, typing.Unpack[Ts]]')
|
||||||
self.assertEndsWith(repr(G[()]), 'A[int]')
|
self.assertEndsWith(repr(G[()]), 'A[int]')
|
||||||
self.assertEndsWith(repr(G[float]), 'A[int, float]')
|
self.assertEndsWith(repr(G[float]), 'A[int, float]')
|
||||||
self.assertEndsWith(repr(G[float, str]), 'A[int, float, str]')
|
self.assertEndsWith(repr(G[float, str]), 'A[int, float, str]')
|
||||||
|
|
||||||
H = A[int, *Ts, str]
|
H = A[int, *Ts, str]
|
||||||
self.assertEndsWith(repr(H), 'A[int, *Ts, str]')
|
self.assertEndsWith(repr(H), 'A[int, typing.Unpack[Ts], str]')
|
||||||
self.assertEndsWith(repr(H[()]), 'A[int, str]')
|
self.assertEndsWith(repr(H[()]), 'A[int, str]')
|
||||||
self.assertEndsWith(repr(H[float]), 'A[int, float, str]')
|
self.assertEndsWith(repr(H[float]), 'A[int, float, str]')
|
||||||
self.assertEndsWith(repr(H[float, str]), 'A[int, float, str, str]')
|
self.assertEndsWith(repr(H[float, str]), 'A[int, float, str, str]')
|
||||||
|
|
||||||
I = A[int, Unpack[Ts], str]
|
I = A[int, Unpack[Ts], str]
|
||||||
self.assertEndsWith(repr(I), 'A[int, *Ts, str]')
|
self.assertEndsWith(repr(I), 'A[int, typing.Unpack[Ts], str]')
|
||||||
self.assertEndsWith(repr(I[()]), 'A[int, str]')
|
self.assertEndsWith(repr(I[()]), 'A[int, str]')
|
||||||
self.assertEndsWith(repr(I[float]), 'A[int, float, str]')
|
self.assertEndsWith(repr(I[float]), 'A[int, float, str]')
|
||||||
self.assertEndsWith(repr(I[float, str]), 'A[int, float, str, str]')
|
self.assertEndsWith(repr(I[float, str]), 'A[int, float, str, str]')
|
||||||
|
|
||||||
J = A[*Ts, *tuple[str, ...]]
|
J = A[*Ts, *tuple[str, ...]]
|
||||||
self.assertEndsWith(repr(J), 'A[*Ts, *tuple[str, ...]]')
|
self.assertEndsWith(repr(J), 'A[typing.Unpack[Ts], *tuple[str, ...]]')
|
||||||
self.assertEndsWith(repr(J[()]), 'A[*tuple[str, ...]]')
|
self.assertEndsWith(repr(J[()]), 'A[*tuple[str, ...]]')
|
||||||
self.assertEndsWith(repr(J[float]), 'A[float, *tuple[str, ...]]')
|
self.assertEndsWith(repr(J[float]), 'A[float, *tuple[str, ...]]')
|
||||||
self.assertEndsWith(repr(J[float, str]), 'A[float, str, *tuple[str, ...]]')
|
self.assertEndsWith(repr(J[float, str]), 'A[float, str, *tuple[str, ...]]')
|
||||||
|
|
||||||
K = A[Unpack[Ts], Unpack[Tuple[str, ...]]]
|
K = A[Unpack[Ts], Unpack[Tuple[str, ...]]]
|
||||||
self.assertEndsWith(repr(K), 'A[*Ts, *typing.Tuple[str, ...]]')
|
self.assertEndsWith(repr(K), 'A[typing.Unpack[Ts], typing.Unpack[typing.Tuple[str, ...]]]')
|
||||||
self.assertEndsWith(repr(K[()]), 'A[*typing.Tuple[str, ...]]')
|
self.assertEndsWith(repr(K[()]), 'A[typing.Unpack[typing.Tuple[str, ...]]]')
|
||||||
self.assertEndsWith(repr(K[float]), 'A[float, *typing.Tuple[str, ...]]')
|
self.assertEndsWith(repr(K[float]), 'A[float, typing.Unpack[typing.Tuple[str, ...]]]')
|
||||||
self.assertEndsWith(repr(K[float, str]), 'A[float, str, *typing.Tuple[str, ...]]')
|
self.assertEndsWith(repr(K[float, str]), 'A[float, str, typing.Unpack[typing.Tuple[str, ...]]]')
|
||||||
|
|
||||||
def test_cannot_subclass(self):
|
def test_cannot_subclass(self):
|
||||||
with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE):
|
with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE):
|
||||||
|
@ -1171,9 +1176,9 @@ class TypeVarTupleTests(BaseTestCase):
|
||||||
with self.assertRaisesRegex(TypeError,
|
with self.assertRaisesRegex(TypeError,
|
||||||
r'Cannot subclass typing\.Unpack'):
|
r'Cannot subclass typing\.Unpack'):
|
||||||
class C(Unpack): pass
|
class C(Unpack): pass
|
||||||
with self.assertRaisesRegex(TypeError, r'Cannot subclass \*Ts'):
|
with self.assertRaisesRegex(TypeError, r'Cannot subclass typing.Unpack\[Ts\]'):
|
||||||
class C(*Ts): pass
|
class C(*Ts): pass
|
||||||
with self.assertRaisesRegex(TypeError, r'Cannot subclass \*Ts'):
|
with self.assertRaisesRegex(TypeError, r'Cannot subclass typing.Unpack\[Ts\]'):
|
||||||
class C(Unpack[Ts]): pass
|
class C(Unpack[Ts]): pass
|
||||||
|
|
||||||
def test_variadic_class_args_are_correct(self):
|
def test_variadic_class_args_are_correct(self):
|
||||||
|
@ -4108,13 +4113,13 @@ class GenericTests(BaseTestCase):
|
||||||
MyCallable[[int], bool]: "MyCallable[[int], bool]",
|
MyCallable[[int], bool]: "MyCallable[[int], bool]",
|
||||||
MyCallable[[int, str], bool]: "MyCallable[[int, str], bool]",
|
MyCallable[[int, str], bool]: "MyCallable[[int, str], bool]",
|
||||||
MyCallable[[int, list[int]], bool]: "MyCallable[[int, list[int]], bool]",
|
MyCallable[[int, list[int]], bool]: "MyCallable[[int, list[int]], bool]",
|
||||||
MyCallable[Concatenate[*Ts, P], T]: "MyCallable[typing.Concatenate[*Ts, ~P], ~T]",
|
MyCallable[Concatenate[*Ts, P], T]: "MyCallable[typing.Concatenate[typing.Unpack[Ts], ~P], ~T]",
|
||||||
|
|
||||||
DoubleSpec[P2, P, T]: "DoubleSpec[~P2, ~P, ~T]",
|
DoubleSpec[P2, P, T]: "DoubleSpec[~P2, ~P, ~T]",
|
||||||
DoubleSpec[[int], [str], bool]: "DoubleSpec[[int], [str], bool]",
|
DoubleSpec[[int], [str], bool]: "DoubleSpec[[int], [str], bool]",
|
||||||
DoubleSpec[[int, int], [str, str], bool]: "DoubleSpec[[int, int], [str, str], bool]",
|
DoubleSpec[[int, int], [str, str], bool]: "DoubleSpec[[int, int], [str, str], bool]",
|
||||||
|
|
||||||
TsP[*Ts, P]: "TsP[*Ts, ~P]",
|
TsP[*Ts, P]: "TsP[typing.Unpack[Ts], ~P]",
|
||||||
TsP[int, str, list[int], []]: "TsP[int, str, list[int], []]",
|
TsP[int, str, list[int], []]: "TsP[int, str, list[int], []]",
|
||||||
TsP[int, [str, list[int]]]: "TsP[int, [str, list[int]]]",
|
TsP[int, [str, list[int]]]: "TsP[int, [str, list[int]]]",
|
||||||
|
|
||||||
|
|
|
@ -1753,6 +1753,17 @@ def Unpack(self, parameters):
|
||||||
Foo[*tuple[int, str]]
|
Foo[*tuple[int, str]]
|
||||||
class Bar(Generic[*Ts]): ...
|
class Bar(Generic[*Ts]): ...
|
||||||
|
|
||||||
|
The operator can also be used along with a `TypedDict` to annotate
|
||||||
|
`**kwargs` in a function signature. For instance:
|
||||||
|
|
||||||
|
class Movie(TypedDict):
|
||||||
|
name: str
|
||||||
|
year: int
|
||||||
|
|
||||||
|
# This function expects two keyword arguments - *name* of type `str` and
|
||||||
|
# *year* of type `int`.
|
||||||
|
def foo(**kwargs: Unpack[Movie]): ...
|
||||||
|
|
||||||
Note that there is only some runtime checking of this operator. Not
|
Note that there is only some runtime checking of this operator. Not
|
||||||
everything the runtime allows may be accepted by static type checkers.
|
everything the runtime allows may be accepted by static type checkers.
|
||||||
|
|
||||||
|
@ -1767,7 +1778,7 @@ class _UnpackGenericAlias(_GenericAlias, _root=True):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
# `Unpack` only takes one argument, so __args__ should contain only
|
# `Unpack` only takes one argument, so __args__ should contain only
|
||||||
# a single item.
|
# a single item.
|
||||||
return '*' + repr(self.__args__[0])
|
return f'typing.Unpack[{_type_repr(self.__args__[0])}]'
|
||||||
|
|
||||||
def __getitem__(self, args):
|
def __getitem__(self, args):
|
||||||
if self.__typing_is_unpacked_typevartuple__:
|
if self.__typing_is_unpacked_typevartuple__:
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Update the ``repr`` of :class:`typing.Unpack` according to :pep:`692`.
|
Loading…
Add table
Add a link
Reference in a new issue