summaryrefslogtreecommitdiffstats
path: root/Lib/typing.py
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2023-03-23 16:54:07 (GMT)
committerGitHub <noreply@github.com>2023-03-23 16:54:07 (GMT)
commit1645a40b5eb59e7021c93fd6593d1a316d3e26e8 (patch)
tree70ff1882d9af12bb5c7eac6312be52a68f8cb8c8 /Lib/typing.py
parent84ae50c9146e98be28cf3af4606f0ebad07807aa (diff)
downloadcpython-1645a40b5eb59e7021c93fd6593d1a316d3e26e8.zip
cpython-1645a40b5eb59e7021c93fd6593d1a316d3e26e8.tar.gz
cpython-1645a40b5eb59e7021c93fd6593d1a316d3e26e8.tar.bz2
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 adb0621652f489033b9db8d3949564c9fe545c1d) Co-authored-by: Nikita Sobolev <mail@sobolevn.me> Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Diffstat (limited to 'Lib/typing.py')
-rw-r--r--Lib/typing.py33
1 files changed, 26 insertions, 7 deletions
diff --git a/Lib/typing.py b/Lib/typing.py
index 9f5db1a..8995564 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -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,