summaryrefslogtreecommitdiffstats
path: root/Lib/_collections_abc.py
diff options
context:
space:
mode:
authorKen Jin <28750310+Fidget-Spinner@users.noreply.github.com>2021-04-28 15:38:14 (GMT)
committerGitHub <noreply@github.com>2021-04-28 15:38:14 (GMT)
commit859577c24981d6b36960d309f99f7fc810fe75c2 (patch)
tree172b56f0aec20bc47c0fe0d39e44e6e040d0e63d /Lib/_collections_abc.py
parentc1a9535989cc7323099725503519a17f79d083f5 (diff)
downloadcpython-859577c24981d6b36960d309f99f7fc810fe75c2.zip
cpython-859577c24981d6b36960d309f99f7fc810fe75c2.tar.gz
cpython-859577c24981d6b36960d309f99f7fc810fe75c2.tar.bz2
bpo-41559: Change PEP 612 implementation to pure Python (#25449)
Diffstat (limited to 'Lib/_collections_abc.py')
-rw-r--r--Lib/_collections_abc.py64
1 files changed, 55 insertions, 9 deletions
diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
index 87302ac..dddf8a2 100644
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -443,6 +443,18 @@ class _CallableGenericAlias(GenericAlias):
ga_args = args
return super().__new__(cls, origin, ga_args)
+ @property
+ def __parameters__(self):
+ params = []
+ for arg in self.__args__:
+ # Looks like a genericalias
+ if hasattr(arg, "__parameters__") and isinstance(arg.__parameters__, tuple):
+ params.extend(arg.__parameters__)
+ else:
+ if _is_typevarlike(arg):
+ params.append(arg)
+ return tuple(dict.fromkeys(params))
+
def __repr__(self):
if _has_special_args(self.__args__):
return super().__repr__()
@@ -458,16 +470,50 @@ class _CallableGenericAlias(GenericAlias):
def __getitem__(self, item):
# Called during TypeVar substitution, returns the custom subclass
- # rather than the default types.GenericAlias object.
- ga = super().__getitem__(item)
- args = ga.__args__
- # args[0] occurs due to things like Z[[int, str, bool]] from PEP 612
- if not isinstance(ga.__args__[0], tuple):
- t_result = ga.__args__[-1]
- t_args = ga.__args__[:-1]
- args = (t_args, t_result)
- return _CallableGenericAlias(Callable, args)
+ # rather than the default types.GenericAlias object. Most of the
+ # code is copied from typing's _GenericAlias and the builtin
+ # types.GenericAlias.
+
+ # A special case in PEP 612 where if X = Callable[P, int],
+ # then X[int, str] == X[[int, str]].
+ param_len = len(self.__parameters__)
+ if param_len == 0:
+ raise TypeError(f'There are no type or parameter specification'
+ f'variables left in {self}')
+ if (param_len == 1
+ and isinstance(item, (tuple, list))
+ and len(item) > 1) or not isinstance(item, tuple):
+ item = (item,)
+ item_len = len(item)
+ if item_len != param_len:
+ raise TypeError(f'Too {"many" if item_len > param_len else "few"}'
+ f' arguments for {self};'
+ f' actual {item_len}, expected {param_len}')
+ subst = dict(zip(self.__parameters__, item))
+ new_args = []
+ for arg in self.__args__:
+ if _is_typevarlike(arg):
+ arg = subst[arg]
+ # Looks like a GenericAlias
+ elif hasattr(arg, '__parameters__') and isinstance(arg.__parameters__, tuple):
+ subparams = arg.__parameters__
+ if subparams:
+ subargs = tuple(subst[x] for x in subparams)
+ arg = arg[subargs]
+ new_args.append(arg)
+ # args[0] occurs due to things like Z[[int, str, bool]] from PEP 612
+ if not isinstance(new_args[0], (tuple, list)):
+ t_result = new_args[-1]
+ t_args = new_args[:-1]
+ new_args = (t_args, t_result)
+ return _CallableGenericAlias(Callable, tuple(new_args))
+
+def _is_typevarlike(arg):
+ obj = type(arg)
+ # looks like a TypeVar/ParamSpec
+ return (obj.__module__ == 'typing'
+ and obj.__name__ in {'ParamSpec', 'TypeVar'})
def _has_special_args(args):
"""Checks if args[0] matches either ``...``, ``ParamSpec`` or