mirror of https://github.com/python/cpython
bpo-43224: Implement PEP 646 changes to typing.py (GH-31021)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
parent
13331a12c3
commit
7a793a388b
|
@ -10,7 +10,7 @@ from unittest import TestCase, main, skipUnless, skip
|
||||||
from copy import copy, deepcopy
|
from copy import copy, deepcopy
|
||||||
|
|
||||||
from typing import Any, NoReturn, Never, assert_never
|
from typing import Any, NoReturn, Never, assert_never
|
||||||
from typing import TypeVar, AnyStr
|
from typing import TypeVar, TypeVarTuple, Unpack, AnyStr
|
||||||
from typing import T, KT, VT # Not in __all__.
|
from typing import T, KT, VT # Not in __all__.
|
||||||
from typing import Union, Optional, Literal
|
from typing import Union, Optional, Literal
|
||||||
from typing import Tuple, List, Dict, MutableMapping
|
from typing import Tuple, List, Dict, MutableMapping
|
||||||
|
@ -370,6 +370,431 @@ class TypeVarTests(BaseTestCase):
|
||||||
list[T][arg]
|
list[T][arg]
|
||||||
|
|
||||||
|
|
||||||
|
class UnpackTests(BaseTestCase):
|
||||||
|
|
||||||
|
def test_accepts_single_type(self):
|
||||||
|
Unpack[Tuple[int]]
|
||||||
|
|
||||||
|
def test_rejects_multiple_types(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
Unpack[Tuple[int], Tuple[str]]
|
||||||
|
|
||||||
|
def test_rejects_multiple_parameterization(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
Unpack[Tuple[int]][Tuple[int]]
|
||||||
|
|
||||||
|
def test_cannot_be_called(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
Unpack()
|
||||||
|
|
||||||
|
|
||||||
|
class TypeVarTupleTests(BaseTestCase):
|
||||||
|
|
||||||
|
def test_instance_is_equal_to_itself(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
self.assertEqual(Ts, Ts)
|
||||||
|
|
||||||
|
def test_different_instances_are_different(self):
|
||||||
|
self.assertNotEqual(TypeVarTuple('Ts'), TypeVarTuple('Ts'))
|
||||||
|
|
||||||
|
def test_instance_isinstance_of_typevartuple(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
self.assertIsInstance(Ts, TypeVarTuple)
|
||||||
|
|
||||||
|
def test_cannot_call_instance(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
Ts()
|
||||||
|
|
||||||
|
def test_unpacked_typevartuple_is_equal_to_itself(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
self.assertEqual(Unpack[Ts], Unpack[Ts])
|
||||||
|
|
||||||
|
def test_parameterised_tuple_is_equal_to_itself(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
self.assertEqual(tuple[Unpack[Ts]], tuple[Unpack[Ts]])
|
||||||
|
self.assertEqual(Tuple[Unpack[Ts]], Tuple[Unpack[Ts]])
|
||||||
|
|
||||||
|
def tests_tuple_arg_ordering_matters(self):
|
||||||
|
Ts1 = TypeVarTuple('Ts1')
|
||||||
|
Ts2 = TypeVarTuple('Ts2')
|
||||||
|
self.assertNotEqual(
|
||||||
|
tuple[Unpack[Ts1], Unpack[Ts2]],
|
||||||
|
tuple[Unpack[Ts2], Unpack[Ts1]],
|
||||||
|
)
|
||||||
|
self.assertNotEqual(
|
||||||
|
Tuple[Unpack[Ts1], Unpack[Ts2]],
|
||||||
|
Tuple[Unpack[Ts2], Unpack[Ts1]],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_tuple_args_and_parameters_are_correct(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
t1 = tuple[Unpack[Ts]]
|
||||||
|
self.assertEqual(t1.__args__, (Unpack[Ts],))
|
||||||
|
self.assertEqual(t1.__parameters__, (Ts,))
|
||||||
|
t2 = Tuple[Unpack[Ts]]
|
||||||
|
self.assertEqual(t2.__args__, (Unpack[Ts],))
|
||||||
|
self.assertEqual(t2.__parameters__, (Ts,))
|
||||||
|
|
||||||
|
def test_repr_is_correct(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
self.assertEqual(repr(Ts), 'Ts')
|
||||||
|
self.assertEqual(repr(Unpack[Ts]), '*Ts')
|
||||||
|
self.assertEqual(repr(tuple[Unpack[Ts]]), 'tuple[*Ts]')
|
||||||
|
self.assertEqual(repr(Tuple[Unpack[Ts]]), 'typing.Tuple[*Ts]')
|
||||||
|
self.assertEqual(repr(Unpack[tuple[Unpack[Ts]]]), '*tuple[*Ts]')
|
||||||
|
self.assertEqual(repr(Unpack[Tuple[Unpack[Ts]]]), '*typing.Tuple[*Ts]')
|
||||||
|
|
||||||
|
def test_variadic_class_repr_is_correct(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
class A(Generic[Unpack[Ts]]): pass
|
||||||
|
|
||||||
|
self.assertTrue(repr(A[()]).endswith('A[()]'))
|
||||||
|
self.assertTrue(repr(A[float]).endswith('A[float]'))
|
||||||
|
self.assertTrue(repr(A[float, str]).endswith('A[float, str]'))
|
||||||
|
self.assertTrue(repr(
|
||||||
|
A[Unpack[tuple[int, ...]]]
|
||||||
|
).endswith(
|
||||||
|
'A[*tuple[int, ...]]'
|
||||||
|
))
|
||||||
|
self.assertTrue(repr(
|
||||||
|
A[float, Unpack[tuple[int, ...]]]
|
||||||
|
).endswith(
|
||||||
|
'A[float, *tuple[int, ...]]'
|
||||||
|
))
|
||||||
|
self.assertTrue(repr(
|
||||||
|
A[Unpack[tuple[int, ...]], str]
|
||||||
|
).endswith(
|
||||||
|
'A[*tuple[int, ...], str]'
|
||||||
|
))
|
||||||
|
self.assertTrue(repr(
|
||||||
|
A[float, Unpack[tuple[int, ...]], str]
|
||||||
|
).endswith(
|
||||||
|
'A[float, *tuple[int, ...], str]'
|
||||||
|
))
|
||||||
|
|
||||||
|
def test_variadic_class_alias_repr_is_correct(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
class A(Generic[Unpack[Ts]]): pass
|
||||||
|
|
||||||
|
B = A[Unpack[Ts]]
|
||||||
|
self.assertTrue(repr(B).endswith('A[*Ts]'))
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
B[()]
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
B[float]
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
B[float, str]
|
||||||
|
|
||||||
|
C = A[Unpack[Ts], int]
|
||||||
|
self.assertTrue(repr(C).endswith('A[*Ts, int]'))
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
C[()]
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
C[float]
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
C[float, str]
|
||||||
|
|
||||||
|
D = A[int, Unpack[Ts]]
|
||||||
|
self.assertTrue(repr(D).endswith('A[int, *Ts]'))
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
D[()]
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
D[float]
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
D[float, str]
|
||||||
|
|
||||||
|
E = A[int, Unpack[Ts], str]
|
||||||
|
self.assertTrue(repr(E).endswith('A[int, *Ts, str]'))
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
E[()]
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
E[float]
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
E[float, bool]
|
||||||
|
|
||||||
|
F = A[Unpack[Ts], Unpack[tuple[str, ...]]]
|
||||||
|
self.assertTrue(repr(F).endswith('A[*Ts, *tuple[str, ...]]'))
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
F[()]
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
F[float]
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
F[float, int]
|
||||||
|
|
||||||
|
def test_cannot_subclass_class(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
class C(TypeVarTuple): pass
|
||||||
|
|
||||||
|
def test_cannot_subclass_instance(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
class C(Ts): pass
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
class C(Unpack[Ts]): pass
|
||||||
|
|
||||||
|
def test_variadic_class_args_are_correct(self):
|
||||||
|
T = TypeVar('T')
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
class A(Generic[Unpack[Ts]]): pass
|
||||||
|
B = A[()]
|
||||||
|
self.assertEqual(B.__args__, ())
|
||||||
|
C = A[int]
|
||||||
|
self.assertEqual(C.__args__, (int,))
|
||||||
|
D = A[int, str]
|
||||||
|
self.assertEqual(D.__args__, (int, str))
|
||||||
|
E = A[T]
|
||||||
|
self.assertEqual(E.__args__, (T,))
|
||||||
|
F = A[Unpack[Ts]]
|
||||||
|
self.assertEqual(F.__args__, (Unpack[Ts],))
|
||||||
|
G = A[T, Unpack[Ts]]
|
||||||
|
self.assertEqual(G.__args__, (T, Unpack[Ts]))
|
||||||
|
H = A[Unpack[Ts], T]
|
||||||
|
self.assertEqual(H.__args__, (Unpack[Ts], T))
|
||||||
|
|
||||||
|
def test_variadic_class_origin_is_correct(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
class D(Generic[Unpack[Ts]]): pass
|
||||||
|
self.assertIs(D[int].__origin__, D)
|
||||||
|
self.assertIs(D[T].__origin__, D)
|
||||||
|
self.assertIs(D[Unpack[Ts]].__origin__, D)
|
||||||
|
|
||||||
|
def test_tuple_args_are_correct(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
|
||||||
|
self.assertEqual(tuple[Unpack[Ts]].__args__, (Unpack[Ts],))
|
||||||
|
self.assertEqual(Tuple[Unpack[Ts]].__args__, (Unpack[Ts],))
|
||||||
|
|
||||||
|
self.assertEqual(tuple[Unpack[Ts], int].__args__, (Unpack[Ts], int))
|
||||||
|
self.assertEqual(Tuple[Unpack[Ts], int].__args__, (Unpack[Ts], int))
|
||||||
|
|
||||||
|
self.assertEqual(tuple[int, Unpack[Ts]].__args__, (int, Unpack[Ts]))
|
||||||
|
self.assertEqual(Tuple[int, Unpack[Ts]].__args__, (int, Unpack[Ts]))
|
||||||
|
|
||||||
|
self.assertEqual(tuple[int, Unpack[Ts], str].__args__,
|
||||||
|
(int, Unpack[Ts], str))
|
||||||
|
self.assertEqual(Tuple[int, Unpack[Ts], str].__args__,
|
||||||
|
(int, Unpack[Ts], str))
|
||||||
|
|
||||||
|
self.assertEqual(tuple[Unpack[Ts], int].__args__, (Unpack[Ts], int))
|
||||||
|
self.assertEqual(Tuple[Unpack[Ts]].__args__, (Unpack[Ts],))
|
||||||
|
|
||||||
|
def test_callable_args_are_correct(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
Ts1 = TypeVarTuple('Ts1')
|
||||||
|
Ts2 = TypeVarTuple('Ts2')
|
||||||
|
|
||||||
|
# TypeVarTuple in the arguments
|
||||||
|
|
||||||
|
a = Callable[[Unpack[Ts]], None]
|
||||||
|
self.assertEqual(a.__args__, (Unpack[Ts], type(None)))
|
||||||
|
|
||||||
|
b = Callable[[int, Unpack[Ts]], None]
|
||||||
|
self.assertEqual(b.__args__, (int, Unpack[Ts], type(None)))
|
||||||
|
|
||||||
|
c = Callable[[Unpack[Ts], int], None]
|
||||||
|
self.assertEqual(c.__args__, (Unpack[Ts], int, type(None)))
|
||||||
|
|
||||||
|
d = Callable[[str, Unpack[Ts], int], None]
|
||||||
|
self.assertEqual(d.__args__, (str, Unpack[Ts], int, type(None)))
|
||||||
|
|
||||||
|
# TypeVarTuple as the return
|
||||||
|
|
||||||
|
e = Callable[[None], Unpack[Ts]]
|
||||||
|
self.assertEqual(e.__args__, (type(None), Unpack[Ts]))
|
||||||
|
|
||||||
|
f = Callable[[None], tuple[int, Unpack[Ts]]]
|
||||||
|
self.assertEqual(f.__args__, (type(None), tuple[int, Unpack[Ts]]))
|
||||||
|
|
||||||
|
g = Callable[[None], tuple[Unpack[Ts], int]]
|
||||||
|
self.assertEqual(g.__args__, (type(None), tuple[Unpack[Ts], int]))
|
||||||
|
|
||||||
|
h = Callable[[None], tuple[str, Unpack[Ts], int]]
|
||||||
|
self.assertEqual(h.__args__, (type(None), tuple[str, Unpack[Ts], int]))
|
||||||
|
|
||||||
|
# TypeVarTuple in both
|
||||||
|
|
||||||
|
i = Callable[[Unpack[Ts]], Unpack[Ts]]
|
||||||
|
self.assertEqual(i.__args__, (Unpack[Ts], Unpack[Ts]))
|
||||||
|
|
||||||
|
j = Callable[[Unpack[Ts1]], Unpack[Ts2]]
|
||||||
|
self.assertEqual(j.__args__, (Unpack[Ts1], Unpack[Ts2]))
|
||||||
|
|
||||||
|
def test_variadic_class_with_duplicate_typevartuples_fails(self):
|
||||||
|
Ts1 = TypeVarTuple('Ts1')
|
||||||
|
Ts2 = TypeVarTuple('Ts2')
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
class C(Generic[Unpack[Ts1], Unpack[Ts1]]): pass
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
class C(Generic[Unpack[Ts1], Unpack[Ts2], Unpack[Ts1]]): pass
|
||||||
|
|
||||||
|
def test_type_concatenation_in_variadic_class_argument_list_succeeds(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
class C(Generic[Unpack[Ts]]): pass
|
||||||
|
C[int, Unpack[Ts]]
|
||||||
|
C[Unpack[Ts], int]
|
||||||
|
C[int, Unpack[Ts], str]
|
||||||
|
C[int, bool, Unpack[Ts], float, str]
|
||||||
|
|
||||||
|
def test_type_concatenation_in_tuple_argument_list_succeeds(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
|
||||||
|
tuple[int, Unpack[Ts]]
|
||||||
|
tuple[Unpack[Ts], int]
|
||||||
|
tuple[int, Unpack[Ts], str]
|
||||||
|
tuple[int, bool, Unpack[Ts], float, str]
|
||||||
|
|
||||||
|
Tuple[int, Unpack[Ts]]
|
||||||
|
Tuple[Unpack[Ts], int]
|
||||||
|
Tuple[int, Unpack[Ts], str]
|
||||||
|
Tuple[int, bool, Unpack[Ts], float, str]
|
||||||
|
|
||||||
|
def test_variadic_class_definition_using_packed_typevartuple_fails(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
class C(Generic[Ts]): pass
|
||||||
|
|
||||||
|
def test_variadic_class_definition_using_concrete_types_fails(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
class E(Generic[Unpack[Ts], int]): pass
|
||||||
|
|
||||||
|
def test_variadic_class_with_2_typevars_accepts_2_or_more_args(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
T1 = TypeVar('T1')
|
||||||
|
T2 = TypeVar('T2')
|
||||||
|
|
||||||
|
class A(Generic[T1, T2, Unpack[Ts]]): pass
|
||||||
|
A[int, str]
|
||||||
|
A[int, str, float]
|
||||||
|
A[int, str, float, bool]
|
||||||
|
|
||||||
|
class B(Generic[T1, Unpack[Ts], T2]): pass
|
||||||
|
B[int, str]
|
||||||
|
B[int, str, float]
|
||||||
|
B[int, str, float, bool]
|
||||||
|
|
||||||
|
class C(Generic[Unpack[Ts], T1, T2]): pass
|
||||||
|
C[int, str]
|
||||||
|
C[int, str, float]
|
||||||
|
C[int, str, float, bool]
|
||||||
|
|
||||||
|
def test_variadic_args_annotations_are_correct(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
def f(*args: Unpack[Ts]): pass
|
||||||
|
self.assertEqual(f.__annotations__, {'args': Unpack[Ts]})
|
||||||
|
|
||||||
|
def test_variadic_args_with_ellipsis_annotations_are_correct(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
|
||||||
|
def a(*args: Unpack[tuple[int, ...]]): pass
|
||||||
|
self.assertEqual(a.__annotations__,
|
||||||
|
{'args': Unpack[tuple[int, ...]]})
|
||||||
|
|
||||||
|
def b(*args: Unpack[Tuple[int, ...]]): pass
|
||||||
|
self.assertEqual(b.__annotations__,
|
||||||
|
{'args': Unpack[Tuple[int, ...]]})
|
||||||
|
|
||||||
|
def test_concatenation_in_variadic_args_annotations_are_correct(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
|
||||||
|
# Unpacking using `Unpack`, native `tuple` type
|
||||||
|
|
||||||
|
def a(*args: Unpack[tuple[int, Unpack[Ts]]]): pass
|
||||||
|
self.assertEqual(
|
||||||
|
a.__annotations__,
|
||||||
|
{'args': Unpack[tuple[int, Unpack[Ts]]]},
|
||||||
|
)
|
||||||
|
|
||||||
|
def b(*args: Unpack[tuple[Unpack[Ts], int]]): pass
|
||||||
|
self.assertEqual(
|
||||||
|
b.__annotations__,
|
||||||
|
{'args': Unpack[tuple[Unpack[Ts], int]]},
|
||||||
|
)
|
||||||
|
|
||||||
|
def c(*args: Unpack[tuple[str, Unpack[Ts], int]]): pass
|
||||||
|
self.assertEqual(
|
||||||
|
c.__annotations__,
|
||||||
|
{'args': Unpack[tuple[str, Unpack[Ts], int]]},
|
||||||
|
)
|
||||||
|
|
||||||
|
def d(*args: Unpack[tuple[int, bool, Unpack[Ts], float, str]]): pass
|
||||||
|
self.assertEqual(
|
||||||
|
d.__annotations__,
|
||||||
|
{'args': Unpack[tuple[int, bool, Unpack[Ts], float, str]]},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Unpacking using `Unpack`, `Tuple` type from typing.py
|
||||||
|
|
||||||
|
def e(*args: Unpack[Tuple[int, Unpack[Ts]]]): pass
|
||||||
|
self.assertEqual(
|
||||||
|
e.__annotations__,
|
||||||
|
{'args': Unpack[Tuple[int, Unpack[Ts]]]},
|
||||||
|
)
|
||||||
|
|
||||||
|
def f(*args: Unpack[Tuple[Unpack[Ts], int]]): pass
|
||||||
|
self.assertEqual(
|
||||||
|
f.__annotations__,
|
||||||
|
{'args': Unpack[Tuple[Unpack[Ts], int]]},
|
||||||
|
)
|
||||||
|
|
||||||
|
def g(*args: Unpack[Tuple[str, Unpack[Ts], int]]): pass
|
||||||
|
self.assertEqual(
|
||||||
|
g.__annotations__,
|
||||||
|
{'args': Unpack[Tuple[str, Unpack[Ts], int]]},
|
||||||
|
)
|
||||||
|
|
||||||
|
def h(*args: Unpack[Tuple[int, bool, Unpack[Ts], float, str]]): pass
|
||||||
|
self.assertEqual(
|
||||||
|
h.__annotations__,
|
||||||
|
{'args': Unpack[Tuple[int, bool, Unpack[Ts], float, str]]},
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_variadic_class_same_args_results_in_equalty(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
class C(Generic[Unpack[Ts]]): pass
|
||||||
|
|
||||||
|
self.assertEqual(C[int], C[int])
|
||||||
|
|
||||||
|
Ts1 = TypeVarTuple('Ts1')
|
||||||
|
Ts2 = TypeVarTuple('Ts2')
|
||||||
|
self.assertEqual(
|
||||||
|
C[Unpack[Ts1]],
|
||||||
|
C[Unpack[Ts1]],
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
C[Unpack[Ts1], Unpack[Ts2]],
|
||||||
|
C[Unpack[Ts1], Unpack[Ts2]],
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
C[int, Unpack[Ts1], Unpack[Ts2]],
|
||||||
|
C[int, Unpack[Ts1], Unpack[Ts2]],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_variadic_class_arg_ordering_matters(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
class C(Generic[Unpack[Ts]]): pass
|
||||||
|
|
||||||
|
self.assertNotEqual(
|
||||||
|
C[int, str],
|
||||||
|
C[str, int],
|
||||||
|
)
|
||||||
|
|
||||||
|
Ts1 = TypeVarTuple('Ts1')
|
||||||
|
Ts2 = TypeVarTuple('Ts2')
|
||||||
|
self.assertNotEqual(
|
||||||
|
C[Unpack[Ts1], Unpack[Ts2]],
|
||||||
|
C[Unpack[Ts2], Unpack[Ts1]],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_variadic_class_arg_typevartuple_identity_matters(self):
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
class C(Generic[Unpack[Ts]]): pass
|
||||||
|
Ts1 = TypeVarTuple('Ts1')
|
||||||
|
Ts2 = TypeVarTuple('Ts2')
|
||||||
|
self.assertNotEqual(C[Unpack[Ts1]], C[Unpack[Ts2]])
|
||||||
|
|
||||||
|
|
||||||
class UnionTests(BaseTestCase):
|
class UnionTests(BaseTestCase):
|
||||||
|
|
||||||
def test_basics(self):
|
def test_basics(self):
|
||||||
|
@ -1819,6 +2244,11 @@ class GenericTests(BaseTestCase):
|
||||||
class MyGeneric(Generic[T], Generic[S]): ...
|
class MyGeneric(Generic[T], Generic[S]): ...
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
class MyGeneric(List[T], Generic[S]): ...
|
class MyGeneric(List[T], Generic[S]): ...
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
Generic[()]
|
||||||
|
class C(Generic[T]): pass
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
C[()]
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
|
|
235
Lib/typing.py
235
Lib/typing.py
|
@ -5,7 +5,7 @@ At large scale, the structure of the module is following:
|
||||||
* Imports and exports, all public names should be explicitly added to __all__.
|
* Imports and exports, all public names should be explicitly added to __all__.
|
||||||
* Internal helper functions: these should never be used in code outside this module.
|
* Internal helper functions: these should never be used in code outside this module.
|
||||||
* _SpecialForm and its instances (special forms):
|
* _SpecialForm and its instances (special forms):
|
||||||
Any, NoReturn, Never, ClassVar, Union, Optional, Concatenate
|
Any, NoReturn, Never, ClassVar, Union, Optional, Concatenate, Unpack
|
||||||
* Classes whose instances can be type arguments in addition to types:
|
* Classes whose instances can be type arguments in addition to types:
|
||||||
ForwardRef, TypeVar and ParamSpec
|
ForwardRef, TypeVar and ParamSpec
|
||||||
* The core of internal generics API: _GenericAlias and _VariadicGenericAlias, the latter is
|
* The core of internal generics API: _GenericAlias and _VariadicGenericAlias, the latter is
|
||||||
|
@ -56,6 +56,7 @@ __all__ = [
|
||||||
'Tuple',
|
'Tuple',
|
||||||
'Type',
|
'Type',
|
||||||
'TypeVar',
|
'TypeVar',
|
||||||
|
'TypeVarTuple',
|
||||||
'Union',
|
'Union',
|
||||||
|
|
||||||
# ABCs (from collections.abc).
|
# ABCs (from collections.abc).
|
||||||
|
@ -139,6 +140,7 @@ __all__ = [
|
||||||
'TYPE_CHECKING',
|
'TYPE_CHECKING',
|
||||||
'TypeAlias',
|
'TypeAlias',
|
||||||
'TypeGuard',
|
'TypeGuard',
|
||||||
|
'Unpack',
|
||||||
]
|
]
|
||||||
|
|
||||||
# The pseudo-submodules 're' and 'io' are part of the public
|
# The pseudo-submodules 're' and 'io' are part of the public
|
||||||
|
@ -182,7 +184,7 @@ def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms=
|
||||||
if isinstance(arg, _SpecialForm) or arg in (Generic, Protocol):
|
if isinstance(arg, _SpecialForm) or arg in (Generic, Protocol):
|
||||||
raise TypeError(f"Plain {arg} is not valid as type argument")
|
raise TypeError(f"Plain {arg} is not valid as type argument")
|
||||||
if isinstance(arg, (type, TypeVar, ForwardRef, types.UnionType, ParamSpec,
|
if isinstance(arg, (type, TypeVar, ForwardRef, types.UnionType, ParamSpec,
|
||||||
ParamSpecArgs, ParamSpecKwargs)):
|
ParamSpecArgs, ParamSpecKwargs, TypeVarTuple)):
|
||||||
return arg
|
return arg
|
||||||
if not callable(arg):
|
if not callable(arg):
|
||||||
raise TypeError(f"{msg} Got {arg!r:.100}.")
|
raise TypeError(f"{msg} Got {arg!r:.100}.")
|
||||||
|
@ -793,8 +795,28 @@ class ForwardRef(_Final, _root=True):
|
||||||
module_repr = f', module={self.__forward_module__!r}'
|
module_repr = f', module={self.__forward_module__!r}'
|
||||||
return f'ForwardRef({self.__forward_arg__!r}{module_repr})'
|
return f'ForwardRef({self.__forward_arg__!r}{module_repr})'
|
||||||
|
|
||||||
class _TypeVarLike:
|
|
||||||
"""Mixin for TypeVar-like types (TypeVar and ParamSpec)."""
|
def _is_unpacked_typevartuple(x: Any) -> bool:
|
||||||
|
return (
|
||||||
|
isinstance(x, _UnpackGenericAlias)
|
||||||
|
# If x is Unpack[tuple[...]], __parameters__ will be empty.
|
||||||
|
and x.__parameters__
|
||||||
|
and isinstance(x.__parameters__[0], TypeVarTuple)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_typevar_like(x: Any) -> bool:
|
||||||
|
return isinstance(x, (TypeVar, ParamSpec)) or _is_unpacked_typevartuple(x)
|
||||||
|
|
||||||
|
|
||||||
|
class _BoundVarianceMixin:
|
||||||
|
"""Mixin giving __init__ bound and variance arguments.
|
||||||
|
|
||||||
|
This is used by TypeVar and ParamSpec, which both employ the notions of
|
||||||
|
a type 'bound' (restricting type arguments to be a subtype of some
|
||||||
|
specified type) and type 'variance' (determining subtype relations between
|
||||||
|
generic types).
|
||||||
|
"""
|
||||||
def __init__(self, bound, covariant, contravariant):
|
def __init__(self, bound, covariant, contravariant):
|
||||||
"""Used to setup TypeVars and ParamSpec's bound, covariant and
|
"""Used to setup TypeVars and ParamSpec's bound, covariant and
|
||||||
contravariant attributes.
|
contravariant attributes.
|
||||||
|
@ -827,7 +849,7 @@ class _TypeVarLike:
|
||||||
return self.__name__
|
return self.__name__
|
||||||
|
|
||||||
|
|
||||||
class TypeVar( _Final, _Immutable, _TypeVarLike, _root=True):
|
class TypeVar(_Final, _Immutable, _BoundVarianceMixin, _root=True):
|
||||||
"""Type variable.
|
"""Type variable.
|
||||||
|
|
||||||
Usage::
|
Usage::
|
||||||
|
@ -886,6 +908,39 @@ class TypeVar( _Final, _Immutable, _TypeVarLike, _root=True):
|
||||||
self.__module__ = def_mod
|
self.__module__ = def_mod
|
||||||
|
|
||||||
|
|
||||||
|
class TypeVarTuple(_Final, _Immutable, _root=True):
|
||||||
|
"""Type variable tuple.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
Ts = TypeVarTuple('Ts') # Can be given any name
|
||||||
|
|
||||||
|
Just as a TypeVar (type variable) is a placeholder for a single type,
|
||||||
|
a TypeVarTuple is a placeholder for an *arbitrary* number of types. For
|
||||||
|
example, if we define a generic class using a TypeVarTuple:
|
||||||
|
|
||||||
|
class C(Generic[*Ts]): ...
|
||||||
|
|
||||||
|
Then we can parameterize that class with an arbitrary number of type
|
||||||
|
arguments:
|
||||||
|
|
||||||
|
C[int] # Fine
|
||||||
|
C[int, str] # Also fine
|
||||||
|
C[()] # Even this is fine
|
||||||
|
|
||||||
|
For more details, see PEP 646.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
self._name = name
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
yield Unpack[self]
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
|
||||||
class ParamSpecArgs(_Final, _Immutable, _root=True):
|
class ParamSpecArgs(_Final, _Immutable, _root=True):
|
||||||
"""The args for a ParamSpec object.
|
"""The args for a ParamSpec object.
|
||||||
|
|
||||||
|
@ -934,7 +989,7 @@ class ParamSpecKwargs(_Final, _Immutable, _root=True):
|
||||||
return self.__origin__ == other.__origin__
|
return self.__origin__ == other.__origin__
|
||||||
|
|
||||||
|
|
||||||
class ParamSpec(_Final, _Immutable, _TypeVarLike, _root=True):
|
class ParamSpec(_Final, _Immutable, _BoundVarianceMixin, _root=True):
|
||||||
"""Parameter specification variable.
|
"""Parameter specification variable.
|
||||||
|
|
||||||
Usage::
|
Usage::
|
||||||
|
@ -1065,6 +1120,45 @@ class _BaseGenericAlias(_Final, _root=True):
|
||||||
return list(set(super().__dir__()
|
return list(set(super().__dir__()
|
||||||
+ [attr for attr in dir(self.__origin__) if not _is_dunder(attr)]))
|
+ [attr for attr in dir(self.__origin__) if not _is_dunder(attr)]))
|
||||||
|
|
||||||
|
|
||||||
|
def _is_unpacked_tuple(x: Any) -> bool:
|
||||||
|
# Is `x` something like `*tuple[int]` or `*tuple[int, ...]`?
|
||||||
|
if not isinstance(x, _UnpackGenericAlias):
|
||||||
|
return False
|
||||||
|
# Alright, `x` is `Unpack[something]`.
|
||||||
|
|
||||||
|
# `x` will always have `__args__`, because Unpack[] and Unpack[()]
|
||||||
|
# aren't legal.
|
||||||
|
unpacked_type = x.__args__[0]
|
||||||
|
|
||||||
|
return getattr(unpacked_type, '__origin__', None) is tuple
|
||||||
|
|
||||||
|
|
||||||
|
def _is_unpacked_arbitrary_length_tuple(x: Any) -> bool:
|
||||||
|
if not _is_unpacked_tuple(x):
|
||||||
|
return False
|
||||||
|
unpacked_tuple = x.__args__[0]
|
||||||
|
|
||||||
|
if not hasattr(unpacked_tuple, '__args__'):
|
||||||
|
# It's `Unpack[tuple]`. We can't make any assumptions about the length
|
||||||
|
# of the tuple, so it's effectively an arbitrary-length tuple.
|
||||||
|
return True
|
||||||
|
|
||||||
|
tuple_args = unpacked_tuple.__args__
|
||||||
|
if not tuple_args:
|
||||||
|
# It's `Unpack[tuple[()]]`.
|
||||||
|
return False
|
||||||
|
|
||||||
|
last_arg = tuple_args[-1]
|
||||||
|
if last_arg is Ellipsis:
|
||||||
|
# It's `Unpack[tuple[something, ...]]`, which is arbitrary-length.
|
||||||
|
return True
|
||||||
|
|
||||||
|
# If the arguments didn't end with an ellipsis, then it's not an
|
||||||
|
# arbitrary-length tuple.
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
# Special typing constructs Union, Optional, Generic, Callable and Tuple
|
# Special typing constructs Union, Optional, Generic, Callable and Tuple
|
||||||
# use three special attributes for internal bookkeeping of generic types:
|
# use three special attributes for internal bookkeeping of generic types:
|
||||||
# * __parameters__ is a tuple of unique free type parameters of a generic
|
# * __parameters__ is a tuple of unique free type parameters of a generic
|
||||||
|
@ -1103,7 +1197,7 @@ class _GenericAlias(_BaseGenericAlias, _root=True):
|
||||||
# TypeVar[bool]
|
# TypeVar[bool]
|
||||||
|
|
||||||
def __init__(self, origin, args, *, inst=True, name=None,
|
def __init__(self, origin, args, *, inst=True, name=None,
|
||||||
_typevar_types=TypeVar,
|
_typevar_types=(TypeVar, TypeVarTuple),
|
||||||
_paramspec_tvars=False):
|
_paramspec_tvars=False):
|
||||||
super().__init__(origin, inst=inst, name=name)
|
super().__init__(origin, inst=inst, name=name)
|
||||||
if not isinstance(args, tuple):
|
if not isinstance(args, tuple):
|
||||||
|
@ -1160,7 +1254,10 @@ class _GenericAlias(_BaseGenericAlias, _root=True):
|
||||||
if (self._paramspec_tvars
|
if (self._paramspec_tvars
|
||||||
and any(isinstance(t, ParamSpec) for t in self.__parameters__)):
|
and any(isinstance(t, ParamSpec) for t in self.__parameters__)):
|
||||||
args = _prepare_paramspec_params(self, args)
|
args = _prepare_paramspec_params(self, args)
|
||||||
else:
|
elif not any(isinstance(p, TypeVarTuple) for p in self.__parameters__):
|
||||||
|
# We only run this if there are no TypeVarTuples, because we
|
||||||
|
# don't check variadic generic arity at runtime (to reduce
|
||||||
|
# complexity of typing.py).
|
||||||
_check_generic(self, args, len(self.__parameters__))
|
_check_generic(self, args, len(self.__parameters__))
|
||||||
|
|
||||||
new_args = self._determine_new_args(args)
|
new_args = self._determine_new_args(args)
|
||||||
|
@ -1182,6 +1279,10 @@ class _GenericAlias(_BaseGenericAlias, _root=True):
|
||||||
# anything more exotic than a plain `TypeVar`, we need to consider
|
# anything more exotic than a plain `TypeVar`, we need to consider
|
||||||
# edge cases.
|
# edge cases.
|
||||||
|
|
||||||
|
if any(isinstance(p, TypeVarTuple) for p in self.__parameters__):
|
||||||
|
raise NotImplementedError(
|
||||||
|
"Type substitution for TypeVarTuples is not yet implemented"
|
||||||
|
)
|
||||||
# In the example above, this would be {T3: str}
|
# In the example above, this would be {T3: str}
|
||||||
new_arg_by_param = dict(zip(self.__parameters__, args))
|
new_arg_by_param = dict(zip(self.__parameters__, args))
|
||||||
|
|
||||||
|
@ -1195,6 +1296,10 @@ class _GenericAlias(_BaseGenericAlias, _root=True):
|
||||||
f"ParamSpec, or Concatenate. Got {new_arg}")
|
f"ParamSpec, or Concatenate. Got {new_arg}")
|
||||||
elif isinstance(old_arg, self._typevar_types):
|
elif isinstance(old_arg, self._typevar_types):
|
||||||
new_arg = new_arg_by_param[old_arg]
|
new_arg = new_arg_by_param[old_arg]
|
||||||
|
elif (TypeVarTuple in self._typevar_types
|
||||||
|
and _is_unpacked_typevartuple(old_arg)):
|
||||||
|
original_typevartuple = old_arg.__parameters__[0]
|
||||||
|
new_arg = new_arg_by_param[original_typevartuple]
|
||||||
elif isinstance(old_arg, (_GenericAlias, GenericAlias, types.UnionType)):
|
elif isinstance(old_arg, (_GenericAlias, GenericAlias, types.UnionType)):
|
||||||
subparams = old_arg.__parameters__
|
subparams = old_arg.__parameters__
|
||||||
if not subparams:
|
if not subparams:
|
||||||
|
@ -1217,6 +1322,17 @@ class _GenericAlias(_BaseGenericAlias, _root=True):
|
||||||
# ...we need to be careful; `new_args` should end up as
|
# ...we need to be careful; `new_args` should end up as
|
||||||
# `(int, str, float)` rather than `([int, str], float)`.
|
# `(int, str, float)` rather than `([int, str], float)`.
|
||||||
new_args.extend(new_arg)
|
new_args.extend(new_arg)
|
||||||
|
elif _is_unpacked_typevartuple(old_arg):
|
||||||
|
# Consider the following `_GenericAlias`, `B`:
|
||||||
|
# class A(Generic[*Ts]): ...
|
||||||
|
# B = A[T, *Ts]
|
||||||
|
# If we then do:
|
||||||
|
# B[float, int, str]
|
||||||
|
# The `new_arg` corresponding to `T` will be `float`, and the
|
||||||
|
# `new_arg` corresponding to `*Ts` will be `(int, str)`. We
|
||||||
|
# should join all these types together in a flat list
|
||||||
|
# `(float, int, str)` - so again, we should `extend`.
|
||||||
|
new_args.extend(new_arg)
|
||||||
else:
|
else:
|
||||||
new_args.append(new_arg)
|
new_args.append(new_arg)
|
||||||
|
|
||||||
|
@ -1230,7 +1346,11 @@ class _GenericAlias(_BaseGenericAlias, _root=True):
|
||||||
name = 'typing.' + self._name
|
name = 'typing.' + self._name
|
||||||
else:
|
else:
|
||||||
name = _type_repr(self.__origin__)
|
name = _type_repr(self.__origin__)
|
||||||
args = ", ".join([_type_repr(a) for a in self.__args__])
|
if self.__args__:
|
||||||
|
args = ", ".join([_type_repr(a) for a in self.__args__])
|
||||||
|
else:
|
||||||
|
# To ensure the repr is eval-able.
|
||||||
|
args = "()"
|
||||||
return f'{name}[{args}]'
|
return f'{name}[{args}]'
|
||||||
|
|
||||||
def __reduce__(self):
|
def __reduce__(self):
|
||||||
|
@ -1258,6 +1378,9 @@ class _GenericAlias(_BaseGenericAlias, _root=True):
|
||||||
return ()
|
return ()
|
||||||
return (self.__origin__,)
|
return (self.__origin__,)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
yield Unpack[self]
|
||||||
|
|
||||||
|
|
||||||
# _nparams is the number of accepted parameters, e.g. 0 for Hashable,
|
# _nparams is the number of accepted parameters, e.g. 0 for Hashable,
|
||||||
# 1 for List and 2 for Dict. It may be -1 if variable number of
|
# 1 for List and 2 for Dict. It may be -1 if variable number of
|
||||||
|
@ -1365,10 +1488,10 @@ class _TupleType(_SpecialGenericAlias, _root=True):
|
||||||
return self.copy_with((_TypingEmpty,))
|
return self.copy_with((_TypingEmpty,))
|
||||||
if not isinstance(params, tuple):
|
if not isinstance(params, tuple):
|
||||||
params = (params,)
|
params = (params,)
|
||||||
if len(params) == 2 and params[1] is ...:
|
if len(params) >= 2 and params[-1] is ...:
|
||||||
msg = "Tuple[t, ...]: t must be a type."
|
msg = "Tuple[t, ...]: t must be a type."
|
||||||
p = _type_check(params[0], msg)
|
params = tuple(_type_check(p, msg) for p in params[:-1])
|
||||||
return self.copy_with((p, _TypingEllipsis))
|
return self.copy_with((*params, _TypingEllipsis))
|
||||||
msg = "Tuple[t0, t1, ...]: each t must be a type."
|
msg = "Tuple[t0, t1, ...]: each t must be a type."
|
||||||
params = tuple(_type_check(p, msg) for p in params)
|
params = tuple(_type_check(p, msg) for p in params)
|
||||||
return self.copy_with(params)
|
return self.copy_with(params)
|
||||||
|
@ -1441,6 +1564,48 @@ class _ConcatenateGenericAlias(_GenericAlias, _root=True):
|
||||||
return super().copy_with(params)
|
return super().copy_with(params)
|
||||||
|
|
||||||
|
|
||||||
|
@_SpecialForm
|
||||||
|
def Unpack(self, parameters):
|
||||||
|
"""Type unpack operator.
|
||||||
|
|
||||||
|
The type unpack operator takes the child types from some container type,
|
||||||
|
such as `tuple[int, str]` or a `TypeVarTuple`, and 'pulls them out'. For
|
||||||
|
example:
|
||||||
|
|
||||||
|
# For some generic class `Foo`:
|
||||||
|
Foo[Unpack[tuple[int, str]]] # Equivalent to Foo[int, str]
|
||||||
|
|
||||||
|
Ts = TypeVarTuple('Ts')
|
||||||
|
# Specifies that `Bar` is generic in an arbitrary number of types.
|
||||||
|
# (Think of `Ts` as a tuple of an arbitrary number of individual
|
||||||
|
# `TypeVar`s, which the `Unpack` is 'pulling out' directly into the
|
||||||
|
# `Generic[]`.)
|
||||||
|
class Bar(Generic[Unpack[Ts]]): ...
|
||||||
|
Bar[int] # Valid
|
||||||
|
Bar[int, str] # Also valid
|
||||||
|
|
||||||
|
From Python 3.11, this can also be done using the `*` operator:
|
||||||
|
|
||||||
|
Foo[*tuple[int, str]]
|
||||||
|
class Bar(Generic[*Ts]): ...
|
||||||
|
|
||||||
|
Note that there is only some runtime checking of this operator. Not
|
||||||
|
everything the runtime allows may be accepted by static type checkers.
|
||||||
|
|
||||||
|
For more information, see PEP 646.
|
||||||
|
"""
|
||||||
|
item = _type_check(parameters, f'{self} accepts only single type.')
|
||||||
|
return _UnpackGenericAlias(origin=self, args=(item,))
|
||||||
|
|
||||||
|
|
||||||
|
class _UnpackGenericAlias(_GenericAlias, _root=True):
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
# `Unpack` only takes one argument, so __args__ should contain only
|
||||||
|
# a single item.
|
||||||
|
return '*' + repr(self.__args__[0])
|
||||||
|
|
||||||
|
|
||||||
class Generic:
|
class Generic:
|
||||||
"""Abstract base class for generic types.
|
"""Abstract base class for generic types.
|
||||||
|
|
||||||
|
@ -1466,15 +1631,36 @@ class Generic:
|
||||||
|
|
||||||
@_tp_cache
|
@_tp_cache
|
||||||
def __class_getitem__(cls, params):
|
def __class_getitem__(cls, params):
|
||||||
|
"""Parameterizes a generic class.
|
||||||
|
|
||||||
|
At least, parameterizing a generic class is the *main* thing this method
|
||||||
|
does. For example, for some generic class `Foo`, this is called when we
|
||||||
|
do `Foo[int]` - there, with `cls=Foo` and `params=int`.
|
||||||
|
|
||||||
|
However, note that this method is also called when defining generic
|
||||||
|
classes in the first place with `class Foo(Generic[T]): ...`.
|
||||||
|
"""
|
||||||
if not isinstance(params, tuple):
|
if not isinstance(params, tuple):
|
||||||
params = (params,)
|
params = (params,)
|
||||||
if not params and cls is not Tuple:
|
|
||||||
raise TypeError(
|
if not params:
|
||||||
f"Parameter list to {cls.__qualname__}[...] cannot be empty")
|
# We're only ok with `params` being empty if the class's only type
|
||||||
|
# parameter is a `TypeVarTuple` (which can contain zero types).
|
||||||
|
class_params = getattr(cls, "__parameters__", None)
|
||||||
|
only_class_parameter_is_typevartuple = (
|
||||||
|
class_params is not None
|
||||||
|
and len(class_params) == 1
|
||||||
|
and isinstance(class_params[0], TypeVarTuple)
|
||||||
|
)
|
||||||
|
if not only_class_parameter_is_typevartuple:
|
||||||
|
raise TypeError(
|
||||||
|
f"Parameter list to {cls.__qualname__}[...] cannot be empty"
|
||||||
|
)
|
||||||
|
|
||||||
params = tuple(_type_convert(p) for p in params)
|
params = tuple(_type_convert(p) for p in params)
|
||||||
if cls in (Generic, Protocol):
|
if cls in (Generic, Protocol):
|
||||||
# Generic and Protocol can only be subscripted with unique type variables.
|
# Generic and Protocol can only be subscripted with unique type variables.
|
||||||
if not all(isinstance(p, (TypeVar, ParamSpec)) for p in params):
|
if not all(_is_typevar_like(p) for p in params):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"Parameters to {cls.__name__}[...] must all be type variables "
|
f"Parameters to {cls.__name__}[...] must all be type variables "
|
||||||
f"or parameter specification variables.")
|
f"or parameter specification variables.")
|
||||||
|
@ -1485,11 +1671,16 @@ class Generic:
|
||||||
# Subscripting a regular Generic subclass.
|
# Subscripting a regular Generic subclass.
|
||||||
if any(isinstance(t, ParamSpec) for t in cls.__parameters__):
|
if any(isinstance(t, ParamSpec) for t in cls.__parameters__):
|
||||||
params = _prepare_paramspec_params(cls, params)
|
params = _prepare_paramspec_params(cls, params)
|
||||||
else:
|
elif not any(isinstance(p, TypeVarTuple) for p in cls.__parameters__):
|
||||||
|
# We only run this if there are no TypeVarTuples, because we
|
||||||
|
# don't check variadic generic arity at runtime (to reduce
|
||||||
|
# complexity of typing.py).
|
||||||
_check_generic(cls, params, len(cls.__parameters__))
|
_check_generic(cls, params, len(cls.__parameters__))
|
||||||
return _GenericAlias(cls, params,
|
return _GenericAlias(
|
||||||
_typevar_types=(TypeVar, ParamSpec),
|
cls, params,
|
||||||
_paramspec_tvars=True)
|
_typevar_types=(TypeVar, TypeVarTuple, ParamSpec),
|
||||||
|
_paramspec_tvars=True,
|
||||||
|
)
|
||||||
|
|
||||||
def __init_subclass__(cls, *args, **kwargs):
|
def __init_subclass__(cls, *args, **kwargs):
|
||||||
super().__init_subclass__(*args, **kwargs)
|
super().__init_subclass__(*args, **kwargs)
|
||||||
|
@ -1501,7 +1692,9 @@ class Generic:
|
||||||
if error:
|
if error:
|
||||||
raise TypeError("Cannot inherit from plain Generic")
|
raise TypeError("Cannot inherit from plain Generic")
|
||||||
if '__orig_bases__' in cls.__dict__:
|
if '__orig_bases__' in cls.__dict__:
|
||||||
tvars = _collect_type_vars(cls.__orig_bases__, (TypeVar, ParamSpec))
|
tvars = _collect_type_vars(
|
||||||
|
cls.__orig_bases__, (TypeVar, TypeVarTuple, ParamSpec)
|
||||||
|
)
|
||||||
# Look for Generic[T1, ..., Tn].
|
# Look for Generic[T1, ..., Tn].
|
||||||
# If found, tvars must be a subset of it.
|
# If found, tvars must be a subset of it.
|
||||||
# If not found, tvars is it.
|
# If not found, tvars is it.
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Implement support for PEP 646 in typing.py.
|
Loading…
Reference in New Issue