Skip to content

Commit 9403ccf

Browse files
PEP 646 implementation (#963)
1 parent 46094aa commit 9403ccf

File tree

4 files changed

+448
-29
lines changed

4 files changed

+448
-29
lines changed

typing_extensions/CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Release 4.x.x
22

3+
- Runtime support for PEP 646, adding `typing_extensions.TypeVarTuple`
4+
and `typing_extensions.Unpack`.
35
- Add interaction of `Required` and `NotRequired` with `__required_keys__`,
46
`__optional_keys__` and `get_type_hints()`. Patch by David Cabot (@d-k-bo).
57
- Runtime support for PEP 675 and `typing_extensions.LiteralString`.

typing_extensions/README.rst

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ This module currently contains the following:
4848
- ``Never``
4949
- ``reveal_type``
5050
- ``Self`` (see PEP 673)
51+
- ``TypeVarTuple`` (see PEP 646)
52+
- ``Unpack`` (see PEP 646)
5153

5254
- In ``typing`` since Python 3.10
5355

@@ -124,9 +126,15 @@ These changes are _not_ backported to prevent subtle compatibility
124126
issues when mixing the differing implementations of modified classes.
125127

126128
Certain types have incorrect runtime behavior due to limitations of older
127-
versions of the typing module. For example, ``ParamSpec`` and ``Concatenate``
128-
will not work with ``get_args``, ``get_origin``. Certain PEP 612 special cases
129-
in user-defined ``Generic``\ s are also not available.
129+
versions of the typing module:
130+
131+
- ``ParamSpec`` and ``Concatenate`` will not work with ``get_args`` and
132+
``get_origin``. Certain PEP 612 special cases in user-defined
133+
``Generic``\ s are also not available.
134+
- ``Unpack`` from PEP 646 does not work properly with user-defined
135+
``Generic``\ s in Python 3.6: ``class X(Generic[Unpack[Ts]]):`` does
136+
not work.
137+
130138
These types are only guaranteed to work for static type checking.
131139

132140
Running tests

typing_extensions/src/test_typing_extensions.py

Lines changed: 146 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from typing_extensions import TypeAlias, ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs, TypeGuard
2323
from typing_extensions import Awaitable, AsyncIterator, AsyncContextManager, Required, NotRequired
2424
from typing_extensions import Protocol, runtime, runtime_checkable, Annotated, overload, final, is_typeddict
25-
from typing_extensions import dataclass_transform, reveal_type, Never, assert_never, LiteralString
25+
from typing_extensions import TypeVarTuple, Unpack, dataclass_transform, reveal_type, Never, assert_never, LiteralString
2626
try:
2727
from typing_extensions import get_type_hints
2828
except ImportError:
@@ -622,6 +622,7 @@ def test_get_origin(self):
622622

623623
T = TypeVar('T')
624624
P = ParamSpec('P')
625+
Ts = TypeVarTuple('Ts')
625626
class C(Generic[T]): pass
626627
self.assertIs(get_origin(C[int]), C)
627628
self.assertIs(get_origin(C[T]), C)
@@ -642,11 +643,16 @@ class C(Generic[T]): pass
642643
self.assertIs(get_origin(list), None)
643644
self.assertIs(get_origin(P.args), P)
644645
self.assertIs(get_origin(P.kwargs), P)
646+
self.assertIs(get_origin(Required[int]), Required)
647+
self.assertIs(get_origin(NotRequired[int]), NotRequired)
648+
self.assertIs(get_origin(Unpack[Ts]), Unpack)
649+
self.assertIs(get_origin(Unpack), None)
645650

646651
def test_get_args(self):
647652
from typing_extensions import get_args
648653

649654
T = TypeVar('T')
655+
Ts = TypeVarTuple('Ts')
650656
class C(Generic[T]): pass
651657
self.assertEqual(get_args(C[int]), (int,))
652658
self.assertEqual(get_args(C[T]), (T,))
@@ -687,6 +693,10 @@ class C(Generic[T]): pass
687693
self.assertIn(get_args(Callable[P, int]), [(P, int), ([P], int)])
688694
self.assertEqual(get_args(Callable[Concatenate[int, P], int]),
689695
(Concatenate[int, P], int))
696+
self.assertEqual(get_args(Required[int]), (int,))
697+
self.assertEqual(get_args(NotRequired[int]), (int,))
698+
self.assertEqual(get_args(Unpack[Ts]), (Ts,))
699+
self.assertEqual(get_args(Unpack), ())
690700

691701

692702
class CollectionsAbcTests(BaseTestCase):
@@ -2438,6 +2448,141 @@ def test_pickle(self):
24382448
self.assertIs(Self, pickle.loads(pickled))
24392449

24402450

2451+
class UnpackTests(BaseTestCase):
2452+
def test_basic_plain(self):
2453+
Ts = TypeVarTuple('Ts')
2454+
self.assertEqual(Unpack[Ts], Unpack[Ts])
2455+
with self.assertRaises(TypeError):
2456+
Unpack()
2457+
2458+
def test_repr(self):
2459+
Ts = TypeVarTuple('Ts')
2460+
self.assertEqual(repr(Unpack[Ts]), 'typing_extensions.Unpack[Ts]')
2461+
2462+
def test_cannot_subclass_vars(self):
2463+
with self.assertRaises(TypeError):
2464+
class V(Unpack[TypeVarTuple('Ts')]):
2465+
pass
2466+
2467+
def test_tuple(self):
2468+
Ts = TypeVarTuple('Ts')
2469+
Tuple[Unpack[Ts]]
2470+
2471+
def test_union(self):
2472+
Xs = TypeVarTuple('Xs')
2473+
Ys = TypeVarTuple('Ys')
2474+
self.assertEqual(
2475+
Union[Unpack[Xs]],
2476+
Unpack[Xs]
2477+
)
2478+
self.assertNotEqual(
2479+
Union[Unpack[Xs]],
2480+
Union[Unpack[Xs], Unpack[Ys]]
2481+
)
2482+
self.assertEqual(
2483+
Union[Unpack[Xs], Unpack[Xs]],
2484+
Unpack[Xs]
2485+
)
2486+
self.assertNotEqual(
2487+
Union[Unpack[Xs], int],
2488+
Union[Unpack[Xs]]
2489+
)
2490+
self.assertNotEqual(
2491+
Union[Unpack[Xs], int],
2492+
Union[int]
2493+
)
2494+
self.assertEqual(
2495+
Union[Unpack[Xs], int].__args__,
2496+
(Unpack[Xs], int)
2497+
)
2498+
self.assertEqual(
2499+
Union[Unpack[Xs], int].__parameters__,
2500+
(Xs,)
2501+
)
2502+
self.assertIs(
2503+
Union[Unpack[Xs], int].__origin__,
2504+
Union
2505+
)
2506+
2507+
@skipUnless(PEP_560, "Unimplemented for 3.6")
2508+
def test_concatenation(self):
2509+
Xs = TypeVarTuple('Xs')
2510+
self.assertEqual(Tuple[int, Unpack[Xs]].__args__, (int, Unpack[Xs]))
2511+
self.assertEqual(Tuple[Unpack[Xs], int].__args__, (Unpack[Xs], int))
2512+
self.assertEqual(Tuple[int, Unpack[Xs], str].__args__,
2513+
(int, Unpack[Xs], str))
2514+
class C(Generic[Unpack[Xs]]): pass
2515+
self.assertEqual(C[int, Unpack[Xs]].__args__, (int, Unpack[Xs]))
2516+
self.assertEqual(C[Unpack[Xs], int].__args__, (Unpack[Xs], int))
2517+
self.assertEqual(C[int, Unpack[Xs], str].__args__,
2518+
(int, Unpack[Xs], str))
2519+
2520+
@skipUnless(PEP_560, "Unimplemented for 3.6")
2521+
def test_class(self):
2522+
Ts = TypeVarTuple('Ts')
2523+
2524+
class C(Generic[Unpack[Ts]]): pass
2525+
self.assertEqual(C[int].__args__, (int,))
2526+
self.assertEqual(C[int, str].__args__, (int, str))
2527+
2528+
with self.assertRaises(TypeError):
2529+
class C(Generic[Unpack[Ts], int]): pass
2530+
2531+
T1 = TypeVar('T')
2532+
T2 = TypeVar('T')
2533+
class C(Generic[T1, T2, Unpack[Ts]]): pass
2534+
self.assertEqual(C[int, str].__args__, (int, str))
2535+
self.assertEqual(C[int, str, float].__args__, (int, str, float))
2536+
self.assertEqual(C[int, str, float, bool].__args__, (int, str, float, bool))
2537+
with self.assertRaises(TypeError):
2538+
C[int]
2539+
2540+
2541+
class TypeVarTupleTests(BaseTestCase):
2542+
2543+
def test_basic_plain(self):
2544+
Ts = TypeVarTuple('Ts')
2545+
self.assertEqual(Ts, Ts)
2546+
self.assertIsInstance(Ts, TypeVarTuple)
2547+
Xs = TypeVarTuple('Xs')
2548+
Ys = TypeVarTuple('Ys')
2549+
self.assertNotEqual(Xs, Ys)
2550+
2551+
def test_repr(self):
2552+
Ts = TypeVarTuple('Ts')
2553+
self.assertEqual(repr(Ts), 'Ts')
2554+
2555+
def test_no_redefinition(self):
2556+
self.assertNotEqual(TypeVarTuple('Ts'), TypeVarTuple('Ts'))
2557+
2558+
def test_cannot_subclass_vars(self):
2559+
with self.assertRaises(TypeError):
2560+
class V(TypeVarTuple('Ts')):
2561+
pass
2562+
2563+
def test_cannot_subclass_var_itself(self):
2564+
with self.assertRaises(TypeError):
2565+
class V(TypeVarTuple):
2566+
pass
2567+
2568+
def test_cannot_instantiate_vars(self):
2569+
Ts = TypeVarTuple('Ts')
2570+
with self.assertRaises(TypeError):
2571+
Ts()
2572+
2573+
def test_tuple(self):
2574+
Ts = TypeVarTuple('Ts')
2575+
# Not legal at type checking time but we can't really check against it.
2576+
Tuple[Ts]
2577+
2578+
def test_args_and_parameters(self):
2579+
Ts = TypeVarTuple('Ts')
2580+
2581+
t = Tuple[tuple(Ts)]
2582+
self.assertEqual(t.__args__, (Ts.__unpacked__,))
2583+
self.assertEqual(t.__parameters__, (Ts,))
2584+
2585+
24412586
class FinalDecoratorTests(BaseTestCase):
24422587
def test_final_unmodified(self):
24432588
def func(x): ...

0 commit comments

Comments
 (0)