mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
bpo-44791: Fix substitution of ParamSpec in Concatenate with different parameter expressions (GH-27518)
* Substitution with a list of types returns now a tuple of types. * Substitution with Concatenate returns now a Concatenate with concatenated lists of arguments. * Substitution with Ellipsis is not supported.
This commit is contained in:
parent
82bce54614
commit
ecfacc362d
4 changed files with 65 additions and 5 deletions
|
@ -500,7 +500,10 @@ class _CallableGenericAlias(GenericAlias):
|
||||||
if subparams:
|
if subparams:
|
||||||
subargs = tuple(subst[x] for x in subparams)
|
subargs = tuple(subst[x] for x in subparams)
|
||||||
arg = arg[subargs]
|
arg = arg[subargs]
|
||||||
new_args.append(arg)
|
if isinstance(arg, tuple):
|
||||||
|
new_args.extend(arg)
|
||||||
|
else:
|
||||||
|
new_args.append(arg)
|
||||||
|
|
||||||
# args[0] occurs due to things like Z[[int, str, bool]] from PEP 612
|
# args[0] occurs due to things like Z[[int, str, bool]] from PEP 612
|
||||||
if not isinstance(new_args[0], list):
|
if not isinstance(new_args[0], list):
|
||||||
|
|
|
@ -628,10 +628,31 @@ class BaseCallableTests:
|
||||||
def test_concatenate(self):
|
def test_concatenate(self):
|
||||||
Callable = self.Callable
|
Callable = self.Callable
|
||||||
fullname = f"{Callable.__module__}.Callable"
|
fullname = f"{Callable.__module__}.Callable"
|
||||||
|
T = TypeVar('T')
|
||||||
P = ParamSpec('P')
|
P = ParamSpec('P')
|
||||||
C1 = Callable[typing.Concatenate[int, P], int]
|
P2 = ParamSpec('P2')
|
||||||
self.assertEqual(repr(C1),
|
C = Callable[Concatenate[int, P], T]
|
||||||
f"{fullname}[typing.Concatenate[int, ~P], int]")
|
self.assertEqual(repr(C),
|
||||||
|
f"{fullname}[typing.Concatenate[int, ~P], ~T]")
|
||||||
|
self.assertEqual(C[P2, int], Callable[Concatenate[int, P2], int])
|
||||||
|
self.assertEqual(C[[str, float], int], Callable[[int, str, float], int])
|
||||||
|
self.assertEqual(C[[], int], Callable[[int], int])
|
||||||
|
self.assertEqual(C[Concatenate[str, P2], int],
|
||||||
|
Callable[Concatenate[int, str, P2], int])
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
C[..., int]
|
||||||
|
|
||||||
|
C = Callable[Concatenate[int, P], int]
|
||||||
|
self.assertEqual(repr(C),
|
||||||
|
f"{fullname}[typing.Concatenate[int, ~P], int]")
|
||||||
|
self.assertEqual(C[P2], Callable[Concatenate[int, P2], int])
|
||||||
|
self.assertEqual(C[[str, float]], Callable[[int, str, float], int])
|
||||||
|
self.assertEqual(C[str, float], Callable[[int, str, float], int])
|
||||||
|
self.assertEqual(C[[]], Callable[[int], int])
|
||||||
|
self.assertEqual(C[Concatenate[str, P2]],
|
||||||
|
Callable[Concatenate[int, str, P2], int])
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
C[...]
|
||||||
|
|
||||||
def test_errors(self):
|
def test_errors(self):
|
||||||
Callable = self.Callable
|
Callable = self.Callable
|
||||||
|
@ -5004,6 +5025,27 @@ class ConcatenateTests(BaseTestCase):
|
||||||
self.assertEqual(C4.__args__, (Concatenate[int, T, P], T))
|
self.assertEqual(C4.__args__, (Concatenate[int, T, P], T))
|
||||||
self.assertEqual(C4.__parameters__, (T, P))
|
self.assertEqual(C4.__parameters__, (T, P))
|
||||||
|
|
||||||
|
def test_var_substitution(self):
|
||||||
|
T = TypeVar('T')
|
||||||
|
P = ParamSpec('P')
|
||||||
|
P2 = ParamSpec('P2')
|
||||||
|
C = Concatenate[T, P]
|
||||||
|
self.assertEqual(C[int, P2], Concatenate[int, P2])
|
||||||
|
self.assertEqual(C[int, [str, float]], (int, str, float))
|
||||||
|
self.assertEqual(C[int, []], (int,))
|
||||||
|
self.assertEqual(C[int, Concatenate[str, P2]],
|
||||||
|
Concatenate[int, str, P2])
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
C[int, ...]
|
||||||
|
|
||||||
|
C = Concatenate[int, P]
|
||||||
|
self.assertEqual(C[P2], Concatenate[int, P2])
|
||||||
|
self.assertEqual(C[[str, float]], (int, str, float))
|
||||||
|
self.assertEqual(C[str, float], (int, str, float))
|
||||||
|
self.assertEqual(C[[]], (int,))
|
||||||
|
self.assertEqual(C[Concatenate[str, P2]], Concatenate[int, str, P2])
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
C[...]
|
||||||
|
|
||||||
class TypeGuardTests(BaseTestCase):
|
class TypeGuardTests(BaseTestCase):
|
||||||
def test_basics(self):
|
def test_basics(self):
|
||||||
|
|
|
@ -604,7 +604,7 @@ def Concatenate(self, parameters):
|
||||||
raise TypeError("The last parameter to Concatenate should be a "
|
raise TypeError("The last parameter to Concatenate should be a "
|
||||||
"ParamSpec variable.")
|
"ParamSpec variable.")
|
||||||
msg = "Concatenate[arg, ...]: each arg must be a type."
|
msg = "Concatenate[arg, ...]: each arg must be a type."
|
||||||
parameters = tuple(_type_check(p, msg) for p in parameters)
|
parameters = (*(_type_check(p, msg) for p in parameters[:-1]), parameters[-1])
|
||||||
return _ConcatenateGenericAlias(self, parameters)
|
return _ConcatenateGenericAlias(self, parameters)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1274,6 +1274,16 @@ class _ConcatenateGenericAlias(_GenericAlias, _root=True):
|
||||||
_typevar_types=(TypeVar, ParamSpec),
|
_typevar_types=(TypeVar, ParamSpec),
|
||||||
_paramspec_tvars=True)
|
_paramspec_tvars=True)
|
||||||
|
|
||||||
|
def copy_with(self, params):
|
||||||
|
if isinstance(params[-1], (list, tuple)):
|
||||||
|
return (*params[:-1], *params[-1])
|
||||||
|
if isinstance(params[-1], _ConcatenateGenericAlias):
|
||||||
|
params = (*params[:-1], *params[-1].__args__)
|
||||||
|
elif not isinstance(params[-1], ParamSpec):
|
||||||
|
raise TypeError("The last parameter to Concatenate should be a "
|
||||||
|
"ParamSpec variable.")
|
||||||
|
return super().copy_with(params)
|
||||||
|
|
||||||
|
|
||||||
class Generic:
|
class Generic:
|
||||||
"""Abstract base class for generic types.
|
"""Abstract base class for generic types.
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
Fix substitution of :class:`~typing.ParamSpec` in
|
||||||
|
:data:`~typing.Concatenate` with different parameter expressions.
|
||||||
|
Substitution with a list of types returns now a tuple of types. Substitution
|
||||||
|
with ``Concatenate`` returns now a ``Concatenate`` with concatenated lists
|
||||||
|
of arguments.
|
Loading…
Add table
Add a link
Reference in a new issue