[Python-checkins] GH-87390: Add remaining tests for PEP 646 (#98267)

JelleZijlstra webhook-mailer at python.org
Tue Oct 25 10:45:03 EDT 2022


https://github.com/python/cpython/commit/cb95cc24efecf32ad035318867b065a2b042d25a
commit: cb95cc24efecf32ad035318867b065a2b042d25a
branch: main
author: Matthew Rahtz <matthew.rahtz at gmail.com>
committer: JelleZijlstra <jelle.zijlstra at gmail.com>
date: 2022-10-25T07:44:30-07:00
summary:

GH-87390: Add remaining tests for PEP 646 (#98267)

Co-authored-by: Guido van Rossum <gvanrossum at gmail.com>

files:
A Misc/NEWS.d/next/Tests/2022-10-15-07-46-48.gh-issue-87390.asR-Zo.rst
M Lib/test/test_genericalias.py
M Lib/test/test_typing.py

diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py
index 1afb7ea4f85d..6b2de724af6b 100644
--- a/Lib/test/test_genericalias.py
+++ b/Lib/test/test_genericalias.py
@@ -205,23 +205,11 @@ class MyList(list):
         self.assertEqual(repr(list[str]), 'list[str]')
         self.assertEqual(repr(list[()]), 'list[()]')
         self.assertEqual(repr(tuple[int, ...]), 'tuple[int, ...]')
-        x1 = tuple[
-            tuple(  # Effectively the same as starring; TODO
-                tuple[int]
-            )
-        ]
+        x1 = tuple[*tuple[int]]
         self.assertEqual(repr(x1), 'tuple[*tuple[int]]')
-        x2 = tuple[
-            tuple(  # Ditto TODO
-                tuple[int, str]
-            )
-        ]
+        x2 = tuple[*tuple[int, str]]
         self.assertEqual(repr(x2), 'tuple[*tuple[int, str]]')
-        x3 = tuple[
-            tuple(  # Ditto TODO
-                tuple[int, ...]
-            )
-        ]
+        x3 = tuple[*tuple[int, ...]]
         self.assertEqual(repr(x3), 'tuple[*tuple[int, ...]]')
         self.assertTrue(repr(MyList[int]).endswith('.BaseTest.test_repr.<locals>.MyList[int]'))
         self.assertEqual(repr(list[str]()), '[]')  # instances should keep their normal repr
@@ -275,42 +263,24 @@ def test_parameters(self):
         self.assertEqual(L5.__args__, (Callable[[K, V], K],))
         self.assertEqual(L5.__parameters__, (K, V))
 
-        T1 = tuple[
-            tuple(  # Ditto TODO
-                tuple[int]
-            )
-        ]
+        T1 = tuple[*tuple[int]]
         self.assertEqual(
             T1.__args__,
-            tuple(  # Ditto TODO
-                tuple[int]
-            )
+            (*tuple[int],),
         )
         self.assertEqual(T1.__parameters__, ())
 
-        T2 = tuple[
-            tuple(  # Ditto TODO
-                tuple[T]
-            )
-        ]
+        T2 = tuple[*tuple[T]]
         self.assertEqual(
             T2.__args__,
-            tuple(  # Ditto TODO
-                tuple[T]
-            )
+            (*tuple[T],),
         )
         self.assertEqual(T2.__parameters__, (T,))
 
-        T4 = tuple[
-            tuple(  # Ditto TODO
-                tuple[int, str]
-            )
-        ]
+        T4 = tuple[*tuple[int, str]]
         self.assertEqual(
             T4.__args__,
-            tuple(  # Ditto TODO
-                tuple[int, str]
-            )
+            (*tuple[int, str],),
         )
         self.assertEqual(T4.__parameters__, ())
 
@@ -345,18 +315,7 @@ def test_equality(self):
         self.assertEqual(list[int], list[int])
         self.assertEqual(dict[str, int], dict[str, int])
         self.assertEqual((*tuple[int],)[0], (*tuple[int],)[0])
-        self.assertEqual(
-            tuple[
-                tuple(  # Effectively the same as starring; TODO
-                    tuple[int]
-                )
-            ],
-            tuple[
-                tuple(  # Ditto TODO
-                    tuple[int]
-                )
-            ]
-        )
+        self.assertEqual(tuple[*tuple[int]], tuple[*tuple[int]])
         self.assertNotEqual(dict[str, int], dict[str, str])
         self.assertNotEqual(list, list[int])
         self.assertNotEqual(list[int], list)
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 26c3e0294ce6..a3f52b8934d0 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -592,10 +592,9 @@ 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
+    For variadic cases, these tests should be regarded as the source of truth,
+    since we hadn't realised the full complexity of variadic substitution
+    at the time of finalizing PEP 646. For full discussion, see
     https://github.com/python/cpython/issues/91162.
     """
 
@@ -682,9 +681,6 @@ 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, ...]]'),
-            # Should raise TypeError according to the tentative spec: unpacked
-            # types cannot be used as arguments to aliases that expect a fixed
-            # number of arguments.
             ('generic[T1, T2]',                        '[*tuple_type[int, ...]]',                           'TypeError'),
             ('generic[T1, T2]',                        '[int, *tuple_type[str, ...]]',                      'TypeError'),
             ('generic[T1, T2]',                        '[*tuple_type[int, ...], str]',                      'TypeError'),
@@ -692,10 +688,12 @@ class C(Generic[T1, T2]): pass
             ('generic[T1, T2]',                        '[*Ts]',                                             'TypeError'),
             ('generic[T1, T2]',                        '[T, *Ts]',                                          'TypeError'),
             ('generic[T1, T2]',                        '[*Ts, T]',                                          'TypeError'),
-            # Should raise TypeError according to the tentative spec: unpacked
-            # 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.)
+            # This one isn't technically valid - none of the things that
+            # `generic` can be (defined in `generics` above) are variadic, so we
+            # shouldn't really be able to do `generic[T1, *tuple_type[int, ...]]`.
+            # So even if type checkers shouldn't allow it, we allow it at
+            # runtime, in accordance with a general philosophy of "Keep the
+            # runtime lenient so people can experiment with typing constructs".
             ('generic[T1, *tuple_type[int, ...]]',     '[str]',                                             'generic[str, *tuple_type[int, ...]]'),
         ]
 
@@ -757,8 +755,6 @@ 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 = [
             # Alias                                    # Args                                            # Expected result
             ('generic[*Ts]',                           '[()]',                                           'generic[()]'),
@@ -791,7 +787,11 @@ class C(Generic[*Ts]): pass
             ('generic[*Ts, list[T]]',                  '[int, str, bool]',                               'generic[int, str, list[bool]]'),
 
             ('generic[T, *Ts]',                        '[*tuple_type[int, ...]]',                        'generic[int, *tuple_type[int, ...]]'),
+            ('generic[T, *Ts]',                        '[str, *tuple_type[int, ...]]',                   'generic[str, *tuple_type[int, ...]]'),
+            ('generic[T, *Ts]',                        '[*tuple_type[int, ...], str]',                   'generic[int, *tuple_type[int, ...], str]'),
             ('generic[*Ts, T]',                        '[*tuple_type[int, ...]]',                        'generic[*tuple_type[int, ...], int]'),
+            ('generic[*Ts, T]',                        '[str, *tuple_type[int, ...]]',                   'generic[str, *tuple_type[int, ...], int]'),
+            ('generic[*Ts, T]',                        '[*tuple_type[int, ...], str]',                   'generic[*tuple_type[int, ...], str]'),
             ('generic[T1, *Ts, T2]',                   '[*tuple_type[int, ...]]',                        'generic[int, *tuple_type[int, ...], int]'),
             ('generic[T, str, *Ts]',                   '[*tuple_type[int, ...]]',                        'generic[int, str, *tuple_type[int, ...]]'),
             ('generic[*Ts, str, T]',                   '[*tuple_type[int, ...]]',                        'generic[*tuple_type[int, ...], str, int]'),
@@ -830,13 +830,19 @@ class C(Generic[*Ts]): pass
 class UnpackTests(BaseTestCase):
 
     def test_accepts_single_type(self):
+        (*tuple[int],)
         Unpack[Tuple[int]]
 
     def test_rejects_multiple_types(self):
         with self.assertRaises(TypeError):
             Unpack[Tuple[int], Tuple[str]]
+        # We can't do the equivalent for `*` here -
+        # *(Tuple[int], Tuple[str]) is just plain tuple unpacking,
+        # which is valid.
 
     def test_rejects_multiple_parameterization(self):
+        with self.assertRaises(TypeError):
+            (*tuple[int],)[0][tuple[int]]
         with self.assertRaises(TypeError):
             Unpack[Tuple[int]][Tuple[int]]
 
@@ -875,19 +881,20 @@ def test_cannot_call_instance(self):
 
     def test_unpacked_typevartuple_is_equal_to_itself(self):
         Ts = TypeVarTuple('Ts')
+        self.assertEqual((*Ts,)[0], (*Ts,)[0])
         self.assertEqual(Unpack[Ts], Unpack[Ts])
 
     def test_parameterised_tuple_is_equal_to_itself(self):
         Ts = TypeVarTuple('Ts')
-        self.assertEqual(tuple[Unpack[Ts]], tuple[Unpack[Ts]])
+        self.assertEqual(tuple[*Ts], tuple[*Ts])
         self.assertEqual(Tuple[Unpack[Ts]], Tuple[Unpack[Ts]])
 
     def tests_tuple_arg_ordering_matters(self):
         Ts1 = TypeVarTuple('Ts1')
         Ts2 = TypeVarTuple('Ts2')
         self.assertNotEqual(
-            tuple[Unpack[Ts1], Unpack[Ts2]],
-            tuple[Unpack[Ts2], Unpack[Ts1]],
+            tuple[*Ts1, *Ts2],
+            tuple[*Ts2, *Ts1],
         )
         self.assertNotEqual(
             Tuple[Unpack[Ts1], Unpack[Ts2]],
@@ -896,8 +903,8 @@ def tests_tuple_arg_ordering_matters(self):
 
     def test_tuple_args_and_parameters_are_correct(self):
         Ts = TypeVarTuple('Ts')
-        t1 = tuple[Unpack[Ts]]
-        self.assertEqual(t1.__args__, (Unpack[Ts],))
+        t1 = tuple[*Ts]
+        self.assertEqual(t1.__args__, (*Ts,))
         self.assertEqual(t1.__parameters__, (Ts,))
         t2 = Tuple[Unpack[Ts]]
         self.assertEqual(t2.__args__, (Unpack[Ts],))
@@ -907,128 +914,218 @@ def test_var_substitution(self):
         Ts = TypeVarTuple('Ts')
         T = TypeVar('T')
         T2 = TypeVar('T2')
-        class G(Generic[Unpack[Ts]]): pass
+        class G1(Generic[*Ts]): pass
+        class G2(Generic[Unpack[Ts]]): pass
 
-        for A in G, Tuple, tuple:
-            B = A[Unpack[Ts]]
+        for A in G1, G2, Tuple, tuple:
+            B = A[*Ts]
             self.assertEqual(B[()], A[()])
             self.assertEqual(B[float], A[float])
             self.assertEqual(B[float, str], A[float, str])
 
-            C = List[A[Unpack[Ts]]]
-            self.assertEqual(C[()], List[A[()]])
-            self.assertEqual(C[float], List[A[float]])
-            self.assertEqual(C[float, str], List[A[float, str]])
+            C = A[Unpack[Ts]]
+            self.assertEqual(C[()], A[()])
+            self.assertEqual(C[float], A[float])
+            self.assertEqual(C[float, str], A[float, str])
+
+            D = list[A[*Ts]]
+            self.assertEqual(D[()], list[A[()]])
+            self.assertEqual(D[float], list[A[float]])
+            self.assertEqual(D[float, str], list[A[float, str]])
+
+            E = List[A[Unpack[Ts]]]
+            self.assertEqual(E[()], List[A[()]])
+            self.assertEqual(E[float], List[A[float]])
+            self.assertEqual(E[float, str], List[A[float, str]])
+
+            F = A[T, *Ts, T2]
+            with self.assertRaises(TypeError):
+                F[()]
+            with self.assertRaises(TypeError):
+                F[float]
+            self.assertEqual(F[float, str], A[float, str])
+            self.assertEqual(F[float, str, int], A[float, str, int])
+            self.assertEqual(F[float, str, int, bytes], A[float, str, int, bytes])
 
-            D = A[T, Unpack[Ts], T2]
+            G = A[T, Unpack[Ts], T2]
             with self.assertRaises(TypeError):
-                D[()]
+                G[()]
             with self.assertRaises(TypeError):
-                D[float]
-            self.assertEqual(D[float, str], A[float, str])
-            self.assertEqual(D[float, str, int], A[float, str, int])
-            self.assertEqual(D[float, str, int, bytes], A[float, str, int, bytes])
+                G[float]
+            self.assertEqual(G[float, str], A[float, str])
+            self.assertEqual(G[float, str, int], A[float, str, int])
+            self.assertEqual(G[float, str, int, bytes], A[float, str, int, bytes])
 
-            E = Tuple[List[T], A[Unpack[Ts]], List[T2]]
+            H = tuple[list[T], A[*Ts], list[T2]]
             with self.assertRaises(TypeError):
-                E[()]
+                H[()]
             with self.assertRaises(TypeError):
-                E[float]
+                H[float]
             if A != Tuple:
-                self.assertEqual(E[float, str],
+                self.assertEqual(H[float, str],
+                                 tuple[list[float], A[()], list[str]])
+            self.assertEqual(H[float, str, int],
+                             tuple[list[float], A[str], list[int]])
+            self.assertEqual(H[float, str, int, bytes],
+                             tuple[list[float], A[str, int], list[bytes]])
+
+            I = Tuple[List[T], A[Unpack[Ts]], List[T2]]
+            with self.assertRaises(TypeError):
+                I[()]
+            with self.assertRaises(TypeError):
+                I[float]
+            if A != Tuple:
+                self.assertEqual(I[float, str],
                                  Tuple[List[float], A[()], List[str]])
-            self.assertEqual(E[float, str, int],
+            self.assertEqual(I[float, str, int],
                              Tuple[List[float], A[str], List[int]])
-            self.assertEqual(E[float, str, int, bytes],
+            self.assertEqual(I[float, str, int, bytes],
                              Tuple[List[float], A[str, int], List[bytes]])
 
     def test_bad_var_substitution(self):
         Ts = TypeVarTuple('Ts')
         T = TypeVar('T')
         T2 = TypeVar('T2')
-        class G(Generic[Unpack[Ts]]): pass
+        class G1(Generic[*Ts]): pass
+        class G2(Generic[Unpack[Ts]]): pass
 
-        for A in G, Tuple, tuple:
+        for A in G1, G2, Tuple, tuple:
             B = A[Ts]
             with self.assertRaises(TypeError):
                 B[int, str]
 
             C = A[T, T2]
+            with self.assertRaises(TypeError):
+                C[*Ts]
             with self.assertRaises(TypeError):
                 C[Unpack[Ts]]
 
-    def test_repr_is_correct(self):
-        Ts = TypeVarTuple('Ts')
-        T = TypeVar('T')
-        T2 = TypeVar('T2')
-        class G(Generic[Unpack[Ts]]): pass
-
-        for A in G, Tuple:
-            B = A[T, Unpack[Ts], str, T2]
+            B = A[T, *Ts, str, T2]
             with self.assertRaises(TypeError):
-                B[int, Unpack[Ts]]
+                B[int, *Ts]
+            with self.assertRaises(TypeError):
+                B[int, *Ts, *Ts]
+
             C = A[T, Unpack[Ts], str, T2]
+            with self.assertRaises(TypeError):
+                C[int, Unpack[Ts]]
             with self.assertRaises(TypeError):
                 C[int, Unpack[Ts], Unpack[Ts]]
 
     def test_repr_is_correct(self):
         Ts = TypeVarTuple('Ts')
+        T = TypeVar('T')
+        T2 = TypeVar('T2')
+
+        class G1(Generic[*Ts]): pass
+        class G2(Generic[Unpack[Ts]]): pass
+
         self.assertEqual(repr(Ts), 'Ts')
+
+        self.assertEqual(repr((*Ts,)[0]), '*Ts')
         self.assertEqual(repr(Unpack[Ts]), '*Ts')
-        self.assertEqual(repr(tuple[Unpack[Ts]]), 'tuple[*Ts]')
+
+        self.assertEqual(repr(tuple[*Ts]), 'tuple[*Ts]')
         self.assertEqual(repr(Tuple[Unpack[Ts]]), 'typing.Tuple[*Ts]')
-        self.assertEqual(repr(Unpack[tuple[Unpack[Ts]]]), '*tuple[*Ts]')
+
+        self.assertEqual(repr(*tuple[*Ts]), '*tuple[*Ts]')
         self.assertEqual(repr(Unpack[Tuple[Unpack[Ts]]]), '*typing.Tuple[*Ts]')
 
     def test_variadic_class_repr_is_correct(self):
         Ts = TypeVarTuple('Ts')
-        class A(Generic[Unpack[Ts]]): pass
+        class A(Generic[*Ts]): pass
+        class B(Generic[Unpack[Ts]]): pass
 
         self.assertEndsWith(repr(A[()]), 'A[()]')
+        self.assertEndsWith(repr(B[()]), 'B[()]')
         self.assertEndsWith(repr(A[float]), 'A[float]')
+        self.assertEndsWith(repr(B[float]), 'B[float]')
         self.assertEndsWith(repr(A[float, str]), 'A[float, str]')
-        self.assertEndsWith(repr(A[Unpack[tuple[int, ...]]]),
+        self.assertEndsWith(repr(B[float, str]), 'B[float, str]')
+
+        self.assertEndsWith(repr(A[*tuple[int, ...]]),
                             'A[*tuple[int, ...]]')
-        self.assertEndsWith(repr(A[float, Unpack[tuple[int, ...]]]),
+        self.assertEndsWith(repr(B[Unpack[Tuple[int, ...]]]),
+                            'B[*typing.Tuple[int, ...]]')
+
+        self.assertEndsWith(repr(A[float, *tuple[int, ...]]),
                             'A[float, *tuple[int, ...]]')
-        self.assertEndsWith(repr(A[Unpack[tuple[int, ...]], str]),
+        self.assertEndsWith(repr(A[float, Unpack[Tuple[int, ...]]]),
+                            'A[float, *typing.Tuple[int, ...]]')
+
+        self.assertEndsWith(repr(A[*tuple[int, ...], str]),
                             'A[*tuple[int, ...], str]')
-        self.assertEndsWith(repr(A[float, Unpack[tuple[int, ...]], str]),
+        self.assertEndsWith(repr(B[Unpack[Tuple[int, ...]], str]),
+                            'B[*typing.Tuple[int, ...], str]')
+
+        self.assertEndsWith(repr(A[float, *tuple[int, ...], str]),
                             'A[float, *tuple[int, ...], str]')
+        self.assertEndsWith(repr(B[float, Unpack[Tuple[int, ...]], str]),
+                            'B[float, *typing.Tuple[int, ...], str]')
 
     def test_variadic_class_alias_repr_is_correct(self):
         Ts = TypeVarTuple('Ts')
         class A(Generic[Unpack[Ts]]): pass
 
-        B = A[Unpack[Ts]]
+        B = A[*Ts]
         self.assertEndsWith(repr(B), 'A[*Ts]')
         self.assertEndsWith(repr(B[()]), 'A[()]')
         self.assertEndsWith(repr(B[float]), 'A[float]')
         self.assertEndsWith(repr(B[float, str]), 'A[float, str]')
 
-        C = A[Unpack[Ts], int]
-        self.assertEndsWith(repr(C), 'A[*Ts, int]')
-        self.assertEndsWith(repr(C[()]), 'A[int]')
-        self.assertEndsWith(repr(C[float]), 'A[float, int]')
-        self.assertEndsWith(repr(C[float, str]), 'A[float, str, int]')
+        C = A[Unpack[Ts]]
+        self.assertEndsWith(repr(C), 'A[*Ts]')
+        self.assertEndsWith(repr(C[()]), 'A[()]')
+        self.assertEndsWith(repr(C[float]), 'A[float]')
+        self.assertEndsWith(repr(C[float, str]), 'A[float, str]')
 
-        D = A[int, Unpack[Ts]]
-        self.assertEndsWith(repr(D), 'A[int, *Ts]')
+        D = A[*Ts, int]
+        self.assertEndsWith(repr(D), 'A[*Ts, int]')
         self.assertEndsWith(repr(D[()]), 'A[int]')
-        self.assertEndsWith(repr(D[float]), 'A[int, float]')
-        self.assertEndsWith(repr(D[float, str]), 'A[int, float, str]')
-
-        E = A[int, Unpack[Ts], str]
-        self.assertEndsWith(repr(E), 'A[int, *Ts, str]')
-        self.assertEndsWith(repr(E[()]), 'A[int, str]')
-        self.assertEndsWith(repr(E[float]), 'A[int, float, str]')
-        self.assertEndsWith(repr(E[float, str]), 'A[int, float, str, str]')
-
-        F = A[Unpack[Ts], Unpack[tuple[str, ...]]]
-        self.assertEndsWith(repr(F), 'A[*Ts, *tuple[str, ...]]')
-        self.assertEndsWith(repr(F[()]), 'A[*tuple[str, ...]]')
-        self.assertEndsWith(repr(F[float]), 'A[float, *tuple[str, ...]]')
-        self.assertEndsWith(repr(F[float, str]), 'A[float, str, *tuple[str, ...]]')
+        self.assertEndsWith(repr(D[float]), 'A[float, int]')
+        self.assertEndsWith(repr(D[float, str]), 'A[float, str, int]')
+
+        E = A[Unpack[Ts], int]
+        self.assertEndsWith(repr(E), 'A[*Ts, int]')
+        self.assertEndsWith(repr(E[()]), 'A[int]')
+        self.assertEndsWith(repr(E[float]), 'A[float, int]')
+        self.assertEndsWith(repr(E[float, str]), 'A[float, str, int]')
+
+        F = A[int, *Ts]
+        self.assertEndsWith(repr(F), 'A[int, *Ts]')
+        self.assertEndsWith(repr(F[()]), 'A[int]')
+        self.assertEndsWith(repr(F[float]), 'A[int, float]')
+        self.assertEndsWith(repr(F[float, str]), 'A[int, float, str]')
+
+        G = A[int, Unpack[Ts]]
+        self.assertEndsWith(repr(G), 'A[int, *Ts]')
+        self.assertEndsWith(repr(G[()]), 'A[int]')
+        self.assertEndsWith(repr(G[float]), 'A[int, float]')
+        self.assertEndsWith(repr(G[float, str]), 'A[int, float, str]')
+
+        H = A[int, *Ts, str]
+        self.assertEndsWith(repr(H), 'A[int, *Ts, str]')
+        self.assertEndsWith(repr(H[()]), 'A[int, str]')
+        self.assertEndsWith(repr(H[float]), 'A[int, float, str]')
+        self.assertEndsWith(repr(H[float, str]), 'A[int, float, str, str]')
+
+        I = A[int, Unpack[Ts], str]
+        self.assertEndsWith(repr(I), 'A[int, *Ts, str]')
+        self.assertEndsWith(repr(I[()]), 'A[int, str]')
+        self.assertEndsWith(repr(I[float]), 'A[int, float, str]')
+        self.assertEndsWith(repr(I[float, str]), 'A[int, float, str, str]')
+
+        J = A[*Ts, *tuple[str, ...]]
+        self.assertEndsWith(repr(J), 'A[*Ts, *tuple[str, ...]]')
+        self.assertEndsWith(repr(J[()]), 'A[*tuple[str, ...]]')
+        self.assertEndsWith(repr(J[float]), 'A[float, *tuple[str, ...]]')
+        self.assertEndsWith(repr(J[float, str]), 'A[float, str, *tuple[str, ...]]')
+
+        K = A[Unpack[Ts], Unpack[Tuple[str, ...]]]
+        self.assertEndsWith(repr(K), 'A[*Ts, *typing.Tuple[str, ...]]')
+        self.assertEndsWith(repr(K[()]), 'A[*typing.Tuple[str, ...]]')
+        self.assertEndsWith(repr(K[float]), 'A[float, *typing.Tuple[str, ...]]')
+        self.assertEndsWith(repr(K[float, str]), 'A[float, str, *typing.Tuple[str, ...]]')
 
     def test_cannot_subclass(self):
         with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE):
@@ -1037,39 +1134,69 @@ class C(TypeVarTuple): pass
         with self.assertRaisesRegex(TypeError,
                 CANNOT_SUBCLASS_INSTANCE % 'TypeVarTuple'):
             class C(Ts): pass
-        with self.assertRaisesRegex(TypeError, r'Cannot subclass \*Ts'):
-            class C(*Ts): pass
         with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE):
             class C(type(Unpack)): pass
+        with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE):
+            class C(type(*Ts)): pass
         with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE):
             class C(type(Unpack[Ts])): pass
         with self.assertRaisesRegex(TypeError,
                                     r'Cannot subclass typing\.Unpack'):
             class C(Unpack): pass
+        with self.assertRaisesRegex(TypeError, r'Cannot subclass \*Ts'):
+            class C(*Ts): pass
         with self.assertRaisesRegex(TypeError, r'Cannot subclass \*Ts'):
             class C(Unpack[Ts]): pass
 
     def test_variadic_class_args_are_correct(self):
         T = TypeVar('T')
         Ts = TypeVarTuple('Ts')
-        class A(Generic[Unpack[Ts]]): pass
-        B = A[()]
-        self.assertEqual(B.__args__, ())
-        C = A[int]
-        self.assertEqual(C.__args__, (int,))
-        D = A[int, str]
-        self.assertEqual(D.__args__, (int, str))
-        E = A[T]
-        self.assertEqual(E.__args__, (T,))
-        F = A[Unpack[Ts]]
-        self.assertEqual(F.__args__, (Unpack[Ts],))
-        G = A[T, Unpack[Ts]]
-        self.assertEqual(G.__args__, (T, Unpack[Ts]))
-        H = A[Unpack[Ts], T]
-        self.assertEqual(H.__args__, (Unpack[Ts], T))
+        class A(Generic[*Ts]): pass
+        class B(Generic[Unpack[Ts]]): pass
+
+        C = A[()]
+        D = B[()]
+        self.assertEqual(C.__args__, ())
+        self.assertEqual(D.__args__, ())
+
+        E = A[int]
+        F = B[int]
+        self.assertEqual(E.__args__, (int,))
+        self.assertEqual(F.__args__, (int,))
+
+        G = A[int, str]
+        H = B[int, str]
+        self.assertEqual(G.__args__, (int, str))
+        self.assertEqual(H.__args__, (int, str))
+
+        I = A[T]
+        J = B[T]
+        self.assertEqual(I.__args__, (T,))
+        self.assertEqual(J.__args__, (T,))
+
+        K = A[*Ts]
+        L = B[Unpack[Ts]]
+        self.assertEqual(K.__args__, (*Ts,))
+        self.assertEqual(L.__args__, (Unpack[Ts],))
+
+        M = A[T, *Ts]
+        N = B[T, Unpack[Ts]]
+        self.assertEqual(M.__args__, (T, *Ts))
+        self.assertEqual(N.__args__, (T, Unpack[Ts]))
+
+        O = A[*Ts, T]
+        P = B[Unpack[Ts], T]
+        self.assertEqual(O.__args__, (*Ts, T))
+        self.assertEqual(P.__args__, (Unpack[Ts], T))
 
     def test_variadic_class_origin_is_correct(self):
         Ts = TypeVarTuple('Ts')
+
+        class C(Generic[*Ts]): pass
+        self.assertIs(C[int].__origin__, C)
+        self.assertIs(C[T].__origin__, C)
+        self.assertIs(C[Unpack[Ts]].__origin__, C)
+
         class D(Generic[Unpack[Ts]]): pass
         self.assertIs(D[int].__origin__, D)
         self.assertIs(D[T].__origin__, D)
@@ -1078,21 +1205,21 @@ class D(Generic[Unpack[Ts]]): pass
     def test_tuple_args_are_correct(self):
         Ts = TypeVarTuple('Ts')
 
-        self.assertEqual(tuple[Unpack[Ts]].__args__, (Unpack[Ts],))
+        self.assertEqual(tuple[*Ts].__args__, (*Ts,))
         self.assertEqual(Tuple[Unpack[Ts]].__args__, (Unpack[Ts],))
 
-        self.assertEqual(tuple[Unpack[Ts], int].__args__, (Unpack[Ts], int))
+        self.assertEqual(tuple[*Ts, int].__args__, (*Ts, int))
         self.assertEqual(Tuple[Unpack[Ts], int].__args__, (Unpack[Ts], int))
 
-        self.assertEqual(tuple[int, Unpack[Ts]].__args__, (int, Unpack[Ts]))
+        self.assertEqual(tuple[int, *Ts].__args__, (int, *Ts))
         self.assertEqual(Tuple[int, Unpack[Ts]].__args__, (int, Unpack[Ts]))
 
-        self.assertEqual(tuple[int, Unpack[Ts], str].__args__,
-                         (int, Unpack[Ts], str))
+        self.assertEqual(tuple[int, *Ts, str].__args__,
+                         (int, *Ts, str))
         self.assertEqual(Tuple[int, Unpack[Ts], str].__args__,
                          (int, Unpack[Ts], str))
 
-        self.assertEqual(tuple[Unpack[Ts], int].__args__, (Unpack[Ts], int))
+        self.assertEqual(tuple[*Ts, int].__args__, (*Ts, int))
         self.assertEqual(Tuple[Unpack[Ts]].__args__, (Unpack[Ts],))
 
     def test_callable_args_are_correct(self):
@@ -1102,63 +1229,97 @@ def test_callable_args_are_correct(self):
 
         # TypeVarTuple in the arguments
 
-        a = Callable[[Unpack[Ts]], None]
-        self.assertEqual(a.__args__, (Unpack[Ts], type(None)))
+        a = Callable[[*Ts], None]
+        b = Callable[[Unpack[Ts]], None]
+        self.assertEqual(a.__args__, (*Ts, type(None)))
+        self.assertEqual(b.__args__, (Unpack[Ts], type(None)))
 
-        b = Callable[[int, Unpack[Ts]], None]
-        self.assertEqual(b.__args__, (int, Unpack[Ts], type(None)))
+        c = Callable[[int, *Ts], None]
+        d = Callable[[int, Unpack[Ts]], None]
+        self.assertEqual(c.__args__, (int, *Ts, type(None)))
+        self.assertEqual(d.__args__, (int, Unpack[Ts], type(None)))
 
-        c = Callable[[Unpack[Ts], int], None]
-        self.assertEqual(c.__args__, (Unpack[Ts], int, type(None)))
+        e = Callable[[*Ts, int], None]
+        f = Callable[[Unpack[Ts], int], None]
+        self.assertEqual(e.__args__, (*Ts, int, type(None)))
+        self.assertEqual(f.__args__, (Unpack[Ts], int, type(None)))
 
-        d = Callable[[str, Unpack[Ts], int], None]
-        self.assertEqual(d.__args__, (str, Unpack[Ts], int, type(None)))
+        g = Callable[[str, *Ts, int], None]
+        h = Callable[[str, Unpack[Ts], int], None]
+        self.assertEqual(g.__args__, (str, *Ts, int, type(None)))
+        self.assertEqual(h.__args__, (str, Unpack[Ts], int, type(None)))
 
         # TypeVarTuple as the return
 
-        e = Callable[[None], Unpack[Ts]]
-        self.assertEqual(e.__args__, (type(None), Unpack[Ts]))
+        i = Callable[[None], *Ts]
+        j = Callable[[None], Unpack[Ts]]
+        self.assertEqual(i.__args__, (type(None), *Ts))
+        self.assertEqual(i.__args__, (type(None), Unpack[Ts]))
 
-        f = Callable[[None], tuple[int, Unpack[Ts]]]
-        self.assertEqual(f.__args__, (type(None), tuple[int, Unpack[Ts]]))
+        k = Callable[[None], tuple[int, *Ts]]
+        l = Callable[[None], Tuple[int, Unpack[Ts]]]
+        self.assertEqual(k.__args__, (type(None), tuple[int, *Ts]))
+        self.assertEqual(l.__args__, (type(None), Tuple[int, Unpack[Ts]]))
 
-        g = Callable[[None], tuple[Unpack[Ts], int]]
-        self.assertEqual(g.__args__, (type(None), tuple[Unpack[Ts], int]))
+        m = Callable[[None], tuple[*Ts, int]]
+        n = Callable[[None], Tuple[Unpack[Ts], int]]
+        self.assertEqual(m.__args__, (type(None), tuple[*Ts, int]))
+        self.assertEqual(n.__args__, (type(None), Tuple[Unpack[Ts], int]))
 
-        h = Callable[[None], tuple[str, Unpack[Ts], int]]
-        self.assertEqual(h.__args__, (type(None), tuple[str, Unpack[Ts], int]))
+        o = Callable[[None], tuple[str, *Ts, int]]
+        p = Callable[[None], Tuple[str, Unpack[Ts], int]]
+        self.assertEqual(o.__args__, (type(None), tuple[str, *Ts, int]))
+        self.assertEqual(p.__args__, (type(None), Tuple[str, Unpack[Ts], int]))
 
         # TypeVarTuple in both
 
-        i = Callable[[Unpack[Ts]], Unpack[Ts]]
-        self.assertEqual(i.__args__, (Unpack[Ts], Unpack[Ts]))
+        q = Callable[[*Ts], *Ts]
+        r = Callable[[Unpack[Ts]], Unpack[Ts]]
+        self.assertEqual(q.__args__, (*Ts, *Ts))
+        self.assertEqual(r.__args__, (Unpack[Ts], Unpack[Ts]))
 
-        j = Callable[[Unpack[Ts1]], Unpack[Ts2]]
-        self.assertEqual(j.__args__, (Unpack[Ts1], Unpack[Ts2]))
+        s = Callable[[*Ts1], *Ts2]
+        u = Callable[[Unpack[Ts1]], Unpack[Ts2]]
+        self.assertEqual(s.__args__, (*Ts1, *Ts2))
+        self.assertEqual(u.__args__, (Unpack[Ts1], Unpack[Ts2]))
 
     def test_variadic_class_with_duplicate_typevartuples_fails(self):
         Ts1 = TypeVarTuple('Ts1')
         Ts2 = TypeVarTuple('Ts2')
+
+        with self.assertRaises(TypeError):
+            class C(Generic[*Ts1, *Ts1]): pass
         with self.assertRaises(TypeError):
             class C(Generic[Unpack[Ts1], Unpack[Ts1]]): pass
+
+        with self.assertRaises(TypeError):
+            class C(Generic[*Ts1, *Ts2, *Ts1]): pass
         with self.assertRaises(TypeError):
             class C(Generic[Unpack[Ts1], Unpack[Ts2], Unpack[Ts1]]): pass
 
     def test_type_concatenation_in_variadic_class_argument_list_succeeds(self):
         Ts = TypeVarTuple('Ts')
         class C(Generic[Unpack[Ts]]): pass
+
+        C[int, *Ts]
         C[int, Unpack[Ts]]
+
+        C[*Ts, int]
         C[Unpack[Ts], int]
+
+        C[int, *Ts, str]
         C[int, Unpack[Ts], str]
+
+        C[int, bool, *Ts, float, str]
         C[int, bool, Unpack[Ts], float, str]
 
     def test_type_concatenation_in_tuple_argument_list_succeeds(self):
         Ts = TypeVarTuple('Ts')
 
-        tuple[int, Unpack[Ts]]
-        tuple[Unpack[Ts], int]
-        tuple[int, Unpack[Ts], str]
-        tuple[int, bool, Unpack[Ts], float, str]
+        tuple[int, *Ts]
+        tuple[*Ts, int]
+        tuple[int, *Ts, str]
+        tuple[int, bool, *Ts, float, str]
 
         Tuple[int, Unpack[Ts]]
         Tuple[Unpack[Ts], int]
@@ -1172,6 +1333,8 @@ class C(Generic[Ts]): pass
 
     def test_variadic_class_definition_using_concrete_types_fails(self):
         Ts = TypeVarTuple('Ts')
+        with self.assertRaises(TypeError):
+            class F(Generic[*Ts, int]): pass
         with self.assertRaises(TypeError):
             class E(Generic[Unpack[Ts], int]): pass
 
@@ -1180,32 +1343,50 @@ def test_variadic_class_with_2_typevars_accepts_2_or_more_args(self):
         T1 = TypeVar('T1')
         T2 = TypeVar('T2')
 
-        class A(Generic[T1, T2, Unpack[Ts]]): pass
+        class A(Generic[T1, T2, *Ts]): pass
         A[int, str]
         A[int, str, float]
         A[int, str, float, bool]
 
-        class B(Generic[T1, Unpack[Ts], T2]): pass
+        class B(Generic[T1, T2, Unpack[Ts]]): pass
         B[int, str]
         B[int, str, float]
         B[int, str, float, bool]
 
-        class C(Generic[Unpack[Ts], T1, T2]): pass
+        class C(Generic[T1, *Ts, T2]): pass
         C[int, str]
         C[int, str, float]
         C[int, str, float, bool]
 
+        class D(Generic[T1, Unpack[Ts], T2]): pass
+        D[int, str]
+        D[int, str, float]
+        D[int, str, float, bool]
+
+        class E(Generic[*Ts, T1, T2]): pass
+        E[int, str]
+        E[int, str, float]
+        E[int, str, float, bool]
+
+        class F(Generic[Unpack[Ts], T1, T2]): pass
+        F[int, str]
+        F[int, str, float]
+        F[int, str, float, bool]
+
     def test_variadic_args_annotations_are_correct(self):
         Ts = TypeVarTuple('Ts')
+
         def f(*args: Unpack[Ts]): pass
+        def g(*args: *Ts): pass
         self.assertEqual(f.__annotations__, {'args': Unpack[Ts]})
+        self.assertEqual(g.__annotations__, {'args': (*Ts,)[0]})
 
     def test_variadic_args_with_ellipsis_annotations_are_correct(self):
         Ts = TypeVarTuple('Ts')
 
-        def a(*args: Unpack[tuple[int, ...]]): pass
+        def a(*args: *tuple[int, ...]): pass
         self.assertEqual(a.__annotations__,
-                         {'args': Unpack[tuple[int, ...]]})
+                         {'args': (*tuple[int, ...],)[0]})
 
         def b(*args: Unpack[Tuple[int, ...]]): pass
         self.assertEqual(b.__annotations__,
@@ -1214,30 +1395,30 @@ def b(*args: Unpack[Tuple[int, ...]]): pass
     def test_concatenation_in_variadic_args_annotations_are_correct(self):
         Ts = TypeVarTuple('Ts')
 
-        # Unpacking using `Unpack`, native `tuple` type
+        # Unpacking using `*`, native `tuple` type
 
-        def a(*args: Unpack[tuple[int, Unpack[Ts]]]): pass
+        def a(*args: *tuple[int, *Ts]): pass
         self.assertEqual(
             a.__annotations__,
-            {'args': Unpack[tuple[int, Unpack[Ts]]]},
+            {'args': (*tuple[int, *Ts],)[0]},
         )
 
-        def b(*args: Unpack[tuple[Unpack[Ts], int]]): pass
+        def b(*args: *tuple[*Ts, int]): pass
         self.assertEqual(
             b.__annotations__,
-            {'args': Unpack[tuple[Unpack[Ts], int]]},
+            {'args': (*tuple[*Ts, int],)[0]},
         )
 
-        def c(*args: Unpack[tuple[str, Unpack[Ts], int]]): pass
+        def c(*args: *tuple[str, *Ts, int]): pass
         self.assertEqual(
             c.__annotations__,
-            {'args': Unpack[tuple[str, Unpack[Ts], int]]},
+            {'args': (*tuple[str, *Ts, int],)[0]},
         )
 
-        def d(*args: Unpack[tuple[int, bool, Unpack[Ts], float, str]]): pass
+        def d(*args: *tuple[int, bool, *Ts, float, str]): pass
         self.assertEqual(
             d.__annotations__,
-            {'args': Unpack[tuple[int, bool, Unpack[Ts], float, str]]},
+            {'args': (*tuple[int, bool, *Ts, float, str],)[0]},
         )
 
         # Unpacking using `Unpack`, `Tuple` type from typing.py
@@ -1268,47 +1449,78 @@ def h(*args: Unpack[Tuple[int, bool, Unpack[Ts], float, str]]): pass
 
     def test_variadic_class_same_args_results_in_equalty(self):
         Ts = TypeVarTuple('Ts')
-        class C(Generic[Unpack[Ts]]): pass
+        class C(Generic[*Ts]): pass
+        class D(Generic[Unpack[Ts]]): pass
 
         self.assertEqual(C[int], C[int])
+        self.assertEqual(D[int], D[int])
 
         Ts1 = TypeVarTuple('Ts1')
         Ts2 = TypeVarTuple('Ts2')
+
         self.assertEqual(
-            C[Unpack[Ts1]],
-            C[Unpack[Ts1]],
+            C[*Ts1],
+            C[*Ts1],
         )
         self.assertEqual(
-            C[Unpack[Ts1], Unpack[Ts2]],
-            C[Unpack[Ts1], Unpack[Ts2]],
+            D[Unpack[Ts1]],
+            D[Unpack[Ts1]],
+        )
+
+        self.assertEqual(
+            C[*Ts1, *Ts2],
+            C[*Ts1, *Ts2],
+        )
+        self.assertEqual(
+            D[Unpack[Ts1], Unpack[Ts2]],
+            D[Unpack[Ts1], Unpack[Ts2]],
+        )
+
+        self.assertEqual(
+            C[int, *Ts1, *Ts2],
+            C[int, *Ts1, *Ts2],
         )
         self.assertEqual(
-            C[int, Unpack[Ts1], Unpack[Ts2]],
-            C[int, Unpack[Ts1], Unpack[Ts2]],
+            D[int, Unpack[Ts1], Unpack[Ts2]],
+            D[int, Unpack[Ts1], Unpack[Ts2]],
         )
 
     def test_variadic_class_arg_ordering_matters(self):
         Ts = TypeVarTuple('Ts')
-        class C(Generic[Unpack[Ts]]): pass
+        class C(Generic[*Ts]): pass
+        class D(Generic[Unpack[Ts]]): pass
 
         self.assertNotEqual(
             C[int, str],
             C[str, int],
         )
+        self.assertNotEqual(
+            D[int, str],
+            D[str, int],
+        )
 
         Ts1 = TypeVarTuple('Ts1')
         Ts2 = TypeVarTuple('Ts2')
+
+        self.assertNotEqual(
+            C[*Ts1, *Ts2],
+            C[*Ts2, *Ts1],
+        )
         self.assertNotEqual(
-            C[Unpack[Ts1], Unpack[Ts2]],
-            C[Unpack[Ts2], Unpack[Ts1]],
+            D[Unpack[Ts1], Unpack[Ts2]],
+            D[Unpack[Ts2], Unpack[Ts1]],
         )
 
     def test_variadic_class_arg_typevartuple_identity_matters(self):
         Ts = TypeVarTuple('Ts')
-        class C(Generic[Unpack[Ts]]): pass
         Ts1 = TypeVarTuple('Ts1')
         Ts2 = TypeVarTuple('Ts2')
-        self.assertNotEqual(C[Unpack[Ts1]], C[Unpack[Ts2]])
+
+        class C(Generic[*Ts]): pass
+        class D(Generic[Unpack[Ts]]): pass
+
+        self.assertNotEqual(C[*Ts1], C[*Ts2])
+        self.assertNotEqual(D[Unpack[Ts1]], D[Unpack[Ts2]])
 
 
 class TypeVarTuplePicklingTests(BaseTestCase):
@@ -1328,10 +1540,15 @@ def test_pickling_then_unpickling_results_in_same_identity(self, proto):
     def test_pickling_then_unpickling_unpacked_results_in_same_identity(self, proto):
         global global_Ts  # See explanation at start of class.
         global_Ts = TypeVarTuple('global_Ts')
-        unpacked1 = Unpack[global_Ts]
+
+        unpacked1 = (*global_Ts,)[0]
         unpacked2 = pickle.loads(pickle.dumps(unpacked1, proto))
         self.assertIs(unpacked1, unpacked2)
 
+        unpacked3 = Unpack[global_Ts]
+        unpacked4 = pickle.loads(pickle.dumps(unpacked3, proto))
+        self.assertIs(unpacked3, unpacked4)
+
     @all_pickle_protocols
     def test_pickling_then_unpickling_tuple_with_typevartuple_equality(
             self, proto
@@ -1340,17 +1557,19 @@ def test_pickling_then_unpickling_tuple_with_typevartuple_equality(
         global_T = TypeVar('global_T')
         global_Ts = TypeVarTuple('global_Ts')
 
-        a1 = Tuple[Unpack[global_Ts]]
-        a2 = pickle.loads(pickle.dumps(a1, proto))
-        self.assertEqual(a1, a2)
+        tuples = [
+            tuple[*global_Ts],
+            Tuple[Unpack[global_Ts]],
 
-        a1 = Tuple[T, Unpack[global_Ts]]
-        a2 = pickle.loads(pickle.dumps(a1, proto))
-        self.assertEqual(a1, a2)
+            tuple[T, *global_Ts],
+            Tuple[T, Unpack[global_Ts]],
 
-        a1 = Tuple[int, Unpack[global_Ts]]
-        a2 = pickle.loads(pickle.dumps(a1, proto))
-        self.assertEqual(a1, a2)
+            tuple[int, *global_Ts],
+            Tuple[int, Unpack[global_Ts]],
+        ]
+        for t in tuples:
+            t2 = pickle.loads(pickle.dumps(t, proto))
+            self.assertEqual(t, t2)
 
 
 class UnionTests(BaseTestCase):
@@ -4936,6 +5155,7 @@ def h(x: collections.abc.Callable[P, int]): ...
 class GetUtilitiesTestCase(TestCase):
     def test_get_origin(self):
         T = TypeVar('T')
+        Ts = TypeVarTuple('Ts')
         P = ParamSpec('P')
         class C(Generic[T]): pass
         self.assertIs(get_origin(C[int]), C)
@@ -4959,6 +5179,10 @@ class C(Generic[T]): pass
         self.assertIs(get_origin(P.kwargs), P)
         self.assertIs(get_origin(Required[int]), Required)
         self.assertIs(get_origin(NotRequired[int]), NotRequired)
+        self.assertIs(get_origin((*Ts,)[0]), Unpack)
+        self.assertIs(get_origin(Unpack[Ts]), Unpack)
+        self.assertIs(get_origin((*tuple[*Ts],)[0]), tuple)
+        self.assertIs(get_origin(Unpack[Tuple[Unpack[Ts]]]), Unpack)
 
     def test_get_args(self):
         T = TypeVar('T')
@@ -5020,8 +5244,12 @@ class C(Generic[T]): pass
         self.assertEqual(get_args(TypeGuard[int]), (int,))
         Ts = TypeVarTuple('Ts')
         self.assertEqual(get_args(Ts), ())
+        self.assertEqual(get_args((*Ts,)[0]), (Ts,))
         self.assertEqual(get_args(Unpack[Ts]), (Ts,))
+        self.assertEqual(get_args(tuple[*Ts]), (*Ts,))
         self.assertEqual(get_args(tuple[Unpack[Ts]]), (Unpack[Ts],))
+        self.assertEqual(get_args((*tuple[*Ts],)[0]), (*Ts,))
+        self.assertEqual(get_args(Unpack[tuple[Unpack[Ts]]]), (tuple[Unpack[Ts]],))
 
 
 class CollectionsAbcTests(BaseTestCase):
@@ -6699,69 +6927,112 @@ def test_typevar_subst(self):
         T1 = TypeVar('T1')
         T2 = TypeVar('T2')
 
-        A = Annotated[Tuple[Unpack[Ts]], dec]
-        self.assertEqual(A[int], Annotated[Tuple[int], dec])
-        self.assertEqual(A[str, int], Annotated[Tuple[str, int], dec])
+        A = Annotated[tuple[*Ts], dec]
+        self.assertEqual(A[int], Annotated[tuple[int], dec])
+        self.assertEqual(A[str, int], Annotated[tuple[str, int], dec])
         with self.assertRaises(TypeError):
-            Annotated[Unpack[Ts], dec]
+            Annotated[*Ts, dec]
 
-        B = Annotated[Tuple[T, Unpack[Ts]], dec]
+        B = Annotated[Tuple[Unpack[Ts]], dec]
         self.assertEqual(B[int], Annotated[Tuple[int], dec])
-        self.assertEqual(B[int, str], Annotated[Tuple[int, str], dec])
-        self.assertEqual(
-            B[int, str, float],
-            Annotated[Tuple[int, str, float], dec]
-        )
+        self.assertEqual(B[str, int], Annotated[Tuple[str, int], dec])
         with self.assertRaises(TypeError):
-            B[()]
+            Annotated[Unpack[Ts], dec]
 
-        C = Annotated[Tuple[Unpack[Ts], T], dec]
-        self.assertEqual(C[int], Annotated[Tuple[int], dec])
-        self.assertEqual(C[int, str], Annotated[Tuple[int, str], dec])
+        C = Annotated[tuple[T, *Ts], dec]
+        self.assertEqual(C[int], Annotated[tuple[int], dec])
+        self.assertEqual(C[int, str], Annotated[tuple[int, str], dec])
         self.assertEqual(
             C[int, str, float],
-            Annotated[Tuple[int, str, float], dec]
+            Annotated[tuple[int, str, float], dec]
         )
         with self.assertRaises(TypeError):
             C[()]
 
-        D = Annotated[Tuple[T1, Unpack[Ts], T2], dec]
+        D = Annotated[Tuple[T, Unpack[Ts]], dec]
+        self.assertEqual(D[int], Annotated[Tuple[int], dec])
         self.assertEqual(D[int, str], Annotated[Tuple[int, str], dec])
         self.assertEqual(
             D[int, str, float],
             Annotated[Tuple[int, str, float], dec]
         )
+        with self.assertRaises(TypeError):
+            D[()]
+
+        E = Annotated[tuple[*Ts, T], dec]
+        self.assertEqual(E[int], Annotated[tuple[int], dec])
+        self.assertEqual(E[int, str], Annotated[tuple[int, str], dec])
         self.assertEqual(
-            D[int, str, bool, float],
-            Annotated[Tuple[int, str, bool, float], dec]
+            E[int, str, float],
+            Annotated[tuple[int, str, float], dec]
         )
         with self.assertRaises(TypeError):
-            D[int]
-
-        # Now let's try creating an alias from an alias.
+            E[()]
 
-        Ts2 = TypeVarTuple('Ts2')
-        T3 = TypeVar('T3')
-        T4 = TypeVar('T4')
+        F = Annotated[Tuple[Unpack[Ts], T], dec]
+        self.assertEqual(F[int], Annotated[Tuple[int], dec])
+        self.assertEqual(F[int, str], Annotated[Tuple[int, str], dec])
+        self.assertEqual(
+            F[int, str, float],
+            Annotated[Tuple[int, str, float], dec]
+        )
+        with self.assertRaises(TypeError):
+            F[()]
 
-        E = D[T3, Unpack[Ts2], T4]
+        G = Annotated[tuple[T1, *Ts, T2], dec]
+        self.assertEqual(G[int, str], Annotated[tuple[int, str], dec])
         self.assertEqual(
-            E,
-            Annotated[Tuple[T3, Unpack[Ts2], T4], dec]
+            G[int, str, float],
+            Annotated[tuple[int, str, float], dec]
         )
         self.assertEqual(
-            E[int, str], Annotated[Tuple[int, str], dec]
+            G[int, str, bool, float],
+            Annotated[tuple[int, str, bool, float], dec]
         )
+        with self.assertRaises(TypeError):
+            G[int]
+
+        H = Annotated[Tuple[T1, Unpack[Ts], T2], dec]
+        self.assertEqual(H[int, str], Annotated[Tuple[int, str], dec])
         self.assertEqual(
-            E[int, str, float],
+            H[int, str, float],
             Annotated[Tuple[int, str, float], dec]
         )
         self.assertEqual(
-            E[int, str, bool, float],
+            H[int, str, bool, float],
             Annotated[Tuple[int, str, bool, float], dec]
         )
         with self.assertRaises(TypeError):
-            E[int]
+            H[int]
+
+        # Now let's try creating an alias from an alias.
+
+        Ts2 = TypeVarTuple('Ts2')
+        T3 = TypeVar('T3')
+        T4 = TypeVar('T4')
+
+        # G is Annotated[tuple[T1, *Ts, T2], dec].
+        I = G[T3, *Ts2, T4]
+        J = G[T3, Unpack[Ts2], T4]
+
+        for x, y in [
+            (I,                  Annotated[tuple[T3, *Ts2, T4], dec]),
+            (J,                  Annotated[tuple[T3, Unpack[Ts2], T4], dec]),
+            (I[int, str],        Annotated[tuple[int, str], dec]),
+            (J[int, str],        Annotated[tuple[int, str], dec]),
+            (I[int, str, float], Annotated[tuple[int, str, float], dec]),
+            (J[int, str, float], Annotated[tuple[int, str, float], dec]),
+            (I[int, str, bool, float],
+                                 Annotated[tuple[int, str, bool, float], dec]),
+            (J[int, str, bool, float],
+                                 Annotated[tuple[int, str, bool, float], dec]),
+        ]:
+            self.assertEqual(x, y)
+
+        with self.assertRaises(TypeError):
+            I[int]
+        with self.assertRaises(TypeError):
+            J[int]
 
     def test_annotated_in_other_types(self):
         X = List[Annotated[T, 5]]
diff --git a/Misc/NEWS.d/next/Tests/2022-10-15-07-46-48.gh-issue-87390.asR-Zo.rst b/Misc/NEWS.d/next/Tests/2022-10-15-07-46-48.gh-issue-87390.asR-Zo.rst
new file mode 100644
index 000000000000..181e12c7430b
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2022-10-15-07-46-48.gh-issue-87390.asR-Zo.rst
@@ -0,0 +1 @@
+Add tests for star-unpacking with PEP 646, and some other miscellaneous PEP 646 tests.



More information about the Python-checkins mailing list