diff options
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 87302ac76d..dddf8a23ff 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 |