diff options
author | Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> | 2021-04-28 15:38:14 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-28 15:38:14 (GMT) |
commit | 859577c24981d6b36960d309f99f7fc810fe75c2 (patch) | |
tree | 172b56f0aec20bc47c0fe0d39e44e6e040d0e63d /Lib/_collections_abc.py | |
parent | c1a9535989cc7323099725503519a17f79d083f5 (diff) | |
download | cpython-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.py | 64 |
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 |