Skip to content

Commit 40dbc09

Browse files
authored
Backport changes to the repr of typing.Unpack that were made in Python 3.12 (#163)
1 parent dfe4889 commit 40dbc09

File tree

4 files changed

+59
-29
lines changed

4 files changed

+59
-29
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@
7373
- Backport the implementation of `NewType` from 3.10 (where it is implemented
7474
as a class rather than a function). This allows user-defined `NewType`s to be
7575
pickled. Patch by Alex Waygood.
76+
- Backport changes to the repr of `typing.Unpack` that were made in order to
77+
implement [PEP 692](https://peps.python.org/pep-0692/) (backport of
78+
https://github.com/python/cpython/pull/104048). Patch by Alex Waygood.
7679

7780
# Release 4.5.0 (February 14, 2023)
7881

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ Certain objects were changed after they were added to `typing`, and
185185
- `NewType` has been in the `typing` module since Python 3.5.2, but
186186
user-defined `NewType`s are only pickleable on Python 3.10+.
187187
`typing_extensions.NewType` backports this feature to all Python versions.
188+
- `Unpack` was added in Python 3.11, but the repr was changed in Python 3.12;
189+
`typing_extensions.Unpack` has the newer repr on all versions.
188190

189191
There are a few types whose interface was modified between different
190192
versions of typing. For example, `typing.Sequence` was modified to

src/test_typing_extensions.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3644,10 +3644,7 @@ def test_basic_plain(self):
36443644

36453645
def test_repr(self):
36463646
Ts = TypeVarTuple('Ts')
3647-
if TYPING_3_11_0:
3648-
self.assertEqual(repr(Unpack[Ts]), '*Ts')
3649-
else:
3650-
self.assertEqual(repr(Unpack[Ts]), 'typing_extensions.Unpack[Ts]')
3647+
self.assertEqual(repr(Unpack[Ts]), f'{Unpack.__module__}.Unpack[Ts]')
36513648

36523649
def test_cannot_subclass_vars(self):
36533650
with self.assertRaises(TypeError):
@@ -3769,7 +3766,10 @@ def test_args_and_parameters(self):
37693766
Ts = TypeVarTuple('Ts')
37703767

37713768
t = Tuple[tuple(Ts)]
3772-
self.assertEqual(t.__args__, (Unpack[Ts],))
3769+
if sys.version_info >= (3, 11):
3770+
self.assertEqual(t.__args__, (typing.Unpack[Ts],))
3771+
else:
3772+
self.assertEqual(t.__args__, (Unpack[Ts],))
37733773
self.assertEqual(t.__parameters__, (Ts,))
37743774

37753775
def test_pickle(self):
@@ -4035,7 +4035,7 @@ def test_typing_extensions_defers_when_possible(self):
40354035
exclude |= {
40364036
'Protocol', 'runtime_checkable', 'SupportsAbs', 'SupportsBytes',
40374037
'SupportsComplex', 'SupportsFloat', 'SupportsIndex', 'SupportsInt',
4038-
'SupportsRound', 'TypedDict', 'is_typeddict', 'NamedTuple',
4038+
'SupportsRound', 'TypedDict', 'is_typeddict', 'NamedTuple', 'Unpack',
40394039
}
40404040
for item in typing_extensions.__all__:
40414041
if item not in exclude and hasattr(typing, item):

src/typing_extensions.py

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1971,14 +1971,60 @@ class Movie(TypedDict):
19711971
""")
19721972

19731973

1974-
if hasattr(typing, "Unpack"): # 3.11+
1974+
_UNPACK_DOC = """\
1975+
Type unpack operator.
1976+
1977+
The type unpack operator takes the child types from some container type,
1978+
such as `tuple[int, str]` or a `TypeVarTuple`, and 'pulls them out'. For
1979+
example:
1980+
1981+
# For some generic class `Foo`:
1982+
Foo[Unpack[tuple[int, str]]] # Equivalent to Foo[int, str]
1983+
1984+
Ts = TypeVarTuple('Ts')
1985+
# Specifies that `Bar` is generic in an arbitrary number of types.
1986+
# (Think of `Ts` as a tuple of an arbitrary number of individual
1987+
# `TypeVar`s, which the `Unpack` is 'pulling out' directly into the
1988+
# `Generic[]`.)
1989+
class Bar(Generic[Unpack[Ts]]): ...
1990+
Bar[int] # Valid
1991+
Bar[int, str] # Also valid
1992+
1993+
From Python 3.11, this can also be done using the `*` operator:
1994+
1995+
Foo[*tuple[int, str]]
1996+
class Bar(Generic[*Ts]): ...
1997+
1998+
The operator can also be used along with a `TypedDict` to annotate
1999+
`**kwargs` in a function signature. For instance:
2000+
2001+
class Movie(TypedDict):
2002+
name: str
2003+
year: int
2004+
2005+
# This function expects two keyword arguments - *name* of type `str` and
2006+
# *year* of type `int`.
2007+
def foo(**kwargs: Unpack[Movie]): ...
2008+
2009+
Note that there is only some runtime checking of this operator. Not
2010+
everything the runtime allows may be accepted by static type checkers.
2011+
2012+
For more information, see PEP 646 and PEP 692.
2013+
"""
2014+
2015+
2016+
if sys.version_info >= (3, 12): # PEP 692 changed the repr of Unpack[]
19752017
Unpack = typing.Unpack
19762018

19772019
def _is_unpack(obj):
19782020
return get_origin(obj) is Unpack
19792021

19802022
elif sys.version_info[:2] >= (3, 9):
19812023
class _UnpackSpecialForm(typing._SpecialForm, _root=True):
2024+
def __init__(self, getitem):
2025+
super().__init__(getitem)
2026+
self.__doc__ = _UNPACK_DOC
2027+
19822028
def __repr__(self):
19832029
return 'typing_extensions.' + self._name
19842030

@@ -1987,16 +2033,6 @@ class _UnpackAlias(typing._GenericAlias, _root=True):
19872033

19882034
@_UnpackSpecialForm
19892035
def Unpack(self, parameters):
1990-
"""A special typing construct to unpack a variadic type. For example:
1991-
1992-
Shape = TypeVarTuple('Shape')
1993-
Batch = NewType('Batch', int)
1994-
1995-
def add_batch_axis(
1996-
x: Array[Unpack[Shape]]
1997-
) -> Array[Batch, Unpack[Shape]]: ...
1998-
1999-
"""
20002036
item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
20012037
return _UnpackAlias(self, (item,))
20022038

@@ -2016,18 +2052,7 @@ def __getitem__(self, parameters):
20162052
f'{self._name} accepts only a single type.')
20172053
return _UnpackAlias(self, (item,))
20182054

2019-
Unpack = _UnpackForm(
2020-
'Unpack',
2021-
doc="""A special typing construct to unpack a variadic type. For example:
2022-
2023-
Shape = TypeVarTuple('Shape')
2024-
Batch = NewType('Batch', int)
2025-
2026-
def add_batch_axis(
2027-
x: Array[Unpack[Shape]]
2028-
) -> Array[Batch, Unpack[Shape]]: ...
2029-
2030-
""")
2055+
Unpack = _UnpackForm('Unpack', doc=_UNPACK_DOC)
20312056

20322057
def _is_unpack(obj):
20332058
return isinstance(obj, _UnpackAlias)

0 commit comments

Comments
 (0)