mirror of https://github.com/python/cpython
gh-118772: Allow TypeVars without a default to follow those with a default when constructing aliases (#118774)
This commit is contained in:
parent
6d419db10c
commit
aac6b019fe
|
@ -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])
|
||||||
|
|
|
@ -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__
|
||||||
|
|
||||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue