]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-118772: Allow TypeVars without a default to follow those with a default when const...
authorJelle Zijlstra <jelle.zijlstra@gmail.com>
Wed, 8 May 2024 16:54:51 +0000 (09:54 -0700)
committerGitHub <noreply@github.com>
Wed, 8 May 2024 16:54:51 +0000 (09:54 -0700)
Lib/test/test_typing.py
Lib/typing.py
Misc/NEWS.d/next/Library/2024-05-08-09-21-49.gh-issue-118772.c16E8X.rst [new file with mode: 0644]

index bd116bb1ab721340c44f542299d3f3770e66d321..fff81f7997764d585b9d203ce5c05e3163746e2d 100644 (file)
@@ -668,6 +668,23 @@ class TypeParameterDefaultsTests(BaseTestCase):
         with self.assertRaises(TypeError):
             class Y(Generic[*Ts_default, T]): ...
 
+    def test_allow_default_after_non_default_in_alias(self):
+        T_default = TypeVar('T_default', default=int)
+        T = TypeVar('T')
+        Ts = TypeVarTuple('Ts')
+
+        a1 = Callable[[T_default], T]
+        self.assertEqual(a1.__args__, (T_default, T))
+
+        a2 = dict[T_default, T]
+        self.assertEqual(a2.__args__, (T_default, T))
+
+        a3 = typing.Dict[T_default, T]
+        self.assertEqual(a3.__args__, (T_default, T))
+
+        a4 = Callable[*Ts, T]
+        self.assertEqual(a4.__args__, (*Ts, T))
+
     def test_paramspec_specialization(self):
         T = TypeVar("T")
         P = ParamSpec('P', default=[str, int])
index 8e61f50477bcc21945ad086daf36ff7498e5b718..c8649e312ddb000f0e565c8d64e834231ac3c9e7 100644 (file)
@@ -257,7 +257,7 @@ def _type_repr(obj):
     return repr(obj)
 
 
-def _collect_parameters(args):
+def _collect_parameters(args, *, enforce_default_ordering: bool = True):
     """Collect all type variables and parameter specifications in args
     in order of first appearance (lexicographic order).
 
@@ -286,15 +286,16 @@ def _collect_parameters(args):
                         parameters.append(collected)
         elif hasattr(t, '__typing_subst__'):
             if t not in parameters:
-                if type_var_tuple_encountered and t.has_default():
-                    raise TypeError('Type parameter with a default'
-                                    ' follows TypeVarTuple')
+                if enforce_default_ordering:
+                    if type_var_tuple_encountered and t.has_default():
+                        raise TypeError('Type parameter with a default'
+                                        ' follows TypeVarTuple')
 
-                if t.has_default():
-                    default_encountered = True
-                elif default_encountered:
-                    raise TypeError(f'Type parameter {t!r} without a default'
-                                    ' follows type parameter with a default')
+                    if t.has_default():
+                        default_encountered = True
+                    elif default_encountered:
+                        raise TypeError(f'Type parameter {t!r} without a default'
+                                        ' follows type parameter with a default')
 
                 parameters.append(t)
         else:
@@ -1416,7 +1417,11 @@ class _GenericAlias(_BaseGenericAlias, _root=True):
             args = (args,)
         self.__args__ = tuple(... if a is _TypingEllipsis else
                               a for a in args)
-        self.__parameters__ = _collect_parameters(args)
+        enforce_default_ordering = origin in (Generic, Protocol)
+        self.__parameters__ = _collect_parameters(
+            args,
+            enforce_default_ordering=enforce_default_ordering,
+        )
         if not name:
             self.__module__ = origin.__module__
 
diff --git a/Misc/NEWS.d/next/Library/2024-05-08-09-21-49.gh-issue-118772.c16E8X.rst b/Misc/NEWS.d/next/Library/2024-05-08-09-21-49.gh-issue-118772.c16E8X.rst
new file mode 100644 (file)
index 0000000..474454b
--- /dev/null
@@ -0,0 +1,2 @@
+Allow :class:`typing.TypeVar` instances without a default to follow
+instances without a default in some cases. Patch by Jelle Zijlstra.