From aac6b019fe91e2f9f7a955d4fc4db5d5efd968c9 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 8 May 2024 09:54:51 -0700 Subject: gh-118772: Allow TypeVars without a default to follow those with a default when constructing aliases (#118774) --- Lib/test/test_typing.py | 17 +++++++++++++++ Lib/typing.py | 25 +++++++++++++--------- .../2024-05-08-09-21-49.gh-issue-118772.c16E8X.rst | 2 ++ 3 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-05-08-09-21-49.gh-issue-118772.c16E8X.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index bd116bb..fff81f7 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -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]) diff --git a/Lib/typing.py b/Lib/typing.py index 8e61f50..c8649e3 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -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 index 0000000..474454b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-05-08-09-21-49.gh-issue-118772.c16E8X.rst @@ -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. -- cgit v0.12