From f508d12ea51855a6eedbbb28a2d55c7a9a3b904f Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Tue, 5 Apr 2022 20:55:02 +0100 Subject: [PATCH 01/23] Add integration tests for type variable substitution --- Lib/test/test_typing.py | 77 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index e09f8aa3fb8496..107b68f0901510 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -447,6 +447,83 @@ def test_bad_var_substitution(self): list[T][arg] +class TypeVarSubstitutionTests(BaseTestCase): + + def test(self): + T = TypeVar('T') + T1 = TypeVar('T1') + T2 = TypeVar('T2') + Ts = TypeVarTuple('Ts') + class A(Generic[T]): pass + class B(Generic[T1, T2]): pass + class C(Generic[*Ts]): pass + tests = [ + ('A[T]', '[()]', TypeError), + ('A[T]', '[int]', A[int]), + ('A[T]', '[int, str]', TypeError), + ('A[T]', '[tuple[int, ...]]', A[tuple[int, ...]]), + ('A[T]', '[*tuple[()]]', A[*tuple[()]]), # Should raise TypeError? + ('A[T]', '[*tuple[int]]', A[*tuple[int]]), # Should be A[int]? + ('A[T]', '[*tuple[int, str]]', A[*tuple[int, str]]), # Should raise TypeError? + ('A[T]', '[*tuple[int, ...]]', A[*tuple[int, ...]]), # Should raise TypeError? + ('A[T]', '[*Ts]', A[T][*Ts]), # Should raise TypeError? + ('A[T]', '[T, *Ts]', TypeError), + ('A[T]', '[*Ts, T]', TypeError), + ('A[T, *tuple[int, ...]]', '[int]', TypeError), + + ('B[T1, T2]', '[()]', TypeError), + ('B[T1, T2]', '[int]', TypeError), + ('B[T1, T2]', '[int, str]', B[int, str]), + ('B[T1, T2]', '[int, str, bool]', TypeError), + ('B[T1, T2]', '[*tuple[int]]', TypeError), + ('B[T1, T2]', '[*tuple[int, str]]', TypeError), # Should be B[int, str]? + ('B[T1, T2]', '[*tuple[int, str, bool]]', TypeError), + ('B[T1, T2]', '[*tuple[int, str], *tuple[float, bool]]', B[*tuple[int, str], *tuple[float, bool]]), # Should raise TypeError? + ('B[T1, T2]', '[tuple[int, ...]]', TypeError), + ('B[T1, T2]', '[tuple[int, ...], tuple[str, ...]]', B[tuple[int, ...], tuple[str, ...]]), + ('B[T1, T2]', '[*tuple[int, ...]]', TypeError), + ('B[T1, T2]', '[*tuple[int, ...], *tuple[str, ...]]', B[*tuple[int, ...], *tuple[str, ...]]), # Should raise TypeError? + ('B[T1, T2]', '[*Ts]', TypeError), + ('B[T1, T2]', '[T, *Ts]', B[T, *Ts]), # Should raise TypeError? + ('B[T1, T2]', '[*Ts, T]', B[*Ts, T]), # Should raise TypeError? + ('B[T1, *tuple[int, ...]]', '[str]', B[str, *tuple[int, ...]]), # Should raise TypeError? + ('B[T1, T2, *tuple[int, ...]]', '[int, str]', TypeError), + + ('C[*Ts]', '[()]', C[()]), + ('C[*Ts]', '[int]', C[int]), + ('C[*Ts]', '[int, str]', C[int, str]), + ('C[*Ts]', '[*tuple[int]]', C[*tuple[int]]), # Should be C[int]? + ('C[*Ts]', '[*tuple[int, str]]', C[*tuple[int, str]]), # Should be C[int, str]? + ('C[*Ts]', '[tuple[int, ...]]', C[tuple[int, ...]]), + ('C[*Ts]', '[tuple[int, ...], tuple[str, ...]]', C[tuple[int, ...], tuple[str, ...]]), + ('C[*Ts]', '[*tuple[int, ...]]', C[*tuple[int, ...]]), + ('C[*Ts]', '[*tuple[int, ...], *tuple[str, ...]]', C[*tuple[int, ...], *tuple[str, ...]]), + ('C[*Ts]', '[*Ts]', C[*Ts]), + ('C[*Ts]', '[T, *Ts]', C[T, *Ts]), + ('C[*Ts]', '[*Ts, T]', C[*Ts, T]), + ('C[T, *Ts]', '[int]', C[int]), + ('C[T, *Ts]', '[int, str]', C[int, str]), + ('C[T, *Ts]', '[int, str, bool]', C[int, str, bool]), + ('C[*Ts, T]', '[int]', C[int]), + ('C[*Ts, T]', '[int, str]', C[int, str]), + ('C[*Ts, T]', '[int, str, bool]', C[int, str, bool]), + ('C[T, *Ts]', '[*tuple[int, ...]]', C[*tuple[int, ...]]), # Should be C[int, *tuple[int, ...]]? + ('C[T, *tuple[int, ...]]', '[str]', C[str, *tuple[int, ...]]), + ('C[T1, T2, *tuple[int, ...]]', '[str, bool]', C[str, bool, *tuple[int, ...]]), + ('C[T1, *tuple[int, ...], T2]', '[str, bool]', C[str, *tuple[int, ...], bool]), + ] + for alias, args, expected in tests: + with self.subTest(alias=alias, args=args, expected=expected): + if inspect.isclass(expected) and issubclass(expected, Exception): + with self.assertRaises(expected): + eval(alias + args) + else: + self.assertEqual( + eval(alias + args), + expected, + ) + + class UnpackTests(BaseTestCase): def test_accepts_single_type(self): From ea104b97a8198bd26e85ea62f383be1c8fae99cf Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Fri, 8 Apr 2022 21:56:55 +0100 Subject: [PATCH 02/23] Add tests for list, dict and tuple --- Lib/test/test_typing.py | 172 +++++++++++++++++++++++++++------------- 1 file changed, 119 insertions(+), 53 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 107b68f0901510..d1bf6951fb1d1a 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -458,59 +458,125 @@ class A(Generic[T]): pass class B(Generic[T1, T2]): pass class C(Generic[*Ts]): pass tests = [ - ('A[T]', '[()]', TypeError), - ('A[T]', '[int]', A[int]), - ('A[T]', '[int, str]', TypeError), - ('A[T]', '[tuple[int, ...]]', A[tuple[int, ...]]), - ('A[T]', '[*tuple[()]]', A[*tuple[()]]), # Should raise TypeError? - ('A[T]', '[*tuple[int]]', A[*tuple[int]]), # Should be A[int]? - ('A[T]', '[*tuple[int, str]]', A[*tuple[int, str]]), # Should raise TypeError? - ('A[T]', '[*tuple[int, ...]]', A[*tuple[int, ...]]), # Should raise TypeError? - ('A[T]', '[*Ts]', A[T][*Ts]), # Should raise TypeError? - ('A[T]', '[T, *Ts]', TypeError), - ('A[T]', '[*Ts, T]', TypeError), - ('A[T, *tuple[int, ...]]', '[int]', TypeError), - - ('B[T1, T2]', '[()]', TypeError), - ('B[T1, T2]', '[int]', TypeError), - ('B[T1, T2]', '[int, str]', B[int, str]), - ('B[T1, T2]', '[int, str, bool]', TypeError), - ('B[T1, T2]', '[*tuple[int]]', TypeError), - ('B[T1, T2]', '[*tuple[int, str]]', TypeError), # Should be B[int, str]? - ('B[T1, T2]', '[*tuple[int, str, bool]]', TypeError), - ('B[T1, T2]', '[*tuple[int, str], *tuple[float, bool]]', B[*tuple[int, str], *tuple[float, bool]]), # Should raise TypeError? - ('B[T1, T2]', '[tuple[int, ...]]', TypeError), - ('B[T1, T2]', '[tuple[int, ...], tuple[str, ...]]', B[tuple[int, ...], tuple[str, ...]]), - ('B[T1, T2]', '[*tuple[int, ...]]', TypeError), - ('B[T1, T2]', '[*tuple[int, ...], *tuple[str, ...]]', B[*tuple[int, ...], *tuple[str, ...]]), # Should raise TypeError? - ('B[T1, T2]', '[*Ts]', TypeError), - ('B[T1, T2]', '[T, *Ts]', B[T, *Ts]), # Should raise TypeError? - ('B[T1, T2]', '[*Ts, T]', B[*Ts, T]), # Should raise TypeError? - ('B[T1, *tuple[int, ...]]', '[str]', B[str, *tuple[int, ...]]), # Should raise TypeError? - ('B[T1, T2, *tuple[int, ...]]', '[int, str]', TypeError), - - ('C[*Ts]', '[()]', C[()]), - ('C[*Ts]', '[int]', C[int]), - ('C[*Ts]', '[int, str]', C[int, str]), - ('C[*Ts]', '[*tuple[int]]', C[*tuple[int]]), # Should be C[int]? - ('C[*Ts]', '[*tuple[int, str]]', C[*tuple[int, str]]), # Should be C[int, str]? - ('C[*Ts]', '[tuple[int, ...]]', C[tuple[int, ...]]), - ('C[*Ts]', '[tuple[int, ...], tuple[str, ...]]', C[tuple[int, ...], tuple[str, ...]]), - ('C[*Ts]', '[*tuple[int, ...]]', C[*tuple[int, ...]]), - ('C[*Ts]', '[*tuple[int, ...], *tuple[str, ...]]', C[*tuple[int, ...], *tuple[str, ...]]), - ('C[*Ts]', '[*Ts]', C[*Ts]), - ('C[*Ts]', '[T, *Ts]', C[T, *Ts]), - ('C[*Ts]', '[*Ts, T]', C[*Ts, T]), - ('C[T, *Ts]', '[int]', C[int]), - ('C[T, *Ts]', '[int, str]', C[int, str]), - ('C[T, *Ts]', '[int, str, bool]', C[int, str, bool]), - ('C[*Ts, T]', '[int]', C[int]), - ('C[*Ts, T]', '[int, str]', C[int, str]), - ('C[*Ts, T]', '[int, str, bool]', C[int, str, bool]), - ('C[T, *Ts]', '[*tuple[int, ...]]', C[*tuple[int, ...]]), # Should be C[int, *tuple[int, ...]]? - ('C[T, *tuple[int, ...]]', '[str]', C[str, *tuple[int, ...]]), - ('C[T1, T2, *tuple[int, ...]]', '[str, bool]', C[str, bool, *tuple[int, ...]]), - ('C[T1, *tuple[int, ...], T2]', '[str, bool]', C[str, *tuple[int, ...], bool]), + ('A[T]', '[()]', TypeError), + ('A[T]', '[int]', A[int]), + ('A[T]', '[int, str]', TypeError), + ('A[T]', '[tuple[int, ...]]', A[tuple[int, ...]]), + ('A[T]', '[*tuple[()]]', A[*tuple[()]]), # Should raise TypeError? + ('A[T]', '[*tuple[int]]', A[*tuple[int]]), # Should be A[int]? + ('A[T]', '[*tuple[int, str]]', A[*tuple[int, str]]), # Should raise TypeError? + ('A[T]', '[*tuple[int, ...]]', A[*tuple[int, ...]]), # Should raise TypeError? + ('A[T]', '[*Ts]', A[T][*Ts]), # Should raise TypeError? + ('A[T]', '[T, *Ts]', TypeError), + ('A[T]', '[*Ts, T]', TypeError), + ('A[T, *tuple[int, ...]]', '[int]', TypeError), + + ('list[T]', '[()]', TypeError), # Should be list[()]? + ('list[T]', '[int]', list[int]), + ('list[T]', '[int, str]', TypeError), + ('list[T]', '[tuple[int, ...]]', list[tuple[int, ...]]), + ('list[T]', '[*tuple[()]]', list[*tuple[()]]), # Should be list[()]? + ('list[T]', '[*tuple[int]]', list[*tuple[int]]), # Should be list[int]? + ('list[T]', '[*tuple[int, str]]', list[*tuple[int, str]]), # Should be list[int, str]? + # Why list[*tuple[int, ...]] rather than list[int, ...]? + # Because as of PEP 484, only tuple[int, ...] is explicitly + # given a meaning - list[int, ...] is not, and we might want + # to give a meaning other than "A list of ints of arbitrary + # length" in the future. + ('list[T]', '[*tuple[int, ...]]', list[*tuple[int, ...]]), + # Ok, technically list isn't a variadic type, but we should + # allow this in line with our spirit of trying to be lenient + # about types at runtime. + ('list[T]', '[*Ts]', list[*Ts]), + ('list[T]', '[T, *Ts]', TypeError), + ('list[T]', '[*Ts, T]', TypeError), + ('list[T, *tuple[int, ...]]', '[int]', list[int, *tuple[int, ...]]), + + ('B[T1, T2]', '[()]', TypeError), + ('B[T1, T2]', '[int]', TypeError), + ('B[T1, T2]', '[int, str]', B[int, str]), + ('B[T1, T2]', '[int, str, bool]', TypeError), + ('B[T1, T2]', '[*tuple[int]]', TypeError), + ('B[T1, T2]', '[*tuple[int, str]]', TypeError), # Should be B[int, str]? + ('B[T1, T2]', '[*tuple[int, str, bool]]', TypeError), + ('B[T1, T2]', '[*tuple[int, str], *tuple[float, bool]]', B[*tuple[int, str], *tuple[float, bool]]), # Should raise TypeError? + ('B[T1, T2]', '[tuple[int, ...]]', TypeError), + ('B[T1, T2]', '[tuple[int, ...], tuple[str, ...]]', B[tuple[int, ...], tuple[str, ...]]), + ('B[T1, T2]', '[*tuple[int, ...]]', TypeError), + ('B[T1, T2]', '[*tuple[int, ...], *tuple[str, ...]]', B[*tuple[int, ...], *tuple[str, ...]]), # Should raise TypeError? + ('B[T1, T2]', '[*Ts]', TypeError), + ('B[T1, T2]', '[T, *Ts]', B[T, *Ts]), # Should raise TypeError? + ('B[T1, T2]', '[*Ts, T]', B[*Ts, T]), # Should raise TypeError? + ('B[T1, *tuple[int, ...]]', '[str]', B[str, *tuple[int, ...]]), # Should raise TypeError? + ('B[T1, T2, *tuple[int, ...]]', '[int, str]', TypeError), + + ('dict[T1, T2]', '[()]', TypeError), + ('dict[T1, T2]', '[int]', TypeError), + ('dict[T1, T2]', '[int, str]', dict[int, str]), + ('dict[T1, T2]', '[int, str, bool]', TypeError), + ('dict[T1, T2]', '[*tuple[int]]', TypeError), + ('dict[T1, T2]', '[*tuple[int, str]]', TypeError), # Should be dict[int, str]? + ('dict[T1, T2]', '[*tuple[int, str, bool]]', TypeError), + ('dict[T1, T2]', '[*tuple[int, str], *tuple[float, bool]]', dict[*tuple[int, str], *tuple[float, bool]]), # Should raise TypeError? + ('dict[T1, T2]', '[tuple[int, ...]]', TypeError), + ('dict[T1, T2]', '[tuple[int, ...], tuple[str, ...]]', dict[tuple[int, ...], tuple[str, ...]]), + # Should be dict[int, int]? This is a tricky one, because we might not + # have exactly two ints. But static checkers accept `t: tuple[int, ...]; t[1]`, + # so I think we should "Try to find to make it *work*" rather than + # "Try to find a way to make it *not* work". + ('dict[T1, T2]', '[*tuple[int, ...]]', TypeError), + ('dict[T1, T2]', '[*tuple[int, ...], *tuple[str, ...]]', dict[*tuple[int, ...], *tuple[str, ...]]), + ('dict[T1, T2]', '[*Ts]', TypeError), + ('dict[T1, T2]', '[T, *Ts]', dict[T, *Ts]), # Should raise TypeError? + ('dict[T1, T2]', '[*Ts, T]', dict[*Ts, T]), # Should raise TypeError? + ('dict[T1, *tuple[int, ...]]', '[str]', dict[str, *tuple[int, ...]]), + ('dict[T1, T2, *tuple[int, ...]]', '[int, str]', dict[int, str, *tuple[int, ...]]), + + ('C[*Ts]', '[()]', C[()]), + ('C[*Ts]', '[int]', C[int]), + ('C[*Ts]', '[int, str]', C[int, str]), + ('C[*Ts]', '[*tuple[int]]', C[*tuple[int]]), # Should be C[int]? + ('C[*Ts]', '[*tuple[int, str]]', C[*tuple[int, str]]), # Should be C[int, str]? + ('C[*Ts]', '[tuple[int, ...]]', C[tuple[int, ...]]), + ('C[*Ts]', '[tuple[int, ...], tuple[str, ...]]', C[tuple[int, ...], tuple[str, ...]]), + ('C[*Ts]', '[*tuple[int, ...]]', C[*tuple[int, ...]]), + ('C[*Ts]', '[*tuple[int, ...], *tuple[str, ...]]', C[*tuple[int, ...], *tuple[str, ...]]), + ('C[*Ts]', '[*Ts]', C[*Ts]), + ('C[*Ts]', '[T, *Ts]', C[T, *Ts]), + ('C[*Ts]', '[*Ts, T]', C[*Ts, T]), + ('C[T, *Ts]', '[int]', C[int]), + ('C[T, *Ts]', '[int, str]', C[int, str]), + ('C[T, *Ts]', '[int, str, bool]', C[int, str, bool]), + ('C[*Ts, T]', '[int]', C[int]), + ('C[*Ts, T]', '[int, str]', C[int, str]), + ('C[*Ts, T]', '[int, str, bool]', C[int, str, bool]), + ('C[T, *Ts]', '[*tuple[int, ...]]', C[*tuple[int, ...]]), # Should be C[int, *tuple[int, ...]]? + ('C[T, *tuple[int, ...]]', '[str]', C[str, *tuple[int, ...]]), + ('C[T1, T2, *tuple[int, ...]]', '[str, bool]', C[str, bool, *tuple[int, ...]]), + ('C[T1, *tuple[int, ...], T2]', '[str, bool]', C[str, *tuple[int, ...], bool]), + + ('tuple[*Ts]', '[()]', TypeError), # Should be tuple[()]? + ('tuple[*Ts]', '[int]', tuple[(int,),]), # Should be tuple[int]? + ('tuple[*Ts]', '[int, str]', TypeError), # Should be tuple[int, str]? + ('tuple[*Ts]', '[*tuple[int]]', tuple[(*tuple[int],),]), # Should be tuple[int]? + ('tuple[*Ts]', '[*tuple[int, str]]', tuple[(*tuple[int, str],),]), # Should be tuple[int, str]? + ('tuple[*Ts]', '[tuple[int, ...]]', tuple[(tuple[int, ...],),]), # Should be tuple[tuple[int, ...]]? + ('tuple[*Ts]', '[tuple[int, ...], tuple[str, ...]]', TypeError), # Should be tuple[tuple[int, ...], tuple[str, ...]]? + ('tuple[*Ts]', '[*tuple[int, ...]]', tuple[(*tuple[int, ...],),]), # Should be tuple[int, ...]? + ('tuple[*Ts]', '[*tuple[int, ...], *tuple[str, ...]]', TypeError), # Should be tuple[*tuple[int, ...], *tuple[str, ...]]? + ('tuple[*Ts]', '[*Ts]', tuple[(*Ts,),]), # Should be tuple[*Ts]? + ('tuple[*Ts]', '[T, *Ts]', TypeError), # Should be tuple[T, *Ts]? + ('tuple[*Ts]', '[*Ts, T]', TypeError), # Should be tuple[*Ts, T]? + ('tuple[T, *Ts]', '[int]', TypeError), # Should be tuple[int]? + ('tuple[T, *Ts]', '[int, str]', tuple[int, (str,)]), # Should be tuple[int, str]? + ('tuple[T, *Ts]', '[int, str, bool]', TypeError), # Should be tuple[int, str, bool]? + ('tuple[*Ts, T]', '[int]', TypeError), # Should be tuple[int]? + ('tuple[*Ts, T]', '[int, str]', tuple[(int,), str]), # Should be tuple[int, str]? + ('tuple[*Ts, T]', '[int, str, bool]', TypeError), # Should be tuple[int, str, bool]? + ('tuple[T, *Ts]', '[*tuple[int, ...]]', TypeError), # Should be tuple[int, *tuple[int, ...]]? + ('tuple[T, *tuple[int, ...]]', '[str]', tuple[str, *tuple[int, ...]]), + ('tuple[T1, T2, *tuple[int, ...]]', '[str, bool]', tuple[str, bool, *tuple[int, ...]]), + ('tuple[T1, *tuple[int, ...], T2]', '[str, bool]', tuple[str, *tuple[int, ...], bool]), ] for alias, args, expected in tests: with self.subTest(alias=alias, args=args, expected=expected): From 526d9e8995ff4da08fd4e2d8c43fc0c39deef593 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 9 Apr 2022 10:15:12 +0100 Subject: [PATCH 03/23] Add tests for Tuple --- Lib/test/test_typing.py | 49 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index d1bf6951fb1d1a..2010f5bb844a2c 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -462,10 +462,15 @@ class C(Generic[*Ts]): pass ('A[T]', '[int]', A[int]), ('A[T]', '[int, str]', TypeError), ('A[T]', '[tuple[int, ...]]', A[tuple[int, ...]]), + ('A[T]', '[Tuple[int, ...]]', A[Tuple[int, ...]]), ('A[T]', '[*tuple[()]]', A[*tuple[()]]), # Should raise TypeError? + ('A[T]', '[*Tuple[()]]', A[*Tuple[()]]), # Should raise TypeError? ('A[T]', '[*tuple[int]]', A[*tuple[int]]), # Should be A[int]? + ('A[T]', '[*Tuple[int]]', A[*Tuple[int]]), # Should be A[int]? ('A[T]', '[*tuple[int, str]]', A[*tuple[int, str]]), # Should raise TypeError? + ('A[T]', '[*Tuple[int, str]]', A[*Tuple[int, str]]), # Should raise TypeError? ('A[T]', '[*tuple[int, ...]]', A[*tuple[int, ...]]), # Should raise TypeError? + ('A[T]', '[*Tuple[int, ...]]', A[*Tuple[int, ...]]), # Should raise TypeError? ('A[T]', '[*Ts]', A[T][*Ts]), # Should raise TypeError? ('A[T]', '[T, *Ts]', TypeError), ('A[T]', '[*Ts, T]', TypeError), @@ -475,15 +480,20 @@ class C(Generic[*Ts]): pass ('list[T]', '[int]', list[int]), ('list[T]', '[int, str]', TypeError), ('list[T]', '[tuple[int, ...]]', list[tuple[int, ...]]), + ('list[T]', '[Tuple[int, ...]]', list[Tuple[int, ...]]), ('list[T]', '[*tuple[()]]', list[*tuple[()]]), # Should be list[()]? + ('list[T]', '[*Tuple[()]]', list[*Tuple[()]]), # Should be list[()]? ('list[T]', '[*tuple[int]]', list[*tuple[int]]), # Should be list[int]? + ('list[T]', '[*Tuple[int]]', list[*Tuple[int]]), # Should be list[int]? ('list[T]', '[*tuple[int, str]]', list[*tuple[int, str]]), # Should be list[int, str]? + ('list[T]', '[*Tuple[int, str]]', list[*Tuple[int, str]]), # Should be list[int, str]? # Why list[*tuple[int, ...]] rather than list[int, ...]? # Because as of PEP 484, only tuple[int, ...] is explicitly # given a meaning - list[int, ...] is not, and we might want # to give a meaning other than "A list of ints of arbitrary # length" in the future. ('list[T]', '[*tuple[int, ...]]', list[*tuple[int, ...]]), + ('list[T]', '[*Tuple[int, ...]]', list[*Tuple[int, ...]]), # Ok, technically list isn't a variadic type, but we should # allow this in line with our spirit of trying to be lenient # about types at runtime. @@ -491,19 +501,28 @@ class C(Generic[*Ts]): pass ('list[T]', '[T, *Ts]', TypeError), ('list[T]', '[*Ts, T]', TypeError), ('list[T, *tuple[int, ...]]', '[int]', list[int, *tuple[int, ...]]), + ('list[T, *Tuple[int, ...]]', '[int]', list[int, *Tuple[int, ...]]), ('B[T1, T2]', '[()]', TypeError), ('B[T1, T2]', '[int]', TypeError), ('B[T1, T2]', '[int, str]', B[int, str]), ('B[T1, T2]', '[int, str, bool]', TypeError), ('B[T1, T2]', '[*tuple[int]]', TypeError), + ('B[T1, T2]', '[*Tuple[int]]', TypeError), ('B[T1, T2]', '[*tuple[int, str]]', TypeError), # Should be B[int, str]? + ('B[T1, T2]', '[*Tuple[int, str]]', TypeError), # Should be B[int, str]? ('B[T1, T2]', '[*tuple[int, str, bool]]', TypeError), + ('B[T1, T2]', '[*Tuple[int, str, bool]]', TypeError), ('B[T1, T2]', '[*tuple[int, str], *tuple[float, bool]]', B[*tuple[int, str], *tuple[float, bool]]), # Should raise TypeError? + ('B[T1, T2]', '[*Tuple[int, str], *Tuple[float, bool]]', B[*Tuple[int, str], *Tuple[float, bool]]), # Should raise TypeError? ('B[T1, T2]', '[tuple[int, ...]]', TypeError), + ('B[T1, T2]', '[Tuple[int, ...]]', TypeError), ('B[T1, T2]', '[tuple[int, ...], tuple[str, ...]]', B[tuple[int, ...], tuple[str, ...]]), + ('B[T1, T2]', '[Tuple[int, ...], Tuple[str, ...]]', B[Tuple[int, ...], Tuple[str, ...]]), ('B[T1, T2]', '[*tuple[int, ...]]', TypeError), + ('B[T1, T2]', '[*Tuple[int, ...]]', TypeError), ('B[T1, T2]', '[*tuple[int, ...], *tuple[str, ...]]', B[*tuple[int, ...], *tuple[str, ...]]), # Should raise TypeError? + ('B[T1, T2]', '[*Tuple[int, ...], *Tuple[str, ...]]', B[*Tuple[int, ...], *Tuple[str, ...]]), # Should raise TypeError? ('B[T1, T2]', '[*Ts]', TypeError), ('B[T1, T2]', '[T, *Ts]', B[T, *Ts]), # Should raise TypeError? ('B[T1, T2]', '[*Ts, T]', B[*Ts, T]), # Should raise TypeError? @@ -515,32 +534,48 @@ class C(Generic[*Ts]): pass ('dict[T1, T2]', '[int, str]', dict[int, str]), ('dict[T1, T2]', '[int, str, bool]', TypeError), ('dict[T1, T2]', '[*tuple[int]]', TypeError), + ('dict[T1, T2]', '[*Tuple[int]]', TypeError), ('dict[T1, T2]', '[*tuple[int, str]]', TypeError), # Should be dict[int, str]? + ('dict[T1, T2]', '[*Tuple[int, str]]', TypeError), # Should be dict[int, str]? ('dict[T1, T2]', '[*tuple[int, str, bool]]', TypeError), + ('dict[T1, T2]', '[*Tuple[int, str, bool]]', TypeError), ('dict[T1, T2]', '[*tuple[int, str], *tuple[float, bool]]', dict[*tuple[int, str], *tuple[float, bool]]), # Should raise TypeError? + ('dict[T1, T2]', '[*Tuple[int, str], *Tuple[float, bool]]', dict[*Tuple[int, str], *Tuple[float, bool]]), # Should raise TypeError? ('dict[T1, T2]', '[tuple[int, ...]]', TypeError), + ('dict[T1, T2]', '[Tuple[int, ...]]', TypeError), ('dict[T1, T2]', '[tuple[int, ...], tuple[str, ...]]', dict[tuple[int, ...], tuple[str, ...]]), + ('dict[T1, T2]', '[Tuple[int, ...], Tuple[str, ...]]', dict[Tuple[int, ...], Tuple[str, ...]]), # Should be dict[int, int]? This is a tricky one, because we might not # have exactly two ints. But static checkers accept `t: tuple[int, ...]; t[1]`, # so I think we should "Try to find to make it *work*" rather than # "Try to find a way to make it *not* work". ('dict[T1, T2]', '[*tuple[int, ...]]', TypeError), + ('dict[T1, T2]', '[*Tuple[int, ...]]', TypeError), ('dict[T1, T2]', '[*tuple[int, ...], *tuple[str, ...]]', dict[*tuple[int, ...], *tuple[str, ...]]), + ('dict[T1, T2]', '[*Tuple[int, ...], *Tuple[str, ...]]', dict[*Tuple[int, ...], *Tuple[str, ...]]), ('dict[T1, T2]', '[*Ts]', TypeError), ('dict[T1, T2]', '[T, *Ts]', dict[T, *Ts]), # Should raise TypeError? ('dict[T1, T2]', '[*Ts, T]', dict[*Ts, T]), # Should raise TypeError? ('dict[T1, *tuple[int, ...]]', '[str]', dict[str, *tuple[int, ...]]), + ('dict[T1, *Tuple[int, ...]]', '[str]', dict[str, *Tuple[int, ...]]), ('dict[T1, T2, *tuple[int, ...]]', '[int, str]', dict[int, str, *tuple[int, ...]]), + ('dict[T1, T2, *Tuple[int, ...]]', '[int, str]', dict[int, str, *Tuple[int, ...]]), ('C[*Ts]', '[()]', C[()]), ('C[*Ts]', '[int]', C[int]), ('C[*Ts]', '[int, str]', C[int, str]), ('C[*Ts]', '[*tuple[int]]', C[*tuple[int]]), # Should be C[int]? + ('C[*Ts]', '[*Tuple[int]]', C[*Tuple[int]]), # Should be C[int]? ('C[*Ts]', '[*tuple[int, str]]', C[*tuple[int, str]]), # Should be C[int, str]? + ('C[*Ts]', '[*Tuple[int, str]]', C[*Tuple[int, str]]), # Should be C[int, str]? ('C[*Ts]', '[tuple[int, ...]]', C[tuple[int, ...]]), + ('C[*Ts]', '[Tuple[int, ...]]', C[Tuple[int, ...]]), ('C[*Ts]', '[tuple[int, ...], tuple[str, ...]]', C[tuple[int, ...], tuple[str, ...]]), + ('C[*Ts]', '[Tuple[int, ...], Tuple[str, ...]]', C[Tuple[int, ...], Tuple[str, ...]]), ('C[*Ts]', '[*tuple[int, ...]]', C[*tuple[int, ...]]), + ('C[*Ts]', '[*Tuple[int, ...]]', C[*Tuple[int, ...]]), ('C[*Ts]', '[*tuple[int, ...], *tuple[str, ...]]', C[*tuple[int, ...], *tuple[str, ...]]), + ('C[*Ts]', '[*Tuple[int, ...], *Tuple[str, ...]]', C[*Tuple[int, ...], *Tuple[str, ...]]), ('C[*Ts]', '[*Ts]', C[*Ts]), ('C[*Ts]', '[T, *Ts]', C[T, *Ts]), ('C[*Ts]', '[*Ts, T]', C[*Ts, T]), @@ -551,19 +586,29 @@ class C(Generic[*Ts]): pass ('C[*Ts, T]', '[int, str]', C[int, str]), ('C[*Ts, T]', '[int, str, bool]', C[int, str, bool]), ('C[T, *Ts]', '[*tuple[int, ...]]', C[*tuple[int, ...]]), # Should be C[int, *tuple[int, ...]]? + ('C[T, *Ts]', '[*Tuple[int, ...]]', C[*Tuple[int, ...]]), # Should be C[int, *tuple[int, ...]]? ('C[T, *tuple[int, ...]]', '[str]', C[str, *tuple[int, ...]]), + ('C[T, *Tuple[int, ...]]', '[str]', C[str, *Tuple[int, ...]]), ('C[T1, T2, *tuple[int, ...]]', '[str, bool]', C[str, bool, *tuple[int, ...]]), + ('C[T1, T2, *Tuple[int, ...]]', '[str, bool]', C[str, bool, *Tuple[int, ...]]), ('C[T1, *tuple[int, ...], T2]', '[str, bool]', C[str, *tuple[int, ...], bool]), + ('C[T1, *Tuple[int, ...], T2]', '[str, bool]', C[str, *Tuple[int, ...], bool]), ('tuple[*Ts]', '[()]', TypeError), # Should be tuple[()]? ('tuple[*Ts]', '[int]', tuple[(int,),]), # Should be tuple[int]? ('tuple[*Ts]', '[int, str]', TypeError), # Should be tuple[int, str]? ('tuple[*Ts]', '[*tuple[int]]', tuple[(*tuple[int],),]), # Should be tuple[int]? + ('tuple[*Ts]', '[*Tuple[int]]', tuple[(*Tuple[int],),]), # Should be tuple[int]? ('tuple[*Ts]', '[*tuple[int, str]]', tuple[(*tuple[int, str],),]), # Should be tuple[int, str]? + ('tuple[*Ts]', '[*Tuple[int, str]]', tuple[(*Tuple[int, str],),]), # Should be tuple[int, str]? ('tuple[*Ts]', '[tuple[int, ...]]', tuple[(tuple[int, ...],),]), # Should be tuple[tuple[int, ...]]? + ('tuple[*Ts]', '[Tuple[int, ...]]', tuple[(Tuple[int, ...],),]), # Should be tuple[tuple[int, ...]]? ('tuple[*Ts]', '[tuple[int, ...], tuple[str, ...]]', TypeError), # Should be tuple[tuple[int, ...], tuple[str, ...]]? + ('tuple[*Ts]', '[Tuple[int, ...], Tuple[str, ...]]', TypeError), # Should be tuple[tuple[int, ...], tuple[str, ...]]? ('tuple[*Ts]', '[*tuple[int, ...]]', tuple[(*tuple[int, ...],),]), # Should be tuple[int, ...]? + ('tuple[*Ts]', '[*Tuple[int, ...]]', tuple[(*Tuple[int, ...],),]), # Should be tuple[int, ...]? ('tuple[*Ts]', '[*tuple[int, ...], *tuple[str, ...]]', TypeError), # Should be tuple[*tuple[int, ...], *tuple[str, ...]]? + ('tuple[*Ts]', '[*Tuple[int, ...], *Tuple[str, ...]]', TypeError), # Should be tuple[*tuple[int, ...], *tuple[str, ...]]? ('tuple[*Ts]', '[*Ts]', tuple[(*Ts,),]), # Should be tuple[*Ts]? ('tuple[*Ts]', '[T, *Ts]', TypeError), # Should be tuple[T, *Ts]? ('tuple[*Ts]', '[*Ts, T]', TypeError), # Should be tuple[*Ts, T]? @@ -574,9 +619,13 @@ class C(Generic[*Ts]): pass ('tuple[*Ts, T]', '[int, str]', tuple[(int,), str]), # Should be tuple[int, str]? ('tuple[*Ts, T]', '[int, str, bool]', TypeError), # Should be tuple[int, str, bool]? ('tuple[T, *Ts]', '[*tuple[int, ...]]', TypeError), # Should be tuple[int, *tuple[int, ...]]? + ('tuple[T, *Ts]', '[*Tuple[int, ...]]', TypeError), # Should be tuple[int, *tuple[int, ...]]? ('tuple[T, *tuple[int, ...]]', '[str]', tuple[str, *tuple[int, ...]]), + ('tuple[T, *Tuple[int, ...]]', '[str]', tuple[str, *Tuple[int, ...]]), ('tuple[T1, T2, *tuple[int, ...]]', '[str, bool]', tuple[str, bool, *tuple[int, ...]]), + ('tuple[T1, T2, *Tuple[int, ...]]', '[str, bool]', tuple[str, bool, *Tuple[int, ...]]), ('tuple[T1, *tuple[int, ...], T2]', '[str, bool]', tuple[str, *tuple[int, ...], bool]), + ('tuple[T1, *Tuple[int, ...], T2]', '[str, bool]', tuple[str, *Tuple[int, ...], bool]), ] for alias, args, expected in tests: with self.subTest(alias=alias, args=args, expected=expected): From 0f022db57132dc6cec9318768250f27640d4d941 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 9 Apr 2022 10:15:42 +0100 Subject: [PATCH 04/23] Revert "Add tests for Tuple" This reverts commit 526d9e8995ff4da08fd4e2d8c43fc0c39deef593. --- Lib/test/test_typing.py | 49 ----------------------------------------- 1 file changed, 49 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 2010f5bb844a2c..d1bf6951fb1d1a 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -462,15 +462,10 @@ class C(Generic[*Ts]): pass ('A[T]', '[int]', A[int]), ('A[T]', '[int, str]', TypeError), ('A[T]', '[tuple[int, ...]]', A[tuple[int, ...]]), - ('A[T]', '[Tuple[int, ...]]', A[Tuple[int, ...]]), ('A[T]', '[*tuple[()]]', A[*tuple[()]]), # Should raise TypeError? - ('A[T]', '[*Tuple[()]]', A[*Tuple[()]]), # Should raise TypeError? ('A[T]', '[*tuple[int]]', A[*tuple[int]]), # Should be A[int]? - ('A[T]', '[*Tuple[int]]', A[*Tuple[int]]), # Should be A[int]? ('A[T]', '[*tuple[int, str]]', A[*tuple[int, str]]), # Should raise TypeError? - ('A[T]', '[*Tuple[int, str]]', A[*Tuple[int, str]]), # Should raise TypeError? ('A[T]', '[*tuple[int, ...]]', A[*tuple[int, ...]]), # Should raise TypeError? - ('A[T]', '[*Tuple[int, ...]]', A[*Tuple[int, ...]]), # Should raise TypeError? ('A[T]', '[*Ts]', A[T][*Ts]), # Should raise TypeError? ('A[T]', '[T, *Ts]', TypeError), ('A[T]', '[*Ts, T]', TypeError), @@ -480,20 +475,15 @@ class C(Generic[*Ts]): pass ('list[T]', '[int]', list[int]), ('list[T]', '[int, str]', TypeError), ('list[T]', '[tuple[int, ...]]', list[tuple[int, ...]]), - ('list[T]', '[Tuple[int, ...]]', list[Tuple[int, ...]]), ('list[T]', '[*tuple[()]]', list[*tuple[()]]), # Should be list[()]? - ('list[T]', '[*Tuple[()]]', list[*Tuple[()]]), # Should be list[()]? ('list[T]', '[*tuple[int]]', list[*tuple[int]]), # Should be list[int]? - ('list[T]', '[*Tuple[int]]', list[*Tuple[int]]), # Should be list[int]? ('list[T]', '[*tuple[int, str]]', list[*tuple[int, str]]), # Should be list[int, str]? - ('list[T]', '[*Tuple[int, str]]', list[*Tuple[int, str]]), # Should be list[int, str]? # Why list[*tuple[int, ...]] rather than list[int, ...]? # Because as of PEP 484, only tuple[int, ...] is explicitly # given a meaning - list[int, ...] is not, and we might want # to give a meaning other than "A list of ints of arbitrary # length" in the future. ('list[T]', '[*tuple[int, ...]]', list[*tuple[int, ...]]), - ('list[T]', '[*Tuple[int, ...]]', list[*Tuple[int, ...]]), # Ok, technically list isn't a variadic type, but we should # allow this in line with our spirit of trying to be lenient # about types at runtime. @@ -501,28 +491,19 @@ class C(Generic[*Ts]): pass ('list[T]', '[T, *Ts]', TypeError), ('list[T]', '[*Ts, T]', TypeError), ('list[T, *tuple[int, ...]]', '[int]', list[int, *tuple[int, ...]]), - ('list[T, *Tuple[int, ...]]', '[int]', list[int, *Tuple[int, ...]]), ('B[T1, T2]', '[()]', TypeError), ('B[T1, T2]', '[int]', TypeError), ('B[T1, T2]', '[int, str]', B[int, str]), ('B[T1, T2]', '[int, str, bool]', TypeError), ('B[T1, T2]', '[*tuple[int]]', TypeError), - ('B[T1, T2]', '[*Tuple[int]]', TypeError), ('B[T1, T2]', '[*tuple[int, str]]', TypeError), # Should be B[int, str]? - ('B[T1, T2]', '[*Tuple[int, str]]', TypeError), # Should be B[int, str]? ('B[T1, T2]', '[*tuple[int, str, bool]]', TypeError), - ('B[T1, T2]', '[*Tuple[int, str, bool]]', TypeError), ('B[T1, T2]', '[*tuple[int, str], *tuple[float, bool]]', B[*tuple[int, str], *tuple[float, bool]]), # Should raise TypeError? - ('B[T1, T2]', '[*Tuple[int, str], *Tuple[float, bool]]', B[*Tuple[int, str], *Tuple[float, bool]]), # Should raise TypeError? ('B[T1, T2]', '[tuple[int, ...]]', TypeError), - ('B[T1, T2]', '[Tuple[int, ...]]', TypeError), ('B[T1, T2]', '[tuple[int, ...], tuple[str, ...]]', B[tuple[int, ...], tuple[str, ...]]), - ('B[T1, T2]', '[Tuple[int, ...], Tuple[str, ...]]', B[Tuple[int, ...], Tuple[str, ...]]), ('B[T1, T2]', '[*tuple[int, ...]]', TypeError), - ('B[T1, T2]', '[*Tuple[int, ...]]', TypeError), ('B[T1, T2]', '[*tuple[int, ...], *tuple[str, ...]]', B[*tuple[int, ...], *tuple[str, ...]]), # Should raise TypeError? - ('B[T1, T2]', '[*Tuple[int, ...], *Tuple[str, ...]]', B[*Tuple[int, ...], *Tuple[str, ...]]), # Should raise TypeError? ('B[T1, T2]', '[*Ts]', TypeError), ('B[T1, T2]', '[T, *Ts]', B[T, *Ts]), # Should raise TypeError? ('B[T1, T2]', '[*Ts, T]', B[*Ts, T]), # Should raise TypeError? @@ -534,48 +515,32 @@ class C(Generic[*Ts]): pass ('dict[T1, T2]', '[int, str]', dict[int, str]), ('dict[T1, T2]', '[int, str, bool]', TypeError), ('dict[T1, T2]', '[*tuple[int]]', TypeError), - ('dict[T1, T2]', '[*Tuple[int]]', TypeError), ('dict[T1, T2]', '[*tuple[int, str]]', TypeError), # Should be dict[int, str]? - ('dict[T1, T2]', '[*Tuple[int, str]]', TypeError), # Should be dict[int, str]? ('dict[T1, T2]', '[*tuple[int, str, bool]]', TypeError), - ('dict[T1, T2]', '[*Tuple[int, str, bool]]', TypeError), ('dict[T1, T2]', '[*tuple[int, str], *tuple[float, bool]]', dict[*tuple[int, str], *tuple[float, bool]]), # Should raise TypeError? - ('dict[T1, T2]', '[*Tuple[int, str], *Tuple[float, bool]]', dict[*Tuple[int, str], *Tuple[float, bool]]), # Should raise TypeError? ('dict[T1, T2]', '[tuple[int, ...]]', TypeError), - ('dict[T1, T2]', '[Tuple[int, ...]]', TypeError), ('dict[T1, T2]', '[tuple[int, ...], tuple[str, ...]]', dict[tuple[int, ...], tuple[str, ...]]), - ('dict[T1, T2]', '[Tuple[int, ...], Tuple[str, ...]]', dict[Tuple[int, ...], Tuple[str, ...]]), # Should be dict[int, int]? This is a tricky one, because we might not # have exactly two ints. But static checkers accept `t: tuple[int, ...]; t[1]`, # so I think we should "Try to find to make it *work*" rather than # "Try to find a way to make it *not* work". ('dict[T1, T2]', '[*tuple[int, ...]]', TypeError), - ('dict[T1, T2]', '[*Tuple[int, ...]]', TypeError), ('dict[T1, T2]', '[*tuple[int, ...], *tuple[str, ...]]', dict[*tuple[int, ...], *tuple[str, ...]]), - ('dict[T1, T2]', '[*Tuple[int, ...], *Tuple[str, ...]]', dict[*Tuple[int, ...], *Tuple[str, ...]]), ('dict[T1, T2]', '[*Ts]', TypeError), ('dict[T1, T2]', '[T, *Ts]', dict[T, *Ts]), # Should raise TypeError? ('dict[T1, T2]', '[*Ts, T]', dict[*Ts, T]), # Should raise TypeError? ('dict[T1, *tuple[int, ...]]', '[str]', dict[str, *tuple[int, ...]]), - ('dict[T1, *Tuple[int, ...]]', '[str]', dict[str, *Tuple[int, ...]]), ('dict[T1, T2, *tuple[int, ...]]', '[int, str]', dict[int, str, *tuple[int, ...]]), - ('dict[T1, T2, *Tuple[int, ...]]', '[int, str]', dict[int, str, *Tuple[int, ...]]), ('C[*Ts]', '[()]', C[()]), ('C[*Ts]', '[int]', C[int]), ('C[*Ts]', '[int, str]', C[int, str]), ('C[*Ts]', '[*tuple[int]]', C[*tuple[int]]), # Should be C[int]? - ('C[*Ts]', '[*Tuple[int]]', C[*Tuple[int]]), # Should be C[int]? ('C[*Ts]', '[*tuple[int, str]]', C[*tuple[int, str]]), # Should be C[int, str]? - ('C[*Ts]', '[*Tuple[int, str]]', C[*Tuple[int, str]]), # Should be C[int, str]? ('C[*Ts]', '[tuple[int, ...]]', C[tuple[int, ...]]), - ('C[*Ts]', '[Tuple[int, ...]]', C[Tuple[int, ...]]), ('C[*Ts]', '[tuple[int, ...], tuple[str, ...]]', C[tuple[int, ...], tuple[str, ...]]), - ('C[*Ts]', '[Tuple[int, ...], Tuple[str, ...]]', C[Tuple[int, ...], Tuple[str, ...]]), ('C[*Ts]', '[*tuple[int, ...]]', C[*tuple[int, ...]]), - ('C[*Ts]', '[*Tuple[int, ...]]', C[*Tuple[int, ...]]), ('C[*Ts]', '[*tuple[int, ...], *tuple[str, ...]]', C[*tuple[int, ...], *tuple[str, ...]]), - ('C[*Ts]', '[*Tuple[int, ...], *Tuple[str, ...]]', C[*Tuple[int, ...], *Tuple[str, ...]]), ('C[*Ts]', '[*Ts]', C[*Ts]), ('C[*Ts]', '[T, *Ts]', C[T, *Ts]), ('C[*Ts]', '[*Ts, T]', C[*Ts, T]), @@ -586,29 +551,19 @@ class C(Generic[*Ts]): pass ('C[*Ts, T]', '[int, str]', C[int, str]), ('C[*Ts, T]', '[int, str, bool]', C[int, str, bool]), ('C[T, *Ts]', '[*tuple[int, ...]]', C[*tuple[int, ...]]), # Should be C[int, *tuple[int, ...]]? - ('C[T, *Ts]', '[*Tuple[int, ...]]', C[*Tuple[int, ...]]), # Should be C[int, *tuple[int, ...]]? ('C[T, *tuple[int, ...]]', '[str]', C[str, *tuple[int, ...]]), - ('C[T, *Tuple[int, ...]]', '[str]', C[str, *Tuple[int, ...]]), ('C[T1, T2, *tuple[int, ...]]', '[str, bool]', C[str, bool, *tuple[int, ...]]), - ('C[T1, T2, *Tuple[int, ...]]', '[str, bool]', C[str, bool, *Tuple[int, ...]]), ('C[T1, *tuple[int, ...], T2]', '[str, bool]', C[str, *tuple[int, ...], bool]), - ('C[T1, *Tuple[int, ...], T2]', '[str, bool]', C[str, *Tuple[int, ...], bool]), ('tuple[*Ts]', '[()]', TypeError), # Should be tuple[()]? ('tuple[*Ts]', '[int]', tuple[(int,),]), # Should be tuple[int]? ('tuple[*Ts]', '[int, str]', TypeError), # Should be tuple[int, str]? ('tuple[*Ts]', '[*tuple[int]]', tuple[(*tuple[int],),]), # Should be tuple[int]? - ('tuple[*Ts]', '[*Tuple[int]]', tuple[(*Tuple[int],),]), # Should be tuple[int]? ('tuple[*Ts]', '[*tuple[int, str]]', tuple[(*tuple[int, str],),]), # Should be tuple[int, str]? - ('tuple[*Ts]', '[*Tuple[int, str]]', tuple[(*Tuple[int, str],),]), # Should be tuple[int, str]? ('tuple[*Ts]', '[tuple[int, ...]]', tuple[(tuple[int, ...],),]), # Should be tuple[tuple[int, ...]]? - ('tuple[*Ts]', '[Tuple[int, ...]]', tuple[(Tuple[int, ...],),]), # Should be tuple[tuple[int, ...]]? ('tuple[*Ts]', '[tuple[int, ...], tuple[str, ...]]', TypeError), # Should be tuple[tuple[int, ...], tuple[str, ...]]? - ('tuple[*Ts]', '[Tuple[int, ...], Tuple[str, ...]]', TypeError), # Should be tuple[tuple[int, ...], tuple[str, ...]]? ('tuple[*Ts]', '[*tuple[int, ...]]', tuple[(*tuple[int, ...],),]), # Should be tuple[int, ...]? - ('tuple[*Ts]', '[*Tuple[int, ...]]', tuple[(*Tuple[int, ...],),]), # Should be tuple[int, ...]? ('tuple[*Ts]', '[*tuple[int, ...], *tuple[str, ...]]', TypeError), # Should be tuple[*tuple[int, ...], *tuple[str, ...]]? - ('tuple[*Ts]', '[*Tuple[int, ...], *Tuple[str, ...]]', TypeError), # Should be tuple[*tuple[int, ...], *tuple[str, ...]]? ('tuple[*Ts]', '[*Ts]', tuple[(*Ts,),]), # Should be tuple[*Ts]? ('tuple[*Ts]', '[T, *Ts]', TypeError), # Should be tuple[T, *Ts]? ('tuple[*Ts]', '[*Ts, T]', TypeError), # Should be tuple[*Ts, T]? @@ -619,13 +574,9 @@ class C(Generic[*Ts]): pass ('tuple[*Ts, T]', '[int, str]', tuple[(int,), str]), # Should be tuple[int, str]? ('tuple[*Ts, T]', '[int, str, bool]', TypeError), # Should be tuple[int, str, bool]? ('tuple[T, *Ts]', '[*tuple[int, ...]]', TypeError), # Should be tuple[int, *tuple[int, ...]]? - ('tuple[T, *Ts]', '[*Tuple[int, ...]]', TypeError), # Should be tuple[int, *tuple[int, ...]]? ('tuple[T, *tuple[int, ...]]', '[str]', tuple[str, *tuple[int, ...]]), - ('tuple[T, *Tuple[int, ...]]', '[str]', tuple[str, *Tuple[int, ...]]), ('tuple[T1, T2, *tuple[int, ...]]', '[str, bool]', tuple[str, bool, *tuple[int, ...]]), - ('tuple[T1, T2, *Tuple[int, ...]]', '[str, bool]', tuple[str, bool, *Tuple[int, ...]]), ('tuple[T1, *tuple[int, ...], T2]', '[str, bool]', tuple[str, *tuple[int, ...], bool]), - ('tuple[T1, *Tuple[int, ...], T2]', '[str, bool]', tuple[str, *Tuple[int, ...], bool]), ] for alias, args, expected in tests: with self.subTest(alias=alias, args=args, expected=expected): From cc7bf8cc6540f1964dd04ae28f1fb0d6806df781 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sun, 10 Apr 2022 10:09:52 +0100 Subject: [PATCH 05/23] Make it easier to tests all tuple, list and dict types --- Lib/test/test_typing.py | 431 ++++++++++++++++++++++++++++------------ 1 file changed, 299 insertions(+), 132 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index d1bf6951fb1d1a..b51f34590eca41 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -2,6 +2,7 @@ import collections from functools import lru_cache import inspect +import itertools import pickle import re import sys @@ -447,147 +448,313 @@ def test_bad_var_substitution(self): list[T][arg] +def template_replace(templates: list[str], replacements: dict[str, list[str]]) -> list[tuple[str]]: + """Renders templates with possible combinations of replacements. + + Example 1: Suppose that: + templates = ["dog_breed are awesome", "dog_breed are cool"] + replacements = ["dog_breed": ["Huskies", "Beagles"]] + Then we would return: + [ + ("Huskies are awesome", "Huskies are cool"), + ("Beagles are awesome", "Beagles are cool") + ] + + Example 2: Suppose that: + templates = ["Huskies are word1 but also word2"] + replacements = {"word1": ["playful", "cute"], + "word2": ["feisty", "tiring"]} + Then we would return: + [ + ("Huskies are playful but also feisty"), + ("Huskies are playful but also tiring"), + ("Huskies are cute but also feisty"), + ("Huskies are cute but also tiring") + ] + + Note that if any of the replacements do not occur in any template: + templates = ["Huskies are word1", "Beagles!"] + replacements = {"word1": ["playful", "cute"], + "word2": ["feisty", "tiring"]} + Then we do not generate duplicates, returning: + [ + ("Huskies are playful", "Beagles!"), + ("Huskies are cute", "Beagles!") + ] + """ + # First, build a structure like: + # [ + # [("word1", "playful"), ("word1", "cute")], + # [("word2", "feisty"), ("word2", "tiring")] + # ] + replacement_combos = [] + for original, possible_replacements in replacements.items(): + original_replacement_tuples = [] + for replacement in possible_replacements: + original_replacement_tuples.append((original, replacement)) + replacement_combos.append(original_replacement_tuples) + + # Second, generate rendered templates, including possible duplicates. + rendered_templates = [] + for replacement_combo in itertools.product(*replacement_combos): + # replacement_combo would be e.g. + # [("word1", "playful"), ("word2", "feisty")] + templates_with_replacements = [] + for template in templates: + for original, replacement in replacement_combo: + template = template.replace(original, replacement) + templates_with_replacements.append(template) + rendered_templates.append(tuple(templates_with_replacements)) + + # Finally, remove the duplicates (but keep the order). + rendered_templates_no_duplicates = [] + for x in rendered_templates: + # Inefficient, but should be fine for our purposes. + if x not in rendered_templates_no_duplicates: + rendered_templates_no_duplicates.append(x) + + return rendered_templates_no_duplicates + + +class TemplateReplacementTests(BaseTestCase): + + def test_two_templates_two_replacements_yields_correct_renders(self): + actual = template_replace( + templates=["Cats are word1", "Dogs are word2"], + replacements={ + "word1": ["small", "cute"], + "word2": ["big", "fluffy"], + }, + ) + expected = [ + ("Cats are small", "Dogs are big"), + ("Cats are small", "Dogs are fluffy"), + ("Cats are cute", "Dogs are big"), + ("Cats are cute", "Dogs are fluffy"), + ] + self.assertEqual(actual, expected) + + def test_no_duplicates_if_replacement_not_in_templates(self): + actual = template_replace( + templates=["Cats are word1", "Dogs!"], + replacements={ + "word1": ["small", "cute"], + "word2": ["big", "fluffy"], + }, + ) + expected = [ + ("Cats are small", "Dogs!"), + ("Cats are cute", "Dogs!"), + ] + self.assertEqual(actual, expected) + + class TypeVarSubstitutionTests(BaseTestCase): - def test(self): + def test_one_parameter(self): T = TypeVar('T') + Ts = TypeVarTuple('Ts') + + class C(Generic[T]): pass + + generics = ['C', 'list', 'List'] + tuple_types = ['tuple', 'Tuple'] + + tests = [ + # Alias # Args # Expected result + ('generic[T]', '[()]', 'TypeError'), + ('generic[T]', '[int]', 'generic[int]'), + ('generic[T]', '[int, str]', 'TypeError'), + ('generic[T]', '[tuple_type[int, ...]]', 'generic[tuple_type[int, ...]]'), + ('generic[T]', '[*tuple_type[()]]', 'generic[*tuple_type[()]]'), # Should raise TypeError + ('generic[T]', '[*tuple_type[int]]', 'generic[*tuple_type[int]]'), # Should be generic[int] + ('generic[T]', '[*tuple_type[int, str]]', 'generic[*tuple_type[int, str]]'), # Should raise TypeError + ('generic[T]', '[*tuple_type[int, ...]]', 'generic[*tuple_type[int, ...]]'), # Should raise TypeError? + ('generic[T]', '[*Ts]', 'generic[*Ts]'), # Should raise TypeError + ('generic[T]', '[T, *Ts]', 'TypeError'), + ('generic[T]', '[*Ts, T]', 'TypeError'), + + # The following two cases work with list/List but not with C + # because list/List don't restrict to only a single type argument + # but C does. + ('list[T, *tuple_type[int, ...]]', '[int]', 'list[int, *tuple_type[int, ...]]'), + ('List[T, *tuple_type[int, ...]]', '[int]', 'TypeError'), # Should be List[int, *tuple_type[int, ...]] + ] + + for alias_template, args_template, expected_template in tests: + rendered_templates = template_replace( + templates=[alias_template, args_template, expected_template], + replacements={'generic': generics, 'tuple_type': tuple_types} + ) + for alias_str, args_str, expected_str in rendered_templates: + with self.subTest(alias=alias_str, args=args_str, expected=expected_str): + if expected_str == 'TypeError': + with self.assertRaises(TypeError): + eval(alias_str + args_str) + else: + self.assertEqual( + eval(alias_str + args_str), + eval(expected_str) + ) + + + def test_two_parameters(self): + T1 = TypeVar('T1') + T2 = TypeVar('T2') + Ts = TypeVarTuple('Ts') + + class C(Generic[T1, T2]): pass + + generics = ['C', 'dict', 'Dict'] + tuple_types = ['tuple', 'Tuple'] + + tests = [ + # Alias # Args # Expected result + ('generic[T1, T2]', '[()]', 'TypeError'), + ('generic[T1, T2]', '[int]', 'TypeError'), + ('generic[T1, T2]', '[int, str]', 'generic[int, str]'), + ('generic[T1, T2]', '[int, str, bool]', 'TypeError'), + ('generic[T1, T2]', '[*tuple_type[int]]', 'TypeError'), + ('generic[T1, T2]', '[*tuple_type[int, str]]', 'TypeError'), # Should be generic[int, str] + ('generic[T1, T2]', '[*tuple_type[int, str, bool]]', 'TypeError'), + ('generic[T1, T2]', '[*tuple_type[int, str], *tuple_type[float, bool]]', 'generic[*tuple_type[int, str], *tuple_type[float, bool]]'), # Should raise TypeError + ('generic[T1, T2]', '[tuple_type[int, ...]]', 'TypeError'), + ('generic[T1, T2]', '[tuple_type[int, ...], tuple_type[str, ...]]', 'generic[tuple_type[int, ...], tuple_type[str, ...]]'), + ('generic[T1, T2]', '[*tuple_type[int, ...]]', 'TypeError'), # Should be generic[int, int]? + ('generic[T1, T2]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'generic[*tuple_type[int, ...], *tuple_type[str, ...]]'), # No idea what to do here + ('generic[T1, T2]', '[*Ts]', 'TypeError'), + ('generic[T1, T2]', '[T, *Ts]', 'generic[T, *Ts]'), # Should raise TypeError + ('generic[T1, T2]', '[*Ts, T]', 'generic[*Ts, T]'), # Should raise TypeError + ('generic[T1, *tuple_type[int, ...]]', '[str]', 'generic[str, *tuple_type[int, ...]]'), # Should raise TypeError + ] + + for alias_template, args_template, expected_template in tests: + rendered_templates = template_replace( + templates=[alias_template, args_template, expected_template], + replacements={'generic': generics, 'tuple_type': tuple_types} + ) + for alias_str, args_str, expected_str in rendered_templates: + with self.subTest(alias=alias_str, args=args_str, expected=expected_str): + if expected_str == 'TypeError': + with self.assertRaises(TypeError): + eval(alias_str + args_str) + else: + self.assertEqual( + eval(alias_str + args_str), + eval(expected_str) + ) + + def test_variadic_parameters(self): T1 = TypeVar('T1') T2 = TypeVar('T2') Ts = TypeVarTuple('Ts') - class A(Generic[T]): pass - class B(Generic[T1, T2]): pass + class C(Generic[*Ts]): pass + + generics = ['C', 'tuple', 'Tuple'] + tuple_types = ['tuple', 'Tuple'] + + # The majority of these have three separate cases for C, tuple and + # Tuple because tuple currently behaves differently. tests = [ - ('A[T]', '[()]', TypeError), - ('A[T]', '[int]', A[int]), - ('A[T]', '[int, str]', TypeError), - ('A[T]', '[tuple[int, ...]]', A[tuple[int, ...]]), - ('A[T]', '[*tuple[()]]', A[*tuple[()]]), # Should raise TypeError? - ('A[T]', '[*tuple[int]]', A[*tuple[int]]), # Should be A[int]? - ('A[T]', '[*tuple[int, str]]', A[*tuple[int, str]]), # Should raise TypeError? - ('A[T]', '[*tuple[int, ...]]', A[*tuple[int, ...]]), # Should raise TypeError? - ('A[T]', '[*Ts]', A[T][*Ts]), # Should raise TypeError? - ('A[T]', '[T, *Ts]', TypeError), - ('A[T]', '[*Ts, T]', TypeError), - ('A[T, *tuple[int, ...]]', '[int]', TypeError), - - ('list[T]', '[()]', TypeError), # Should be list[()]? - ('list[T]', '[int]', list[int]), - ('list[T]', '[int, str]', TypeError), - ('list[T]', '[tuple[int, ...]]', list[tuple[int, ...]]), - ('list[T]', '[*tuple[()]]', list[*tuple[()]]), # Should be list[()]? - ('list[T]', '[*tuple[int]]', list[*tuple[int]]), # Should be list[int]? - ('list[T]', '[*tuple[int, str]]', list[*tuple[int, str]]), # Should be list[int, str]? - # Why list[*tuple[int, ...]] rather than list[int, ...]? - # Because as of PEP 484, only tuple[int, ...] is explicitly - # given a meaning - list[int, ...] is not, and we might want - # to give a meaning other than "A list of ints of arbitrary - # length" in the future. - ('list[T]', '[*tuple[int, ...]]', list[*tuple[int, ...]]), - # Ok, technically list isn't a variadic type, but we should - # allow this in line with our spirit of trying to be lenient - # about types at runtime. - ('list[T]', '[*Ts]', list[*Ts]), - ('list[T]', '[T, *Ts]', TypeError), - ('list[T]', '[*Ts, T]', TypeError), - ('list[T, *tuple[int, ...]]', '[int]', list[int, *tuple[int, ...]]), - - ('B[T1, T2]', '[()]', TypeError), - ('B[T1, T2]', '[int]', TypeError), - ('B[T1, T2]', '[int, str]', B[int, str]), - ('B[T1, T2]', '[int, str, bool]', TypeError), - ('B[T1, T2]', '[*tuple[int]]', TypeError), - ('B[T1, T2]', '[*tuple[int, str]]', TypeError), # Should be B[int, str]? - ('B[T1, T2]', '[*tuple[int, str, bool]]', TypeError), - ('B[T1, T2]', '[*tuple[int, str], *tuple[float, bool]]', B[*tuple[int, str], *tuple[float, bool]]), # Should raise TypeError? - ('B[T1, T2]', '[tuple[int, ...]]', TypeError), - ('B[T1, T2]', '[tuple[int, ...], tuple[str, ...]]', B[tuple[int, ...], tuple[str, ...]]), - ('B[T1, T2]', '[*tuple[int, ...]]', TypeError), - ('B[T1, T2]', '[*tuple[int, ...], *tuple[str, ...]]', B[*tuple[int, ...], *tuple[str, ...]]), # Should raise TypeError? - ('B[T1, T2]', '[*Ts]', TypeError), - ('B[T1, T2]', '[T, *Ts]', B[T, *Ts]), # Should raise TypeError? - ('B[T1, T2]', '[*Ts, T]', B[*Ts, T]), # Should raise TypeError? - ('B[T1, *tuple[int, ...]]', '[str]', B[str, *tuple[int, ...]]), # Should raise TypeError? - ('B[T1, T2, *tuple[int, ...]]', '[int, str]', TypeError), - - ('dict[T1, T2]', '[()]', TypeError), - ('dict[T1, T2]', '[int]', TypeError), - ('dict[T1, T2]', '[int, str]', dict[int, str]), - ('dict[T1, T2]', '[int, str, bool]', TypeError), - ('dict[T1, T2]', '[*tuple[int]]', TypeError), - ('dict[T1, T2]', '[*tuple[int, str]]', TypeError), # Should be dict[int, str]? - ('dict[T1, T2]', '[*tuple[int, str, bool]]', TypeError), - ('dict[T1, T2]', '[*tuple[int, str], *tuple[float, bool]]', dict[*tuple[int, str], *tuple[float, bool]]), # Should raise TypeError? - ('dict[T1, T2]', '[tuple[int, ...]]', TypeError), - ('dict[T1, T2]', '[tuple[int, ...], tuple[str, ...]]', dict[tuple[int, ...], tuple[str, ...]]), - # Should be dict[int, int]? This is a tricky one, because we might not - # have exactly two ints. But static checkers accept `t: tuple[int, ...]; t[1]`, - # so I think we should "Try to find to make it *work*" rather than - # "Try to find a way to make it *not* work". - ('dict[T1, T2]', '[*tuple[int, ...]]', TypeError), - ('dict[T1, T2]', '[*tuple[int, ...], *tuple[str, ...]]', dict[*tuple[int, ...], *tuple[str, ...]]), - ('dict[T1, T2]', '[*Ts]', TypeError), - ('dict[T1, T2]', '[T, *Ts]', dict[T, *Ts]), # Should raise TypeError? - ('dict[T1, T2]', '[*Ts, T]', dict[*Ts, T]), # Should raise TypeError? - ('dict[T1, *tuple[int, ...]]', '[str]', dict[str, *tuple[int, ...]]), - ('dict[T1, T2, *tuple[int, ...]]', '[int, str]', dict[int, str, *tuple[int, ...]]), - - ('C[*Ts]', '[()]', C[()]), - ('C[*Ts]', '[int]', C[int]), - ('C[*Ts]', '[int, str]', C[int, str]), - ('C[*Ts]', '[*tuple[int]]', C[*tuple[int]]), # Should be C[int]? - ('C[*Ts]', '[*tuple[int, str]]', C[*tuple[int, str]]), # Should be C[int, str]? - ('C[*Ts]', '[tuple[int, ...]]', C[tuple[int, ...]]), - ('C[*Ts]', '[tuple[int, ...], tuple[str, ...]]', C[tuple[int, ...], tuple[str, ...]]), - ('C[*Ts]', '[*tuple[int, ...]]', C[*tuple[int, ...]]), - ('C[*Ts]', '[*tuple[int, ...], *tuple[str, ...]]', C[*tuple[int, ...], *tuple[str, ...]]), - ('C[*Ts]', '[*Ts]', C[*Ts]), - ('C[*Ts]', '[T, *Ts]', C[T, *Ts]), - ('C[*Ts]', '[*Ts, T]', C[*Ts, T]), - ('C[T, *Ts]', '[int]', C[int]), - ('C[T, *Ts]', '[int, str]', C[int, str]), - ('C[T, *Ts]', '[int, str, bool]', C[int, str, bool]), - ('C[*Ts, T]', '[int]', C[int]), - ('C[*Ts, T]', '[int, str]', C[int, str]), - ('C[*Ts, T]', '[int, str, bool]', C[int, str, bool]), - ('C[T, *Ts]', '[*tuple[int, ...]]', C[*tuple[int, ...]]), # Should be C[int, *tuple[int, ...]]? - ('C[T, *tuple[int, ...]]', '[str]', C[str, *tuple[int, ...]]), - ('C[T1, T2, *tuple[int, ...]]', '[str, bool]', C[str, bool, *tuple[int, ...]]), - ('C[T1, *tuple[int, ...], T2]', '[str, bool]', C[str, *tuple[int, ...], bool]), - - ('tuple[*Ts]', '[()]', TypeError), # Should be tuple[()]? - ('tuple[*Ts]', '[int]', tuple[(int,),]), # Should be tuple[int]? - ('tuple[*Ts]', '[int, str]', TypeError), # Should be tuple[int, str]? - ('tuple[*Ts]', '[*tuple[int]]', tuple[(*tuple[int],),]), # Should be tuple[int]? - ('tuple[*Ts]', '[*tuple[int, str]]', tuple[(*tuple[int, str],),]), # Should be tuple[int, str]? - ('tuple[*Ts]', '[tuple[int, ...]]', tuple[(tuple[int, ...],),]), # Should be tuple[tuple[int, ...]]? - ('tuple[*Ts]', '[tuple[int, ...], tuple[str, ...]]', TypeError), # Should be tuple[tuple[int, ...], tuple[str, ...]]? - ('tuple[*Ts]', '[*tuple[int, ...]]', tuple[(*tuple[int, ...],),]), # Should be tuple[int, ...]? - ('tuple[*Ts]', '[*tuple[int, ...], *tuple[str, ...]]', TypeError), # Should be tuple[*tuple[int, ...], *tuple[str, ...]]? - ('tuple[*Ts]', '[*Ts]', tuple[(*Ts,),]), # Should be tuple[*Ts]? - ('tuple[*Ts]', '[T, *Ts]', TypeError), # Should be tuple[T, *Ts]? - ('tuple[*Ts]', '[*Ts, T]', TypeError), # Should be tuple[*Ts, T]? - ('tuple[T, *Ts]', '[int]', TypeError), # Should be tuple[int]? - ('tuple[T, *Ts]', '[int, str]', tuple[int, (str,)]), # Should be tuple[int, str]? - ('tuple[T, *Ts]', '[int, str, bool]', TypeError), # Should be tuple[int, str, bool]? - ('tuple[*Ts, T]', '[int]', TypeError), # Should be tuple[int]? - ('tuple[*Ts, T]', '[int, str]', tuple[(int,), str]), # Should be tuple[int, str]? - ('tuple[*Ts, T]', '[int, str, bool]', TypeError), # Should be tuple[int, str, bool]? - ('tuple[T, *Ts]', '[*tuple[int, ...]]', TypeError), # Should be tuple[int, *tuple[int, ...]]? - ('tuple[T, *tuple[int, ...]]', '[str]', tuple[str, *tuple[int, ...]]), - ('tuple[T1, T2, *tuple[int, ...]]', '[str, bool]', tuple[str, bool, *tuple[int, ...]]), - ('tuple[T1, *tuple[int, ...], T2]', '[str, bool]', tuple[str, *tuple[int, ...], bool]), + # Alias # Args # Expected result + ('C[*Ts]', '[()]', 'C[()]'), + ('tuple[*Ts]', '[()]', 'TypeError'), # Should be tuple[()] + ('Tuple[*Ts]', '[()]', 'Tuple[()]'), + + ('C[*Ts]', '[int]', 'C[int]'), + ('tuple[*Ts]', '[int]', 'tuple[(int,),]'), # Should be tuple[int] + ('Tuple[*Ts]', '[int]', 'Tuple[int]'), + + ('C[*Ts]', '[int, str]', 'C[int, str]'), + ('tuple[*Ts]', '[int, str]', 'TypeError'), # Should be tuple[int, str] + ('Tuple[*Ts]', '[int, str]', 'Tuple[int, str]'), + + ('C[*Ts]', '[*tuple_type[int]]', 'C[*tuple_type[int]]'), # Should be C[int] + ('tuple[*Ts]', '[*tuple_type[int]]', 'tuple[(*tuple_type[int],),]'), # Should be tuple[int] + ('Tuple[*Ts]', '[*tuple_type[int]]', 'Tuple[*tuple_type[int]]'), # Should be Tuple[int] + + ('C[*Ts]', '[*tuple_type[int, str]]', 'C[*tuple_type[int, str]]'), # Should be C[int, str] + ('tuple[*Ts]', '[*tuple_type[int, str]]', 'tuple[(*tuple_type[int, str],),]'), # Should be tuple[int, str] + ('Tuple[*Ts]', '[*tuple_type[int, str]]', 'Tuple[*tuple_type[int, str]]'), # Should be Tuple[int, str] + + ('C[*Ts]', '[tuple_type[int, ...]]', 'C[tuple_type[int, ...]]'), + ('tuple[*Ts]', '[tuple_type[int, ...]]', 'tuple[(tuple_type[int, ...],),]'), # Should be tuple[tuple_type[int, ...]] + ('Tuple[*Ts]', '[tuple_type[int, ...]]', 'Tuple[tuple_type[int, ...]]'), + + ('C[*Ts]', '[tuple_type[int, ...], tuple_type[str, ...]]', 'C[tuple_type[int, ...], tuple_type[str, ...]]'), + ('tuple[*Ts]', '[tuple_type[int, ...], tuple_type[str, ...]]', 'TypeError'), # Should be tuple[tuple_type[int, ...], tuple_type[str, ...]] + ('Tuple[*Ts]', '[tuple_type[int, ...], tuple_type[str, ...]]', 'Tuple[tuple_type[int, ...], tuple_type[str, ...]]'), + + ('C[*Ts]', '[*tuple_type[int, ...]]', 'C[*tuple_type[int, ...]]'), + ('tuple[*Ts]', '[*tuple_type[int, ...]]', 'tuple[(*tuple_type[int, ...],),]'), # Should be tuple[*tuple_tuple[int, ...]] + ('Tuple[*Ts]', '[*tuple_type[int, ...]]', 'Tuple[*tuple_type[int, ...]]'), + + ('C[*Ts]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'C[*tuple_type[int, ...], *tuple_type[str, ...]]'), + ('tuple[*Ts]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'TypeError'), # Should be tuple[*tuple_type[int, ...], *tuple_type[str, ...]] + ('Tuple[*Ts]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'Tuple[*tuple_type[int, ...], *tuple_type[str, ...]]'), + + ('C[*Ts]', '[*Ts]', 'C[*Ts]'), + ('tuple[*Ts]', '[*Ts]', 'tuple[(*Ts,),]'), # Should be tuple[*Ts] + ('Tuple[*Ts]', '[*Ts]', 'Tuple[*Ts]'), + + ('C[*Ts]', '[T, *Ts]', 'C[T, *Ts]'), + ('tuple[*Ts]', '[T, *Ts]', 'TypeError'), # Should be tuple[T, *Ts] + ('Tuple[*Ts]', '[T, *Ts]', 'Tuple[T, *Ts]'), + + ('C[*Ts]', '[*Ts, T]', 'C[*Ts, T]'), + ('tuple[*Ts]', '[*Ts, T]', 'TypeError'), # Should be tuple[*Ts, T] + ('Tuple[*Ts]', '[*Ts, T]', 'Tuple[*Ts, T]'), + + ('C[T, *Ts]', '[int]', 'C[int]'), + ('tuple[T, *Ts]', '[int]', 'TypeError'), # Should be tuple[int] + ('Tuple[T, *Ts]', '[int]', 'Tuple[int]'), + + ('C[T, *Ts]', '[int, str]', 'C[int, str]'), + ('tuple[T, *Ts]', '[int, str]', 'tuple[int, (str,)]'), # Should be tuple[int, str] + ('Tuple[T, *Ts]', '[int, str]', 'Tuple[int, str]'), + + ('C[T, *Ts]', '[int, str, bool]', 'C[int, str, bool]'), + ('tuple[T, *Ts]', '[int, str, bool]', 'TypeError'), # Should be tuple[int, str, bool] + ('Tuple[T, *Ts]', '[int, str, bool]', 'Tuple[int, str, bool]'), + + ('C[T, *Ts]', '[*tuple_type[int, ...]]', 'C[*tuple_type[int, ...]]'), # Should be C[int, *tuple_type[int, ...]]? + ('tuple[T, *Ts]', '[*tuple_type[int, ...]]', 'TypeError'), # Should be tuple[int, *tuple_type[int, ...]]? + ('Tuple[T, *Ts]', '[*tuple_type[int, ...]]', 'Tuple[*tuple_type[int, ...]]'), # Should be Tuple[int, *tuple_type[int, ...]]? + + ('C[*Ts, T]', '[int]', 'C[int]'), + ('tuple[*Ts, T]', '[int]', 'TypeError'), # Should be tuple[int] + ('Tuple[*Ts, T]', '[int]', 'Tuple[int]'), + + ('C[*Ts, T]', '[int, str]', 'C[int, str]'), + ('tuple[*Ts, T]', '[int, str]', 'tuple[(int,), str]'), # Should be tuple[int, str] + ('Tuple[*Ts, T]', '[int, str]', 'Tuple[int, str]'), + + ('C[*Ts, T]', '[int, str, bool]', 'C[int, str, bool]'), + ('tuple[*Ts, T]', '[int, str, bool]', 'TypeError'), # Should be tuple[int, str, bool] + ('Tuple[*Ts, T]', '[int, str, bool]', 'Tuple[int, str, bool]'), + + ('generic[T, *tuple_type[int, ...]]', '[str]', 'generic[str, *tuple_type[int, ...]]'), + ('generic[T1, T2, *tuple_type[int, ...]]', '[str, bool]', 'generic[str, bool, *tuple_type[int, ...]]'), + ('generic[T1, *tuple_type[int, ...], T2]', '[str, bool]', 'generic[str, *tuple_type[int, ...], bool]'), ] - for alias, args, expected in tests: - with self.subTest(alias=alias, args=args, expected=expected): - if inspect.isclass(expected) and issubclass(expected, Exception): - with self.assertRaises(expected): - eval(alias + args) - else: - self.assertEqual( - eval(alias + args), - expected, - ) + + for alias_template, args_template, expected_template in tests: + rendered_templates = template_replace( + templates=[alias_template, args_template, expected_template], + replacements={'generic': generics, 'tuple_type': tuple_types} + ) + for alias_str, args_str, expected_str in rendered_templates: + with self.subTest(alias=alias_str, args=args_str, expected=expected_str): + if expected_str == 'TypeError': + with self.assertRaises(TypeError): + eval(alias_str + args_str) + else: + self.assertEqual( + eval(alias_str + args_str), + eval(expected_str) + ) + class UnpackTests(BaseTestCase): From 458b5ddbbb96301496113a7a8aceca4d7116cbf8 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sun, 10 Apr 2022 10:23:54 +0100 Subject: [PATCH 06/23] TypeVarSubstitutionTests -> GenericAliasSubstitutionTests --- Lib/test/test_typing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index b51f34590eca41..98d251ca62915b 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -549,7 +549,7 @@ def test_no_duplicates_if_replacement_not_in_templates(self): self.assertEqual(actual, expected) -class TypeVarSubstitutionTests(BaseTestCase): +class GenericAliasSubstitutionTests(BaseTestCase): def test_one_parameter(self): T = TypeVar('T') From 0c8ba383d83731e03ad9569daec84570d4f57d0f Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sun, 10 Apr 2022 10:39:24 +0100 Subject: [PATCH 07/23] Remove question marks from cases I'm actually pretty sure about --- Lib/test/test_typing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 98d251ca62915b..c5d429e376ccfe 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -569,7 +569,7 @@ class C(Generic[T]): pass ('generic[T]', '[*tuple_type[()]]', 'generic[*tuple_type[()]]'), # Should raise TypeError ('generic[T]', '[*tuple_type[int]]', 'generic[*tuple_type[int]]'), # Should be generic[int] ('generic[T]', '[*tuple_type[int, str]]', 'generic[*tuple_type[int, str]]'), # Should raise TypeError - ('generic[T]', '[*tuple_type[int, ...]]', 'generic[*tuple_type[int, ...]]'), # Should raise TypeError? + ('generic[T]', '[*tuple_type[int, ...]]', 'generic[*tuple_type[int, ...]]'), # Should be generic[int]? ('generic[T]', '[*Ts]', 'generic[*Ts]'), # Should raise TypeError ('generic[T]', '[T, *Ts]', 'TypeError'), ('generic[T]', '[*Ts, T]', 'TypeError'), @@ -718,9 +718,9 @@ class C(Generic[*Ts]): pass ('tuple[T, *Ts]', '[int, str, bool]', 'TypeError'), # Should be tuple[int, str, bool] ('Tuple[T, *Ts]', '[int, str, bool]', 'Tuple[int, str, bool]'), - ('C[T, *Ts]', '[*tuple_type[int, ...]]', 'C[*tuple_type[int, ...]]'), # Should be C[int, *tuple_type[int, ...]]? + ('C[T, *Ts]', '[*tuple_type[int, ...]]', 'C[*tuple_type[int, ...]]'), # Should be C[int, *tuple_type[int, ...]] ('tuple[T, *Ts]', '[*tuple_type[int, ...]]', 'TypeError'), # Should be tuple[int, *tuple_type[int, ...]]? - ('Tuple[T, *Ts]', '[*tuple_type[int, ...]]', 'Tuple[*tuple_type[int, ...]]'), # Should be Tuple[int, *tuple_type[int, ...]]? + ('Tuple[T, *Ts]', '[*tuple_type[int, ...]]', 'Tuple[*tuple_type[int, ...]]'), # Should be Tuple[int, *tuple_type[int, ...]] ('C[*Ts, T]', '[int]', 'C[int]'), ('tuple[*Ts, T]', '[int]', 'TypeError'), # Should be tuple[int] From db24e70cd086bc0ca65d59cc7e014466ebbd9fc7 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Mon, 11 Apr 2022 19:19:15 +0100 Subject: [PATCH 08/23] Note that list[] should only take one argument --- Lib/test/test_typing.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index c5d429e376ccfe..a92a0ce45cdc67 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -574,11 +574,9 @@ class C(Generic[T]): pass ('generic[T]', '[T, *Ts]', 'TypeError'), ('generic[T]', '[*Ts, T]', 'TypeError'), - # The following two cases work with list/List but not with C - # because list/List don't restrict to only a single type argument - # but C does. - ('list[T, *tuple_type[int, ...]]', '[int]', 'list[int, *tuple_type[int, ...]]'), - ('List[T, *tuple_type[int, ...]]', '[int]', 'TypeError'), # Should be List[int, *tuple_type[int, ...]] + ('C[T, *tuple_type[int, ...]]', '[int]', 'TypeError'), + ('list[T, *tuple_type[int, ...]]', '[int]', 'list[int, *tuple_type[int, ...]]'), # Should raise TypeError + ('List[T, *tuple_type[int, ...]]', '[int]', 'TypeError'), ] for alias_template, args_template, expected_template in tests: From 15a48099849ea0367e5949c2e5a93a6643772695 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Mon, 11 Apr 2022 19:23:43 +0100 Subject: [PATCH 09/23] Leave unpacked tuples unsimplified --- Lib/test/test_typing.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index a92a0ce45cdc67..2f647336f237e1 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -567,9 +567,9 @@ class C(Generic[T]): pass ('generic[T]', '[int, str]', 'TypeError'), ('generic[T]', '[tuple_type[int, ...]]', 'generic[tuple_type[int, ...]]'), ('generic[T]', '[*tuple_type[()]]', 'generic[*tuple_type[()]]'), # Should raise TypeError - ('generic[T]', '[*tuple_type[int]]', 'generic[*tuple_type[int]]'), # Should be generic[int] + ('generic[T]', '[*tuple_type[int]]', 'generic[*tuple_type[int]]'), ('generic[T]', '[*tuple_type[int, str]]', 'generic[*tuple_type[int, str]]'), # Should raise TypeError - ('generic[T]', '[*tuple_type[int, ...]]', 'generic[*tuple_type[int, ...]]'), # Should be generic[int]? + ('generic[T]', '[*tuple_type[int, ...]]', 'generic[*tuple_type[int, ...]]'), ('generic[T]', '[*Ts]', 'generic[*Ts]'), # Should raise TypeError ('generic[T]', '[T, *Ts]', 'TypeError'), ('generic[T]', '[*Ts, T]', 'TypeError'), @@ -619,7 +619,7 @@ class C(Generic[T1, T2]): pass ('generic[T1, T2]', '[tuple_type[int, ...]]', 'TypeError'), ('generic[T1, T2]', '[tuple_type[int, ...], tuple_type[str, ...]]', 'generic[tuple_type[int, ...], tuple_type[str, ...]]'), ('generic[T1, T2]', '[*tuple_type[int, ...]]', 'TypeError'), # Should be generic[int, int]? - ('generic[T1, T2]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'generic[*tuple_type[int, ...], *tuple_type[str, ...]]'), # No idea what to do here + ('generic[T1, T2]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'generic[*tuple_type[int, ...], *tuple_type[str, ...]]'), ('generic[T1, T2]', '[*Ts]', 'TypeError'), ('generic[T1, T2]', '[T, *Ts]', 'generic[T, *Ts]'), # Should raise TypeError ('generic[T1, T2]', '[*Ts, T]', 'generic[*Ts, T]'), # Should raise TypeError @@ -669,12 +669,12 @@ class C(Generic[*Ts]): pass ('Tuple[*Ts]', '[int, str]', 'Tuple[int, str]'), ('C[*Ts]', '[*tuple_type[int]]', 'C[*tuple_type[int]]'), # Should be C[int] - ('tuple[*Ts]', '[*tuple_type[int]]', 'tuple[(*tuple_type[int],),]'), # Should be tuple[int] - ('Tuple[*Ts]', '[*tuple_type[int]]', 'Tuple[*tuple_type[int]]'), # Should be Tuple[int] + ('tuple[*Ts]', '[*tuple_type[int]]', 'tuple[(*tuple_type[int],),]'), # Should be tuple[*tuple_type[int]] + ('Tuple[*Ts]', '[*tuple_type[int]]', 'Tuple[*tuple_type[int]]'), # Should be Tuple[*tuple_type[int]] ('C[*Ts]', '[*tuple_type[int, str]]', 'C[*tuple_type[int, str]]'), # Should be C[int, str] - ('tuple[*Ts]', '[*tuple_type[int, str]]', 'tuple[(*tuple_type[int, str],),]'), # Should be tuple[int, str] - ('Tuple[*Ts]', '[*tuple_type[int, str]]', 'Tuple[*tuple_type[int, str]]'), # Should be Tuple[int, str] + ('tuple[*Ts]', '[*tuple_type[int, str]]', 'tuple[(*tuple_type[int, str],),]'), # Should be tuple[*tuple_type[int, str]] + ('Tuple[*Ts]', '[*tuple_type[int, str]]', 'Tuple[*tuple_type[int, str]]'), # Should be Tuple[*tuple_type[int, str]] ('C[*Ts]', '[tuple_type[int, ...]]', 'C[tuple_type[int, ...]]'), ('tuple[*Ts]', '[tuple_type[int, ...]]', 'tuple[(tuple_type[int, ...],),]'), # Should be tuple[tuple_type[int, ...]] From 0d7d2eb5e6860d61c1e9741fb34e491b212facfb Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Mon, 11 Apr 2022 19:29:11 +0100 Subject: [PATCH 10/23] Add generic[*Ts][*tuple[*Ts]] --- Lib/test/test_typing.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 2f647336f237e1..456e33878c92da 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -672,6 +672,10 @@ class C(Generic[*Ts]): pass ('tuple[*Ts]', '[*tuple_type[int]]', 'tuple[(*tuple_type[int],),]'), # Should be tuple[*tuple_type[int]] ('Tuple[*Ts]', '[*tuple_type[int]]', 'Tuple[*tuple_type[int]]'), # Should be Tuple[*tuple_type[int]] + ('C[*Ts]', '[*tuple_type[*Ts]]', 'C[*tuple_type[*Ts]]'), + ('tuple[*Ts]', '[*tuple_type[*Ts]]', 'tuple[(*tuple_type[*Ts],),]'), # Should be tuple[*tuple_type[*Ts]] + ('Tuple[*Ts]', '[*tuple_type[*Ts]]', 'Tuple[*tuple_type[*Ts]]'), + ('C[*Ts]', '[*tuple_type[int, str]]', 'C[*tuple_type[int, str]]'), # Should be C[int, str] ('tuple[*Ts]', '[*tuple_type[int, str]]', 'tuple[(*tuple_type[int, str],),]'), # Should be tuple[*tuple_type[int, str]] ('Tuple[*Ts]', '[*tuple_type[int, str]]', 'Tuple[*tuple_type[int, str]]'), # Should be Tuple[*tuple_type[int, str]] From ab01c8a8c24c31f04a8c70242ef374bca23374e7 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Mon, 11 Apr 2022 19:44:53 +0100 Subject: [PATCH 11/23] Update comments based on decision to leave unpacked tuples unsimplified --- Lib/test/test_typing.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 456e33878c92da..1b589bc4e93e48 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -566,11 +566,15 @@ class C(Generic[T]): pass ('generic[T]', '[int]', 'generic[int]'), ('generic[T]', '[int, str]', 'TypeError'), ('generic[T]', '[tuple_type[int, ...]]', 'generic[tuple_type[int, ...]]'), - ('generic[T]', '[*tuple_type[()]]', 'generic[*tuple_type[()]]'), # Should raise TypeError + ('generic[T]', '[*tuple_type[()]]', 'generic[*tuple_type[()]]'), ('generic[T]', '[*tuple_type[int]]', 'generic[*tuple_type[int]]'), - ('generic[T]', '[*tuple_type[int, str]]', 'generic[*tuple_type[int, str]]'), # Should raise TypeError + # The one below technically isn't valid: after unpacking the tuple, it'd be giving two type + # arguments to an alias that only has one type parameters. But since we don't unpack + # tuples at runtime, we allow it, so the runtime doesn't have to somehow detect that + # *tuple[int, str] counts for two type arguments even though it only looks like one. + ('generic[T]', '[*tuple_type[int, str]]', 'generic[*tuple_type[int, str]]'), ('generic[T]', '[*tuple_type[int, ...]]', 'generic[*tuple_type[int, ...]]'), - ('generic[T]', '[*Ts]', 'generic[*Ts]'), # Should raise TypeError + ('generic[T]', '[*Ts]', 'generic[*Ts]'), # Should raise TypeError ('generic[T]', '[T, *Ts]', 'TypeError'), ('generic[T]', '[*Ts, T]', 'TypeError'), @@ -613,12 +617,12 @@ class C(Generic[T1, T2]): pass ('generic[T1, T2]', '[int, str]', 'generic[int, str]'), ('generic[T1, T2]', '[int, str, bool]', 'TypeError'), ('generic[T1, T2]', '[*tuple_type[int]]', 'TypeError'), - ('generic[T1, T2]', '[*tuple_type[int, str]]', 'TypeError'), # Should be generic[int, str] + ('generic[T1, T2]', '[*tuple_type[int, str]]', 'TypeError'), # Should be generic[*tuple_type[int, str]] ('generic[T1, T2]', '[*tuple_type[int, str, bool]]', 'TypeError'), - ('generic[T1, T2]', '[*tuple_type[int, str], *tuple_type[float, bool]]', 'generic[*tuple_type[int, str], *tuple_type[float, bool]]'), # Should raise TypeError + ('generic[T1, T2]', '[*tuple_type[int, str], *tuple_type[float, bool]]', 'generic[*tuple_type[int, str], *tuple_type[float, bool]]'), ('generic[T1, T2]', '[tuple_type[int, ...]]', 'TypeError'), ('generic[T1, T2]', '[tuple_type[int, ...], tuple_type[str, ...]]', 'generic[tuple_type[int, ...], tuple_type[str, ...]]'), - ('generic[T1, T2]', '[*tuple_type[int, ...]]', 'TypeError'), # Should be generic[int, int]? + ('generic[T1, T2]', '[*tuple_type[int, ...]]', 'TypeError'), # Should be generic[*tuple_type[int, ...]] ('generic[T1, T2]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'generic[*tuple_type[int, ...], *tuple_type[str, ...]]'), ('generic[T1, T2]', '[*Ts]', 'TypeError'), ('generic[T1, T2]', '[T, *Ts]', 'generic[T, *Ts]'), # Should raise TypeError From c4fe922c4e2f301a84e74ca02ec979fa3e47cc0c Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Fri, 15 Apr 2022 10:07:05 +0100 Subject: [PATCH 12/23] Add tests for generic[T1, bool, T2][*tuple[int, str]] --- Lib/test/test_typing.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 1b589bc4e93e48..5ca3d9ff96fb20 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -646,6 +646,38 @@ class C(Generic[T1, T2]): pass eval(expected_str) ) + def test_three_parameters(self): + T1 = TypeVar('T1') + T2 = TypeVar('T2') + T3 = TypeVar('T3') + + class C(Generic[T1, T2, T3]): pass + + generics = ['C'] + tuple_types = ['tuple', 'Tuple'] + + tests = [ + # Alias # Args # Expected result + ('generic[T1, bool, T2]', '[int, str]', 'generic[int, bool, str]'), + ('generic[T1, bool, T2]', '[*tuple_type[int, str]]', 'TypeError'), # Should be generic[int, bool, str] + ] + + for alias_template, args_template, expected_template in tests: + rendered_templates = template_replace( + templates=[alias_template, args_template, expected_template], + replacements={'generic': generics, 'tuple_type': tuple_types} + ) + for alias_str, args_str, expected_str in rendered_templates: + with self.subTest(alias=alias_str, args=args_str, expected=expected_str): + if expected_str == 'TypeError': + with self.assertRaises(TypeError): + eval(alias_str + args_str) + else: + self.assertEqual( + eval(alias_str + args_str), + eval(expected_str) + ) + def test_variadic_parameters(self): T1 = TypeVar('T1') T2 = TypeVar('T2') From d55b8fb02cfd203085ac306936d783faa41080f8 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Fri, 15 Apr 2022 10:21:10 +0100 Subject: [PATCH 13/23] Update comments based on decision to unpack *some* tuples --- Lib/test/test_typing.py | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 5ca3d9ff96fb20..a6c7a6bb9d3b88 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -568,11 +568,9 @@ class C(Generic[T]): pass ('generic[T]', '[tuple_type[int, ...]]', 'generic[tuple_type[int, ...]]'), ('generic[T]', '[*tuple_type[()]]', 'generic[*tuple_type[()]]'), ('generic[T]', '[*tuple_type[int]]', 'generic[*tuple_type[int]]'), - # The one below technically isn't valid: after unpacking the tuple, it'd be giving two type - # arguments to an alias that only has one type parameters. But since we don't unpack - # tuples at runtime, we allow it, so the runtime doesn't have to somehow detect that - # *tuple[int, str] counts for two type arguments even though it only looks like one. + # Should raise TypeError (because too many args) ('generic[T]', '[*tuple_type[int, str]]', 'generic[*tuple_type[int, str]]'), + # Should raise TypeError: *tuple[int, ...] may only be used as an arg if the alias is generic[*Ts]. ('generic[T]', '[*tuple_type[int, ...]]', 'generic[*tuple_type[int, ...]]'), ('generic[T]', '[*Ts]', 'generic[*Ts]'), # Should raise TypeError ('generic[T]', '[T, *Ts]', 'TypeError'), @@ -617,12 +615,15 @@ class C(Generic[T1, T2]): pass ('generic[T1, T2]', '[int, str]', 'generic[int, str]'), ('generic[T1, T2]', '[int, str, bool]', 'TypeError'), ('generic[T1, T2]', '[*tuple_type[int]]', 'TypeError'), - ('generic[T1, T2]', '[*tuple_type[int, str]]', 'TypeError'), # Should be generic[*tuple_type[int, str]] + ('generic[T1, T2]', '[*tuple_type[int, str]]', 'TypeError'), # Should be generic[int, str] ('generic[T1, T2]', '[*tuple_type[int, str, bool]]', 'TypeError'), + # Should raise TypeError (because too many args) ('generic[T1, T2]', '[*tuple_type[int, str], *tuple_type[float, bool]]', 'generic[*tuple_type[int, str], *tuple_type[float, bool]]'), ('generic[T1, T2]', '[tuple_type[int, ...]]', 'TypeError'), ('generic[T1, T2]', '[tuple_type[int, ...], tuple_type[str, ...]]', 'generic[tuple_type[int, ...], tuple_type[str, ...]]'), - ('generic[T1, T2]', '[*tuple_type[int, ...]]', 'TypeError'), # Should be generic[*tuple_type[int, ...]] + # Should raise TypeError: *tuple[int, ...] may only be used as an arg if the alias is generic[*Ts]. + ('generic[T1, T2]', '[*tuple_type[int, ...]]', 'TypeError'), + # Should raise TypeError: more than one unpacked arbitrary-length tuple may not appear in the same arg list. ('generic[T1, T2]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'generic[*tuple_type[int, ...], *tuple_type[str, ...]]'), ('generic[T1, T2]', '[*Ts]', 'TypeError'), ('generic[T1, T2]', '[T, *Ts]', 'generic[T, *Ts]'), # Should raise TypeError @@ -705,16 +706,16 @@ class C(Generic[*Ts]): pass ('Tuple[*Ts]', '[int, str]', 'Tuple[int, str]'), ('C[*Ts]', '[*tuple_type[int]]', 'C[*tuple_type[int]]'), # Should be C[int] - ('tuple[*Ts]', '[*tuple_type[int]]', 'tuple[(*tuple_type[int],),]'), # Should be tuple[*tuple_type[int]] - ('Tuple[*Ts]', '[*tuple_type[int]]', 'Tuple[*tuple_type[int]]'), # Should be Tuple[*tuple_type[int]] + ('tuple[*Ts]', '[*tuple_type[int]]', 'tuple[(*tuple_type[int],),]'), # Should be tuple[int] + ('Tuple[*Ts]', '[*tuple_type[int]]', 'Tuple[*tuple_type[int]]'), # Should be Tuple[int] - ('C[*Ts]', '[*tuple_type[*Ts]]', 'C[*tuple_type[*Ts]]'), - ('tuple[*Ts]', '[*tuple_type[*Ts]]', 'tuple[(*tuple_type[*Ts],),]'), # Should be tuple[*tuple_type[*Ts]] - ('Tuple[*Ts]', '[*tuple_type[*Ts]]', 'Tuple[*tuple_type[*Ts]]'), + ('C[*Ts]', '[*tuple_type[*Ts]]', 'C[*tuple_type[*Ts]]'), # Should be C[*Ts] + ('tuple[*Ts]', '[*tuple_type[*Ts]]', 'tuple[(*tuple_type[*Ts],),]'), # Should be tuple[*Ts] + ('Tuple[*Ts]', '[*tuple_type[*Ts]]', 'Tuple[*tuple_type[*Ts]]'), # Should be Tuple[*Ts] ('C[*Ts]', '[*tuple_type[int, str]]', 'C[*tuple_type[int, str]]'), # Should be C[int, str] - ('tuple[*Ts]', '[*tuple_type[int, str]]', 'tuple[(*tuple_type[int, str],),]'), # Should be tuple[*tuple_type[int, str]] - ('Tuple[*Ts]', '[*tuple_type[int, str]]', 'Tuple[*tuple_type[int, str]]'), # Should be Tuple[*tuple_type[int, str]] + ('tuple[*Ts]', '[*tuple_type[int, str]]', 'tuple[(*tuple_type[int, str],),]'), # Should be tuple[int, str] + ('Tuple[*Ts]', '[*tuple_type[int, str]]', 'Tuple[*tuple_type[int, str]]'), # Should be Tuple[int, str] ('C[*Ts]', '[tuple_type[int, ...]]', 'C[tuple_type[int, ...]]'), ('tuple[*Ts]', '[tuple_type[int, ...]]', 'tuple[(tuple_type[int, ...],),]'), # Should be tuple[tuple_type[int, ...]] @@ -728,8 +729,10 @@ class C(Generic[*Ts]): pass ('tuple[*Ts]', '[*tuple_type[int, ...]]', 'tuple[(*tuple_type[int, ...],),]'), # Should be tuple[*tuple_tuple[int, ...]] ('Tuple[*Ts]', '[*tuple_type[int, ...]]', 'Tuple[*tuple_type[int, ...]]'), + # Should raise TypeError: more than one unpacked arbitrary-length tuple may not appear in the same arg list. ('C[*Ts]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'C[*tuple_type[int, ...], *tuple_type[str, ...]]'), - ('tuple[*Ts]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'TypeError'), # Should be tuple[*tuple_type[int, ...], *tuple_type[str, ...]] + ('tuple[*Ts]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'TypeError'), + # Should raise TypeError: more than one unpacked arbitrary-length tuple may not appear in the same arg list. ('Tuple[*Ts]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'Tuple[*tuple_type[int, ...], *tuple_type[str, ...]]'), ('C[*Ts]', '[*Ts]', 'C[*Ts]'), @@ -756,9 +759,11 @@ class C(Generic[*Ts]): pass ('tuple[T, *Ts]', '[int, str, bool]', 'TypeError'), # Should be tuple[int, str, bool] ('Tuple[T, *Ts]', '[int, str, bool]', 'Tuple[int, str, bool]'), - ('C[T, *Ts]', '[*tuple_type[int, ...]]', 'C[*tuple_type[int, ...]]'), # Should be C[int, *tuple_type[int, ...]] - ('tuple[T, *Ts]', '[*tuple_type[int, ...]]', 'TypeError'), # Should be tuple[int, *tuple_type[int, ...]]? - ('Tuple[T, *Ts]', '[*tuple_type[int, ...]]', 'Tuple[*tuple_type[int, ...]]'), # Should be Tuple[int, *tuple_type[int, ...]] + # Should raise TypeError: *tuple[int, ...] may only be used as an arg if the alias is generic[*Ts]. + ('C[T, *Ts]', '[*tuple_type[int, ...]]', 'C[*tuple_type[int, ...]]'), + ('tuple[T, *Ts]', '[*tuple_type[int, ...]]', 'TypeError'), + # Should raise TypeError: *tuple[int, ...] may only be used as an arg if the alias is generic[*Ts]. + ('Tuple[T, *Ts]', '[*tuple_type[int, ...]]', 'Tuple[*tuple_type[int, ...]]'), ('C[*Ts, T]', '[int]', 'C[int]'), ('tuple[*Ts, T]', '[int]', 'TypeError'), # Should be tuple[int] From e858fc6af418027f850a505333dd88f1751ff75d Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 16 Apr 2022 17:45:04 +0100 Subject: [PATCH 14/23] Note that generic[*tuple[()]] should raise TypeError --- Lib/test/test_typing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index a6c7a6bb9d3b88..2a979380bb056c 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -566,6 +566,7 @@ class C(Generic[T]): pass ('generic[T]', '[int]', 'generic[int]'), ('generic[T]', '[int, str]', 'TypeError'), ('generic[T]', '[tuple_type[int, ...]]', 'generic[tuple_type[int, ...]]'), + # Should raise TypeError: it's equivalent to generic[()] ('generic[T]', '[*tuple_type[()]]', 'generic[*tuple_type[()]]'), ('generic[T]', '[*tuple_type[int]]', 'generic[*tuple_type[int]]'), # Should raise TypeError (because too many args) From 3bc7d262192bb23bfba50f97011cc1bca8aab71b Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 16 Apr 2022 17:48:22 +0100 Subject: [PATCH 15/23] Note that C[T, *tuple[int, ...]][int] raises TypeError only because C is non-variadic --- Lib/test/test_typing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 2a979380bb056c..72d9ff5bf6b42e 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -576,7 +576,7 @@ class C(Generic[T]): pass ('generic[T]', '[*Ts]', 'generic[*Ts]'), # Should raise TypeError ('generic[T]', '[T, *Ts]', 'TypeError'), ('generic[T]', '[*Ts, T]', 'TypeError'), - + # Raises TypeError because C is not variadic. ('C[T, *tuple_type[int, ...]]', '[int]', 'TypeError'), ('list[T, *tuple_type[int, ...]]', '[int]', 'list[int, *tuple_type[int, ...]]'), # Should raise TypeError ('List[T, *tuple_type[int, ...]]', '[int]', 'TypeError'), From c980e4f1e85eea9f4c107534fc0efd92a579985f Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 16 Apr 2022 17:53:22 +0100 Subject: [PATCH 16/23] State that multiple unpackings should be allowed where possible --- Lib/test/test_typing.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 72d9ff5bf6b42e..5605b1b3e2ccdc 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -730,10 +730,12 @@ class C(Generic[*Ts]): pass ('tuple[*Ts]', '[*tuple_type[int, ...]]', 'tuple[(*tuple_type[int, ...],),]'), # Should be tuple[*tuple_tuple[int, ...]] ('Tuple[*Ts]', '[*tuple_type[int, ...]]', 'Tuple[*tuple_type[int, ...]]'), - # Should raise TypeError: more than one unpacked arbitrary-length tuple may not appear in the same arg list. + # Technically, multiple unpackings are forbidden by PEP 646, but we + # choose to be less restrictive at runtime, to allow folks room + # to experiment. So all three of these should be valid. ('C[*Ts]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'C[*tuple_type[int, ...], *tuple_type[str, ...]]'), + # Should be tuple[*tuple_type[int, ...], *tuple_type[str, ...]], to match the other two. ('tuple[*Ts]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'TypeError'), - # Should raise TypeError: more than one unpacked arbitrary-length tuple may not appear in the same arg list. ('Tuple[*Ts]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'Tuple[*tuple_type[int, ...], *tuple_type[str, ...]]'), ('C[*Ts]', '[*Ts]', 'C[*Ts]'), From 95b721074524f89b6e136f9d728ac22410a1e303 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 16 Apr 2022 18:13:43 +0100 Subject: [PATCH 17/23] Add test for generic[T1, *tuple[int, ...], T2][str, bool, float] --- Lib/test/test_typing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 5605b1b3e2ccdc..79e5393f0790f1 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -783,6 +783,7 @@ class C(Generic[*Ts]): pass ('generic[T, *tuple_type[int, ...]]', '[str]', 'generic[str, *tuple_type[int, ...]]'), ('generic[T1, T2, *tuple_type[int, ...]]', '[str, bool]', 'generic[str, bool, *tuple_type[int, ...]]'), ('generic[T1, *tuple_type[int, ...], T2]', '[str, bool]', 'generic[str, *tuple_type[int, ...], bool]'), + ('generic[T1, *tuple_type[int, ...], T2]', '[str, bool, float]', 'TypeError'), ] for alias_template, args_template, expected_template in tests: From 96394b1f86b13fb82ee07a953f2669560f037ecd Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 16 Apr 2022 18:40:01 +0100 Subject: [PATCH 18/23] Update comments according to revised tentative spec at https://github.com/python/cpython/issues/91162 --- Lib/test/test_typing.py | 46 +++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 79e5393f0790f1..c14c41aa3d6421 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -550,6 +550,14 @@ def test_no_duplicates_if_replacement_not_in_templates(self): class GenericAliasSubstitutionTests(BaseTestCase): + """Tests for type variable substitution in generic aliases. + + Note that the expected results here are tentative, based on a + still-being-worked-out spec for what we allow at runtime (given that + implementation of *full* substitution logic at runtime would add too much + complexity to typing.py). This spec is currently being discussed at + https://github.com/python/cpython/issues/91162. + """ def test_one_parameter(self): T = TypeVar('T') @@ -566,19 +574,24 @@ class C(Generic[T]): pass ('generic[T]', '[int]', 'generic[int]'), ('generic[T]', '[int, str]', 'TypeError'), ('generic[T]', '[tuple_type[int, ...]]', 'generic[tuple_type[int, ...]]'), - # Should raise TypeError: it's equivalent to generic[()] + # Should definitely raise TypeError: it's equivalent to generic[()]. ('generic[T]', '[*tuple_type[()]]', 'generic[*tuple_type[()]]'), ('generic[T]', '[*tuple_type[int]]', 'generic[*tuple_type[int]]'), - # Should raise TypeError (because too many args) + # Should definitely raise TypeError: too many args. ('generic[T]', '[*tuple_type[int, str]]', 'generic[*tuple_type[int, str]]'), - # Should raise TypeError: *tuple[int, ...] may only be used as an arg if the alias is generic[*Ts]. + # Should raise TypeError according to the tentative spec: + # *tuple[int, ...] is only valid as an argument to *Ts. ('generic[T]', '[*tuple_type[int, ...]]', 'generic[*tuple_type[int, ...]]'), - ('generic[T]', '[*Ts]', 'generic[*Ts]'), # Should raise TypeError + # Should definitely raise TypeError: a TypeVarTuple is not be a + # valid argument to a TypeVar. + ('generic[T]', '[*Ts]', 'generic[*Ts]'), ('generic[T]', '[T, *Ts]', 'TypeError'), ('generic[T]', '[*Ts, T]', 'TypeError'), # Raises TypeError because C is not variadic. + # (If C _were_ variadic, it'd be fine.) ('C[T, *tuple_type[int, ...]]', '[int]', 'TypeError'), - ('list[T, *tuple_type[int, ...]]', '[int]', 'list[int, *tuple_type[int, ...]]'), # Should raise TypeError + # Should definitely raise TypeError: list only takes one argument. + ('list[T, *tuple_type[int, ...]]', '[int]', 'list[int, *tuple_type[int, ...]]'), ('List[T, *tuple_type[int, ...]]', '[int]', 'TypeError'), ] @@ -618,18 +631,24 @@ class C(Generic[T1, T2]): pass ('generic[T1, T2]', '[*tuple_type[int]]', 'TypeError'), ('generic[T1, T2]', '[*tuple_type[int, str]]', 'TypeError'), # Should be generic[int, str] ('generic[T1, T2]', '[*tuple_type[int, str, bool]]', 'TypeError'), - # Should raise TypeError (because too many args) + # Should definitely raise TypeError: too many args. ('generic[T1, T2]', '[*tuple_type[int, str], *tuple_type[float, bool]]', 'generic[*tuple_type[int, str], *tuple_type[float, bool]]'), ('generic[T1, T2]', '[tuple_type[int, ...]]', 'TypeError'), ('generic[T1, T2]', '[tuple_type[int, ...], tuple_type[str, ...]]', 'generic[tuple_type[int, ...], tuple_type[str, ...]]'), - # Should raise TypeError: *tuple[int, ...] may only be used as an arg if the alias is generic[*Ts]. + # Should raise TypeError according to the tentative spec: + # *tuple[int, ...] is only valid as an argument to *Ts. ('generic[T1, T2]', '[*tuple_type[int, ...]]', 'TypeError'), - # Should raise TypeError: more than one unpacked arbitrary-length tuple may not appear in the same arg list. + # Ditto. ('generic[T1, T2]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'generic[*tuple_type[int, ...], *tuple_type[str, ...]]'), ('generic[T1, T2]', '[*Ts]', 'TypeError'), - ('generic[T1, T2]', '[T, *Ts]', 'generic[T, *Ts]'), # Should raise TypeError - ('generic[T1, T2]', '[*Ts, T]', 'generic[*Ts, T]'), # Should raise TypeError - ('generic[T1, *tuple_type[int, ...]]', '[str]', 'generic[str, *tuple_type[int, ...]]'), # Should raise TypeError + # Should definitely raise TypeError: a TypeVarTuple is not be a + # valid argument to a TypeVar. + ('generic[T1, T2]', '[T, *Ts]', 'generic[T, *Ts]'), + # Ditto. + ('generic[T1, T2]', '[*Ts, T]', 'generic[*Ts, T]'), + # Should raise TypeError according to the tentative spec: + # *tuple[int, ...] is only valid as an argument to *Ts. + ('generic[T1, *tuple_type[int, ...]]', '[str]', 'generic[str, *tuple_type[int, ...]]'), ] for alias_template, args_template, expected_template in tests: @@ -762,10 +781,11 @@ class C(Generic[*Ts]): pass ('tuple[T, *Ts]', '[int, str, bool]', 'TypeError'), # Should be tuple[int, str, bool] ('Tuple[T, *Ts]', '[int, str, bool]', 'Tuple[int, str, bool]'), - # Should raise TypeError: *tuple[int, ...] may only be used as an arg if the alias is generic[*Ts]. + # Should raise TypeError according to the tentative spec: + # *tuple[int, ...] must 'match' exactly with a *Ts, rather than + # being split between a T and a *Ts. ('C[T, *Ts]', '[*tuple_type[int, ...]]', 'C[*tuple_type[int, ...]]'), ('tuple[T, *Ts]', '[*tuple_type[int, ...]]', 'TypeError'), - # Should raise TypeError: *tuple[int, ...] may only be used as an arg if the alias is generic[*Ts]. ('Tuple[T, *Ts]', '[*tuple_type[int, ...]]', 'Tuple[*tuple_type[int, ...]]'), ('C[*Ts, T]', '[int]', 'C[int]'), From ed9a5576d86d24ebd8502b2d10d1b17ff63c6064 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Mon, 18 Apr 2022 08:14:25 +0100 Subject: [PATCH 19/23] Update expected comments to account for unpacked types only being valid arguments for a *Ts --- Lib/test/test_typing.py | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index c14c41aa3d6421..96c2598ae0d4c8 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -574,16 +574,18 @@ class C(Generic[T]): pass ('generic[T]', '[int]', 'generic[int]'), ('generic[T]', '[int, str]', 'TypeError'), ('generic[T]', '[tuple_type[int, ...]]', 'generic[tuple_type[int, ...]]'), - # Should definitely raise TypeError: it's equivalent to generic[()]. + # Should raise TypeError: a) according to the tentative spec, + # unpacked types are only valid as arguments to a TypeVarTuple, and + # b) it's equivalent to generic[()]. ('generic[T]', '[*tuple_type[()]]', 'generic[*tuple_type[()]]'), + # Should raise TypeError according to the tentative spec: unpacked + # types are only valid as arguments to a TypeVarTuple. ('generic[T]', '[*tuple_type[int]]', 'generic[*tuple_type[int]]'), - # Should definitely raise TypeError: too many args. + # Ditto. ('generic[T]', '[*tuple_type[int, str]]', 'generic[*tuple_type[int, str]]'), - # Should raise TypeError according to the tentative spec: - # *tuple[int, ...] is only valid as an argument to *Ts. + # Ditto. ('generic[T]', '[*tuple_type[int, ...]]', 'generic[*tuple_type[int, ...]]'), - # Should definitely raise TypeError: a TypeVarTuple is not be a - # valid argument to a TypeVar. + # Ditto. ('generic[T]', '[*Ts]', 'generic[*Ts]'), ('generic[T]', '[T, *Ts]', 'TypeError'), ('generic[T]', '[*Ts, T]', 'TypeError'), @@ -628,26 +630,27 @@ class C(Generic[T1, T2]): pass ('generic[T1, T2]', '[int]', 'TypeError'), ('generic[T1, T2]', '[int, str]', 'generic[int, str]'), ('generic[T1, T2]', '[int, str, bool]', 'TypeError'), + # Should raise TypeError according to the tentative spec: unpacked + # types are only valid as arguments to a TypeVarTuple. ('generic[T1, T2]', '[*tuple_type[int]]', 'TypeError'), - ('generic[T1, T2]', '[*tuple_type[int, str]]', 'TypeError'), # Should be generic[int, str] + # Ditto. + ('generic[T1, T2]', '[*tuple_type[int, str]]', 'TypeError'), + # Ditto. ('generic[T1, T2]', '[*tuple_type[int, str, bool]]', 'TypeError'), - # Should definitely raise TypeError: too many args. + # Ditto. ('generic[T1, T2]', '[*tuple_type[int, str], *tuple_type[float, bool]]', 'generic[*tuple_type[int, str], *tuple_type[float, bool]]'), ('generic[T1, T2]', '[tuple_type[int, ...]]', 'TypeError'), ('generic[T1, T2]', '[tuple_type[int, ...], tuple_type[str, ...]]', 'generic[tuple_type[int, ...], tuple_type[str, ...]]'), - # Should raise TypeError according to the tentative spec: - # *tuple[int, ...] is only valid as an argument to *Ts. + # Ditto. ('generic[T1, T2]', '[*tuple_type[int, ...]]', 'TypeError'), # Ditto. ('generic[T1, T2]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'generic[*tuple_type[int, ...], *tuple_type[str, ...]]'), ('generic[T1, T2]', '[*Ts]', 'TypeError'), - # Should definitely raise TypeError: a TypeVarTuple is not be a - # valid argument to a TypeVar. + # Ditto. ('generic[T1, T2]', '[T, *Ts]', 'generic[T, *Ts]'), # Ditto. ('generic[T1, T2]', '[*Ts, T]', 'generic[*Ts, T]'), - # Should raise TypeError according to the tentative spec: - # *tuple[int, ...] is only valid as an argument to *Ts. + # Ditto. (None of the things in `generics` were defined using *Ts.) ('generic[T1, *tuple_type[int, ...]]', '[str]', 'generic[str, *tuple_type[int, ...]]'), ] @@ -680,7 +683,9 @@ class C(Generic[T1, T2, T3]): pass tests = [ # Alias # Args # Expected result ('generic[T1, bool, T2]', '[int, str]', 'generic[int, bool, str]'), - ('generic[T1, bool, T2]', '[*tuple_type[int, str]]', 'TypeError'), # Should be generic[int, bool, str] + # Should raise TypeError according to the tentative spec: unpacked + # types are only valid as arguments to a TypeVarTuple. + ('generic[T1, bool, T2]', '[*tuple_type[int, str]]', 'TypeError'), ] for alias_template, args_template, expected_template in tests: From 9947fc073b9d9a16f7d5cecba0794214ea737179 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Mon, 18 Apr 2022 08:34:33 +0100 Subject: [PATCH 20/23] Update to reflect results after merging latest main --- Lib/test/test_typing.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 8cc25a4194a67e..992c11215f55c7 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -580,16 +580,19 @@ class C(Generic[T]): pass # Should raise TypeError: a) according to the tentative spec, # unpacked types are only valid as arguments to a TypeVarTuple, and # b) it's equivalent to generic[()]. - ('generic[T]', '[*tuple_type[()]]', 'generic[*tuple_type[()]]'), + ('generic[T]', '[*tuple[()]]', 'generic[*tuple[()]]'), + ('generic[T]', '[*Tuple[()]]', 'TypeError'), # Should raise TypeError according to the tentative spec: unpacked # types are only valid as arguments to a TypeVarTuple. - ('generic[T]', '[*tuple_type[int]]', 'generic[*tuple_type[int]]'), + ('generic[T]', '[*tuple[int]]', 'generic[*tuple[int]]'), + ('generic[T]', '[*Tuple[int]]', 'TypeError'), # Ditto. - ('generic[T]', '[*tuple_type[int, str]]', 'generic[*tuple_type[int, str]]'), + ('generic[T]', '[*tuple[int, str]]', 'generic[*tuple[int, str]]'), + ('generic[T]', '[*Tuple[int, str]]', 'TypeError'), # Ditto. - ('generic[T]', '[*tuple_type[int, ...]]', 'generic[*tuple_type[int, ...]]'), - # Ditto. - ('generic[T]', '[*Ts]', 'generic[*Ts]'), + ('generic[T]', '[*tuple[int, ...]]', 'generic[*tuple[int, ...]]'), + ('generic[T]', '[*Tuple[int, ...]]', 'TypeError'), + ('generic[T]', '[*Ts]', 'TypeError'), ('generic[T]', '[T, *Ts]', 'TypeError'), ('generic[T]', '[*Ts, T]', 'TypeError'), # Raises TypeError because C is not variadic. From 12496fc0ec0b1adb3030d3f89b054f3e50697040 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Mon, 18 Apr 2022 08:44:01 +0100 Subject: [PATCH 21/23] Update ALL tests to reflect results after merging latest main --- Lib/test/test_typing.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 992c11215f55c7..c007422937985a 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -644,18 +644,18 @@ class C(Generic[T1, T2]): pass # Ditto. ('generic[T1, T2]', '[*tuple_type[int, str, bool]]', 'TypeError'), # Ditto. - ('generic[T1, T2]', '[*tuple_type[int, str], *tuple_type[float, bool]]', 'generic[*tuple_type[int, str], *tuple_type[float, bool]]'), + ('generic[T1, T2]', '[*tuple[int, str], *tuple[float, bool]]', 'generic[*tuple[int, str], *tuple[float, bool]]'), + ('generic[T1, T2]', '[*Tuple[int, str], *Tuple[float, bool]]', 'TypeError'), ('generic[T1, T2]', '[tuple_type[int, ...]]', 'TypeError'), ('generic[T1, T2]', '[tuple_type[int, ...], tuple_type[str, ...]]', 'generic[tuple_type[int, ...], tuple_type[str, ...]]'), # Ditto. ('generic[T1, T2]', '[*tuple_type[int, ...]]', 'TypeError'), # Ditto. - ('generic[T1, T2]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'generic[*tuple_type[int, ...], *tuple_type[str, ...]]'), + ('generic[T1, T2]', '[*tuple[int, ...], *tuple[str, ...]]', 'generic[*tuple[int, ...], *tuple[str, ...]]'), + ('generic[T1, T2]', '[*Tuple[int, ...], *Tuple[str, ...]]', 'TypeError'), ('generic[T1, T2]', '[*Ts]', 'TypeError'), - # Ditto. - ('generic[T1, T2]', '[T, *Ts]', 'generic[T, *Ts]'), - # Ditto. - ('generic[T1, T2]', '[*Ts, T]', 'generic[*Ts, T]'), + ('generic[T1, T2]', '[T, *Ts]', 'TypeError'), + ('generic[T1, T2]', '[*Ts, T]', 'TypeError'), # Ditto. (None of the things in `generics` were defined using *Ts.) ('generic[T1, *tuple_type[int, ...]]', '[str]', 'generic[str, *tuple_type[int, ...]]'), ] @@ -795,9 +795,11 @@ class C(Generic[*Ts]): pass # Should raise TypeError according to the tentative spec: # *tuple[int, ...] must 'match' exactly with a *Ts, rather than # being split between a T and a *Ts. - ('C[T, *Ts]', '[*tuple_type[int, ...]]', 'C[*tuple_type[int, ...]]'), + ('C[T, *Ts]', '[*tuple[int, ...]]', 'C[*tuple[int, ...]]'), + ('C[T, *Ts]', '[*Tuple[int, ...]]', 'TypeError'), ('tuple[T, *Ts]', '[*tuple_type[int, ...]]', 'TypeError'), - ('Tuple[T, *Ts]', '[*tuple_type[int, ...]]', 'Tuple[*tuple_type[int, ...]]'), + ('Tuple[T, *Ts]', '[*tuple[int, ...]]', 'Tuple[*tuple[int, ...]]'), + ('Tuple[T, *Ts]', '[*Tuple[int, ...]]', 'TypeError'), ('C[*Ts, T]', '[int]', 'C[int]'), ('tuple[*Ts, T]', '[int]', 'TypeError'), # Should be tuple[int] From db9af53d38f31961ac7d97bb174a35a77d5d274e Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Mon, 18 Apr 2022 08:54:55 +0100 Subject: [PATCH 22/23] Remove comments saying we should get a TypeError in cases where we *do* now get a TypeError --- Lib/test/test_typing.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index c007422937985a..90a7119c1c298e 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -636,27 +636,30 @@ class C(Generic[T1, T2]): pass ('generic[T1, T2]', '[int]', 'TypeError'), ('generic[T1, T2]', '[int, str]', 'generic[int, str]'), ('generic[T1, T2]', '[int, str, bool]', 'TypeError'), - # Should raise TypeError according to the tentative spec: unpacked - # types are only valid as arguments to a TypeVarTuple. ('generic[T1, T2]', '[*tuple_type[int]]', 'TypeError'), - # Ditto. ('generic[T1, T2]', '[*tuple_type[int, str]]', 'TypeError'), - # Ditto. ('generic[T1, T2]', '[*tuple_type[int, str, bool]]', 'TypeError'), - # Ditto. + + # Should raise TypeError according to the tentative spec: unpacked + # types are only valid as arguments to a TypeVarTuple. ('generic[T1, T2]', '[*tuple[int, str], *tuple[float, bool]]', 'generic[*tuple[int, str], *tuple[float, bool]]'), ('generic[T1, T2]', '[*Tuple[int, str], *Tuple[float, bool]]', 'TypeError'), + ('generic[T1, T2]', '[tuple_type[int, ...]]', 'TypeError'), ('generic[T1, T2]', '[tuple_type[int, ...], tuple_type[str, ...]]', 'generic[tuple_type[int, ...], tuple_type[str, ...]]'), - # Ditto. ('generic[T1, T2]', '[*tuple_type[int, ...]]', 'TypeError'), - # Ditto. + + # Should raise TypeError according to the tentative spec: unpacked + # types are only valid as arguments to a TypeVarTuple. ('generic[T1, T2]', '[*tuple[int, ...], *tuple[str, ...]]', 'generic[*tuple[int, ...], *tuple[str, ...]]'), ('generic[T1, T2]', '[*Tuple[int, ...], *Tuple[str, ...]]', 'TypeError'), + ('generic[T1, T2]', '[*Ts]', 'TypeError'), ('generic[T1, T2]', '[T, *Ts]', 'TypeError'), ('generic[T1, T2]', '[*Ts, T]', 'TypeError'), - # Ditto. (None of the things in `generics` were defined using *Ts.) + # Should raise TypeError according to the tentative spec: unpacked + # types are only valid as arguments to a TypeVarTuple. + # (None of the things in `generics` were defined using *Ts.) ('generic[T1, *tuple_type[int, ...]]', '[str]', 'generic[str, *tuple_type[int, ...]]'), ] @@ -689,8 +692,6 @@ class C(Generic[T1, T2, T3]): pass tests = [ # Alias # Args # Expected result ('generic[T1, bool, T2]', '[int, str]', 'generic[int, bool, str]'), - # Should raise TypeError according to the tentative spec: unpacked - # types are only valid as arguments to a TypeVarTuple. ('generic[T1, bool, T2]', '[*tuple_type[int, str]]', 'TypeError'), ] From c5b5a63585f5e0cd656ddafd1ff00fd25eded010 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Fri, 29 Apr 2022 18:10:30 +0000 Subject: [PATCH 23/23] Update comments on expected results based on latest tentative spec at https://github.com/python/cpython/issues/91162 --- Lib/test/test_typing.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 90a7119c1c298e..e9b42f62ca003b 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -578,12 +578,13 @@ class C(Generic[T]): pass ('generic[T]', '[int, str]', 'TypeError'), ('generic[T]', '[tuple_type[int, ...]]', 'generic[tuple_type[int, ...]]'), # Should raise TypeError: a) according to the tentative spec, - # unpacked types are only valid as arguments to a TypeVarTuple, and - # b) it's equivalent to generic[()]. + # unpacked types cannot be used as arguments to aliases that expect + # a fixed number of arguments; b) it's equivalent to generic[()]. ('generic[T]', '[*tuple[()]]', 'generic[*tuple[()]]'), ('generic[T]', '[*Tuple[()]]', 'TypeError'), # Should raise TypeError according to the tentative spec: unpacked - # types are only valid as arguments to a TypeVarTuple. + # types cannot be used as arguments to aliases that expect a fixed + # number of arguments. ('generic[T]', '[*tuple[int]]', 'generic[*tuple[int]]'), ('generic[T]', '[*Tuple[int]]', 'TypeError'), # Ditto. @@ -641,7 +642,8 @@ class C(Generic[T1, T2]): pass ('generic[T1, T2]', '[*tuple_type[int, str, bool]]', 'TypeError'), # Should raise TypeError according to the tentative spec: unpacked - # types are only valid as arguments to a TypeVarTuple. + # types cannot be used as arguments to aliases that expect a fixed + # number of arguments. ('generic[T1, T2]', '[*tuple[int, str], *tuple[float, bool]]', 'generic[*tuple[int, str], *tuple[float, bool]]'), ('generic[T1, T2]', '[*Tuple[int, str], *Tuple[float, bool]]', 'TypeError'), @@ -649,8 +651,7 @@ class C(Generic[T1, T2]): pass ('generic[T1, T2]', '[tuple_type[int, ...], tuple_type[str, ...]]', 'generic[tuple_type[int, ...], tuple_type[str, ...]]'), ('generic[T1, T2]', '[*tuple_type[int, ...]]', 'TypeError'), - # Should raise TypeError according to the tentative spec: unpacked - # types are only valid as arguments to a TypeVarTuple. + # Ditto. ('generic[T1, T2]', '[*tuple[int, ...], *tuple[str, ...]]', 'generic[*tuple[int, ...], *tuple[str, ...]]'), ('generic[T1, T2]', '[*Tuple[int, ...], *Tuple[str, ...]]', 'TypeError'), @@ -658,7 +659,8 @@ class C(Generic[T1, T2]): pass ('generic[T1, T2]', '[T, *Ts]', 'TypeError'), ('generic[T1, T2]', '[*Ts, T]', 'TypeError'), # Should raise TypeError according to the tentative spec: unpacked - # types are only valid as arguments to a TypeVarTuple. + # types cannot be used as arguments to generics that expect a fixed + # number of arguments. # (None of the things in `generics` were defined using *Ts.) ('generic[T1, *tuple_type[int, ...]]', '[str]', 'generic[str, *tuple_type[int, ...]]'), ] @@ -758,7 +760,7 @@ class C(Generic[*Ts]): pass ('Tuple[*Ts]', '[tuple_type[int, ...], tuple_type[str, ...]]', 'Tuple[tuple_type[int, ...], tuple_type[str, ...]]'), ('C[*Ts]', '[*tuple_type[int, ...]]', 'C[*tuple_type[int, ...]]'), - ('tuple[*Ts]', '[*tuple_type[int, ...]]', 'tuple[(*tuple_type[int, ...],),]'), # Should be tuple[*tuple_tuple[int, ...]] + ('tuple[*Ts]', '[*tuple_type[int, ...]]', 'tuple[(*tuple_type[int, ...],),]'), # Should be tuple[*tuple_type[int, ...]] ('Tuple[*Ts]', '[*tuple_type[int, ...]]', 'Tuple[*tuple_type[int, ...]]'), # Technically, multiple unpackings are forbidden by PEP 646, but we @@ -793,14 +795,11 @@ class C(Generic[*Ts]): pass ('tuple[T, *Ts]', '[int, str, bool]', 'TypeError'), # Should be tuple[int, str, bool] ('Tuple[T, *Ts]', '[int, str, bool]', 'Tuple[int, str, bool]'), - # Should raise TypeError according to the tentative spec: - # *tuple[int, ...] must 'match' exactly with a *Ts, rather than - # being split between a T and a *Ts. - ('C[T, *Ts]', '[*tuple[int, ...]]', 'C[*tuple[int, ...]]'), - ('C[T, *Ts]', '[*Tuple[int, ...]]', 'TypeError'), - ('tuple[T, *Ts]', '[*tuple_type[int, ...]]', 'TypeError'), - ('Tuple[T, *Ts]', '[*tuple[int, ...]]', 'Tuple[*tuple[int, ...]]'), - ('Tuple[T, *Ts]', '[*Tuple[int, ...]]', 'TypeError'), + ('C[T, *Ts]', '[*tuple[int, ...]]', 'C[*tuple[int, ...]]'), # Should be C[int, *tuple[int, ...]] + ('C[T, *Ts]', '[*Tuple[int, ...]]', 'TypeError'), # Ditto + ('tuple[T, *Ts]', '[*tuple_type[int, ...]]', 'TypeError'), # Should be tuple[int, *tuple[int, ...]] + ('Tuple[T, *Ts]', '[*tuple[int, ...]]', 'Tuple[*tuple[int, ...]]'), # Ditto + ('Tuple[T, *Ts]', '[*Tuple[int, ...]]', 'TypeError'), # Ditto ('C[*Ts, T]', '[int]', 'C[int]'), ('tuple[*Ts, T]', '[int]', 'TypeError'), # Should be tuple[int]