diff options
author | kj <28750310+Fidget-Spinner@users.noreply.github.com> | 2020-12-13 18:38:24 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-13 18:38:24 (GMT) |
commit | 463c7d3d149283814d879a9bb8411af64e656c8e (patch) | |
tree | 204f9b460e5740291fd33d03908131bad317a922 /Lib/_collections_abc.py | |
parent | 43c4fb6c90c013a00cb820cb61e4990cd8ec7f5e (diff) | |
download | cpython-463c7d3d149283814d879a9bb8411af64e656c8e.zip cpython-463c7d3d149283814d879a9bb8411af64e656c8e.tar.gz cpython-463c7d3d149283814d879a9bb8411af64e656c8e.tar.bz2 |
bpo-42195: Ensure consistency of Callable's __args__ in collections.abc and typing (GH-23060)
Diffstat (limited to 'Lib/_collections_abc.py')
-rw-r--r-- | Lib/_collections_abc.py | 69 |
1 files changed, 68 insertions, 1 deletions
diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py index 28690f8..7c3faa6 100644 --- a/Lib/_collections_abc.py +++ b/Lib/_collections_abc.py @@ -10,6 +10,10 @@ from abc import ABCMeta, abstractmethod import sys GenericAlias = type(list[int]) +EllipsisType = type(...) +def _f(): pass +FunctionType = type(_f) +del _f __all__ = ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator", "AsyncGenerator", @@ -409,6 +413,69 @@ class Collection(Sized, Iterable, Container): return NotImplemented +class _CallableGenericAlias(GenericAlias): + """ Represent `Callable[argtypes, resulttype]`. + + This sets ``__args__`` to a tuple containing the flattened``argtypes`` + followed by ``resulttype``. + + Example: ``Callable[[int, str], float]`` sets ``__args__`` to + ``(int, str, float)``. + """ + + __slots__ = () + + def __new__(cls, origin, args): + return cls.__create_ga(origin, args) + + @classmethod + def __create_ga(cls, origin, args): + if not isinstance(args, tuple) or len(args) != 2: + raise TypeError( + "Callable must be used as Callable[[arg, ...], result].") + t_args, t_result = args + if isinstance(t_args, list): + ga_args = tuple(t_args) + (t_result,) + # This relaxes what t_args can be on purpose to allow things like + # PEP 612 ParamSpec. Responsibility for whether a user is using + # Callable[...] properly is deferred to static type checkers. + else: + ga_args = args + return super().__new__(cls, origin, ga_args) + + def __repr__(self): + if len(self.__args__) == 2 and self.__args__[0] is Ellipsis: + return super().__repr__() + return (f'collections.abc.Callable' + f'[[{", ".join([_type_repr(a) for a in self.__args__[:-1]])}], ' + f'{_type_repr(self.__args__[-1])}]') + + def __reduce__(self): + args = self.__args__ + if not (len(args) == 2 and args[0] is Ellipsis): + args = list(args[:-1]), args[-1] + return _CallableGenericAlias, (Callable, args) + + +def _type_repr(obj): + """Return the repr() of an object, special-casing types (internal helper). + + Copied from :mod:`typing` since collections.abc + shouldn't depend on that module. + """ + if isinstance(obj, GenericAlias): + return repr(obj) + if isinstance(obj, type): + if obj.__module__ == 'builtins': + return obj.__qualname__ + return f'{obj.__module__}.{obj.__qualname__}' + if obj is Ellipsis: + return '...' + if isinstance(obj, FunctionType): + return obj.__name__ + return repr(obj) + + class Callable(metaclass=ABCMeta): __slots__ = () @@ -423,7 +490,7 @@ class Callable(metaclass=ABCMeta): return _check_methods(C, "__call__") return NotImplemented - __class_getitem__ = classmethod(GenericAlias) + __class_getitem__ = classmethod(_CallableGenericAlias) ### SETS ### |