mirror of
https://github.com/python/cpython.git
synced 2025-09-04 16:01:10 +00:00
gh-88965: typing: fix type substitution of a list of types after initial ParamSpec
substitution (GH-102808)
Previously, this used to fail:
```py
from typing import *
T = TypeVar("T")
P = ParamSpec("P")
class X(Generic[P]):
f: Callable[P, int]
Y = X[[int, T]]
Z = Y[str]
```
(cherry picked from commit adb0621652
)
Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
parent
84ae50c914
commit
1645a40b5e
3 changed files with 154 additions and 7 deletions
|
@ -250,10 +250,17 @@ def _collect_parameters(args):
|
|||
"""
|
||||
parameters = []
|
||||
for t in args:
|
||||
# We don't want __parameters__ descriptor of a bare Python class.
|
||||
if isinstance(t, type):
|
||||
continue
|
||||
if hasattr(t, '__typing_subst__'):
|
||||
# We don't want __parameters__ descriptor of a bare Python class.
|
||||
pass
|
||||
elif isinstance(t, tuple):
|
||||
# `t` might be a tuple, when `ParamSpec` is substituted with
|
||||
# `[T, int]`, or `[int, *Ts]`, etc.
|
||||
for x in t:
|
||||
for collected in _collect_parameters([x]):
|
||||
if collected not in parameters:
|
||||
parameters.append(collected)
|
||||
elif hasattr(t, '__typing_subst__'):
|
||||
if t not in parameters:
|
||||
parameters.append(t)
|
||||
else:
|
||||
|
@ -1416,10 +1423,12 @@ class _GenericAlias(_BaseGenericAlias, _root=True):
|
|||
raise TypeError(f"Too {'many' if alen > plen else 'few'} arguments for {self};"
|
||||
f" actual {alen}, expected {plen}")
|
||||
new_arg_by_param = dict(zip(params, args))
|
||||
return tuple(self._make_substitution(self.__args__, new_arg_by_param))
|
||||
|
||||
def _make_substitution(self, args, new_arg_by_param):
|
||||
"""Create a list of new type arguments."""
|
||||
new_args = []
|
||||
for old_arg in self.__args__:
|
||||
|
||||
for old_arg in args:
|
||||
if isinstance(old_arg, type):
|
||||
new_args.append(old_arg)
|
||||
continue
|
||||
|
@ -1463,10 +1472,20 @@ class _GenericAlias(_BaseGenericAlias, _root=True):
|
|||
# should join all these types together in a flat list
|
||||
# `(float, int, str)` - so again, we should `extend`.
|
||||
new_args.extend(new_arg)
|
||||
elif isinstance(old_arg, tuple):
|
||||
# Corner case:
|
||||
# P = ParamSpec('P')
|
||||
# T = TypeVar('T')
|
||||
# class Base(Generic[P]): ...
|
||||
# Can be substituted like this:
|
||||
# X = Base[[int, T]]
|
||||
# In this case, `old_arg` will be a tuple:
|
||||
new_args.append(
|
||||
tuple(self._make_substitution(old_arg, new_arg_by_param)),
|
||||
)
|
||||
else:
|
||||
new_args.append(new_arg)
|
||||
|
||||
return tuple(new_args)
|
||||
return new_args
|
||||
|
||||
def copy_with(self, args):
|
||||
return self.__class__(self.__origin__, args, name=self._name, inst=self._inst,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue