gh-118772: Allow TypeVars without a default to follow those with a default when constructing aliases (#118774)

This commit is contained in:
Jelle Zijlstra 2024-05-08 09:54:51 -07:00 committed by GitHub
parent 6d419db10c
commit aac6b019fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 34 additions and 10 deletions

View File

@ -668,6 +668,23 @@ class TypeParameterDefaultsTests(BaseTestCase):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
class Y(Generic[*Ts_default, T]): ... 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): def test_paramspec_specialization(self):
T = TypeVar("T") T = TypeVar("T")
P = ParamSpec('P', default=[str, int]) P = ParamSpec('P', default=[str, int])

View File

@ -257,7 +257,7 @@ def _type_repr(obj):
return 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 """Collect all type variables and parameter specifications in args
in order of first appearance (lexicographic order). in order of first appearance (lexicographic order).
@ -286,15 +286,16 @@ def _collect_parameters(args):
parameters.append(collected) parameters.append(collected)
elif hasattr(t, '__typing_subst__'): elif hasattr(t, '__typing_subst__'):
if t not in parameters: if t not in parameters:
if type_var_tuple_encountered and t.has_default(): if enforce_default_ordering:
raise TypeError('Type parameter with a default' if type_var_tuple_encountered and t.has_default():
' follows TypeVarTuple') raise TypeError('Type parameter with a default'
' follows TypeVarTuple')
if t.has_default(): if t.has_default():
default_encountered = True default_encountered = True
elif default_encountered: elif default_encountered:
raise TypeError(f'Type parameter {t!r} without a default' raise TypeError(f'Type parameter {t!r} without a default'
' follows type parameter with a default') ' follows type parameter with a default')
parameters.append(t) parameters.append(t)
else: else:
@ -1416,7 +1417,11 @@ class _GenericAlias(_BaseGenericAlias, _root=True):
args = (args,) args = (args,)
self.__args__ = tuple(... if a is _TypingEllipsis else self.__args__ = tuple(... if a is _TypingEllipsis else
a for a in args) 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: if not name:
self.__module__ = origin.__module__ self.__module__ = origin.__module__

View File

@ -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.