mirror of
https://github.com/python/cpython.git
synced 2025-10-07 07:31:46 +00:00
bpo-46589: Improve documentation for typing._GenericAlias (GH-31026)
(These should arguably be docstrings per convention in the rest of the file, but it doesn't really matter.)
This commit is contained in:
parent
067c03bf40
commit
a335d98f19
1 changed files with 112 additions and 34 deletions
146
Lib/typing.py
146
Lib/typing.py
|
@ -1000,16 +1000,41 @@ class _BaseGenericAlias(_Final, _root=True):
|
||||||
|
|
||||||
|
|
||||||
class _GenericAlias(_BaseGenericAlias, _root=True):
|
class _GenericAlias(_BaseGenericAlias, _root=True):
|
||||||
def __init__(self, origin, params, *, inst=True, name=None,
|
# The type of parameterized generics.
|
||||||
|
#
|
||||||
|
# That is, for example, `type(List[int])` is `_GenericAlias`.
|
||||||
|
#
|
||||||
|
# Objects which are instances of this class include:
|
||||||
|
# * Parameterized container types, e.g. `Tuple[int]`, `List[int]`.
|
||||||
|
# * Note that native container types, e.g. `tuple`, `list`, use
|
||||||
|
# `types.GenericAlias` instead.
|
||||||
|
# * Parameterized classes:
|
||||||
|
# T = TypeVar('T')
|
||||||
|
# class C(Generic[T]): pass
|
||||||
|
# # C[int] is a _GenericAlias
|
||||||
|
# * `Callable` aliases, generic `Callable` aliases, and
|
||||||
|
# parameterized `Callable` aliases:
|
||||||
|
# T = TypeVar('T')
|
||||||
|
# # _CallableGenericAlias inherits from _GenericAlias.
|
||||||
|
# A = Callable[[], None] # _CallableGenericAlias
|
||||||
|
# B = Callable[[T], None] # _CallableGenericAlias
|
||||||
|
# C = B[int] # _CallableGenericAlias
|
||||||
|
# * Parameterized `Final`, `ClassVar` and `TypeGuard`:
|
||||||
|
# # All _GenericAlias
|
||||||
|
# Final[int]
|
||||||
|
# ClassVar[float]
|
||||||
|
# TypeVar[bool]
|
||||||
|
|
||||||
|
def __init__(self, origin, args, *, inst=True, name=None,
|
||||||
_typevar_types=TypeVar,
|
_typevar_types=TypeVar,
|
||||||
_paramspec_tvars=False):
|
_paramspec_tvars=False):
|
||||||
super().__init__(origin, inst=inst, name=name)
|
super().__init__(origin, inst=inst, name=name)
|
||||||
if not isinstance(params, tuple):
|
if not isinstance(args, tuple):
|
||||||
params = (params,)
|
args = (args,)
|
||||||
self.__args__ = tuple(... if a is _TypingEllipsis else
|
self.__args__ = tuple(... if a is _TypingEllipsis else
|
||||||
() if a is _TypingEmpty else
|
() if a is _TypingEmpty else
|
||||||
a for a in params)
|
a for a in args)
|
||||||
self.__parameters__ = _collect_type_vars(params, typevar_types=_typevar_types)
|
self.__parameters__ = _collect_type_vars(args, typevar_types=_typevar_types)
|
||||||
self._typevar_types = _typevar_types
|
self._typevar_types = _typevar_types
|
||||||
self._paramspec_tvars = _paramspec_tvars
|
self._paramspec_tvars = _paramspec_tvars
|
||||||
if not name:
|
if not name:
|
||||||
|
@ -1031,44 +1056,97 @@ class _GenericAlias(_BaseGenericAlias, _root=True):
|
||||||
return Union[left, self]
|
return Union[left, self]
|
||||||
|
|
||||||
@_tp_cache
|
@_tp_cache
|
||||||
def __getitem__(self, params):
|
def __getitem__(self, args):
|
||||||
|
# Parameterizes an already-parameterized object.
|
||||||
|
#
|
||||||
|
# For example, we arrive here doing something like:
|
||||||
|
# T1 = TypeVar('T1')
|
||||||
|
# T2 = TypeVar('T2')
|
||||||
|
# T3 = TypeVar('T3')
|
||||||
|
# class A(Generic[T1]): pass
|
||||||
|
# B = A[T2] # B is a _GenericAlias
|
||||||
|
# C = B[T3] # Invokes _GenericAlias.__getitem__
|
||||||
|
#
|
||||||
|
# We also arrive here when parameterizing a generic `Callable` alias:
|
||||||
|
# T = TypeVar('T')
|
||||||
|
# C = Callable[[T], None]
|
||||||
|
# C[int] # Invokes _GenericAlias.__getitem__
|
||||||
|
|
||||||
if self.__origin__ in (Generic, Protocol):
|
if self.__origin__ in (Generic, Protocol):
|
||||||
# Can't subscript Generic[...] or Protocol[...].
|
# Can't subscript Generic[...] or Protocol[...].
|
||||||
raise TypeError(f"Cannot subscript already-subscripted {self}")
|
raise TypeError(f"Cannot subscript already-subscripted {self}")
|
||||||
if not isinstance(params, tuple):
|
|
||||||
params = (params,)
|
# Preprocess `args`.
|
||||||
params = tuple(_type_convert(p) for p in params)
|
if not isinstance(args, tuple):
|
||||||
|
args = (args,)
|
||||||
|
args = tuple(_type_convert(p) for p in args)
|
||||||
if (self._paramspec_tvars
|
if (self._paramspec_tvars
|
||||||
and any(isinstance(t, ParamSpec) for t in self.__parameters__)):
|
and any(isinstance(t, ParamSpec) for t in self.__parameters__)):
|
||||||
params = _prepare_paramspec_params(self, params)
|
args = _prepare_paramspec_params(self, args)
|
||||||
else:
|
else:
|
||||||
_check_generic(self, params, len(self.__parameters__))
|
_check_generic(self, args, len(self.__parameters__))
|
||||||
|
|
||||||
|
new_args = self._determine_new_args(args)
|
||||||
|
r = self.copy_with(new_args)
|
||||||
|
return r
|
||||||
|
|
||||||
|
def _determine_new_args(self, args):
|
||||||
|
# Determines new __args__ for __getitem__.
|
||||||
|
#
|
||||||
|
# For example, suppose we had:
|
||||||
|
# T1 = TypeVar('T1')
|
||||||
|
# T2 = TypeVar('T2')
|
||||||
|
# class A(Generic[T1, T2]): pass
|
||||||
|
# T3 = TypeVar('T3')
|
||||||
|
# B = A[int, T3]
|
||||||
|
# C = B[str]
|
||||||
|
# `B.__args__` is `(int, T3)`, so `C.__args__` should be `(int, str)`.
|
||||||
|
# Unfortunately, this is harder than it looks, because if `T3` is
|
||||||
|
# anything more exotic than a plain `TypeVar`, we need to consider
|
||||||
|
# edge cases.
|
||||||
|
|
||||||
|
# In the example above, this would be {T3: str}
|
||||||
|
new_arg_by_param = dict(zip(self.__parameters__, args))
|
||||||
|
|
||||||
subst = dict(zip(self.__parameters__, params))
|
|
||||||
new_args = []
|
new_args = []
|
||||||
for arg in self.__args__:
|
for old_arg in self.__args__:
|
||||||
if isinstance(arg, self._typevar_types):
|
|
||||||
if isinstance(arg, ParamSpec):
|
|
||||||
arg = subst[arg]
|
|
||||||
if not _is_param_expr(arg):
|
|
||||||
raise TypeError(f"Expected a list of types, an ellipsis, "
|
|
||||||
f"ParamSpec, or Concatenate. Got {arg}")
|
|
||||||
else:
|
|
||||||
arg = subst[arg]
|
|
||||||
elif isinstance(arg, (_GenericAlias, GenericAlias, types.UnionType)):
|
|
||||||
subparams = arg.__parameters__
|
|
||||||
if subparams:
|
|
||||||
subargs = tuple(subst[x] for x in subparams)
|
|
||||||
arg = arg[subargs]
|
|
||||||
# Required to flatten out the args for CallableGenericAlias
|
|
||||||
if self.__origin__ == collections.abc.Callable and isinstance(arg, tuple):
|
|
||||||
new_args.extend(arg)
|
|
||||||
else:
|
|
||||||
new_args.append(arg)
|
|
||||||
return self.copy_with(tuple(new_args))
|
|
||||||
|
|
||||||
def copy_with(self, params):
|
if isinstance(old_arg, ParamSpec):
|
||||||
return self.__class__(self.__origin__, params, name=self._name, inst=self._inst)
|
new_arg = new_arg_by_param[old_arg]
|
||||||
|
if not _is_param_expr(new_arg):
|
||||||
|
raise TypeError(f"Expected a list of types, an ellipsis, "
|
||||||
|
f"ParamSpec, or Concatenate. Got {new_arg}")
|
||||||
|
elif isinstance(old_arg, self._typevar_types):
|
||||||
|
new_arg = new_arg_by_param[old_arg]
|
||||||
|
elif isinstance(old_arg, (_GenericAlias, GenericAlias, types.UnionType)):
|
||||||
|
subparams = old_arg.__parameters__
|
||||||
|
if not subparams:
|
||||||
|
new_arg = old_arg
|
||||||
|
else:
|
||||||
|
subargs = tuple(new_arg_by_param[x] for x in subparams)
|
||||||
|
new_arg = old_arg[subargs]
|
||||||
|
else:
|
||||||
|
new_arg = old_arg
|
||||||
|
|
||||||
|
if self.__origin__ == collections.abc.Callable and isinstance(new_arg, tuple):
|
||||||
|
# Consider the following `Callable`.
|
||||||
|
# C = Callable[[int], str]
|
||||||
|
# Here, `C.__args__` should be (int, str) - NOT ([int], str).
|
||||||
|
# That means that if we had something like...
|
||||||
|
# P = ParamSpec('P')
|
||||||
|
# T = TypeVar('T')
|
||||||
|
# C = Callable[P, T]
|
||||||
|
# D = C[[int, str], float]
|
||||||
|
# ...we need to be careful; `new_args` should end up as
|
||||||
|
# `(int, str, float)` rather than `([int, str], float)`.
|
||||||
|
new_args.extend(new_arg)
|
||||||
|
else:
|
||||||
|
new_args.append(new_arg)
|
||||||
|
|
||||||
|
return tuple(new_args)
|
||||||
|
|
||||||
|
def copy_with(self, args):
|
||||||
|
return self.__class__(self.__origin__, args, name=self._name, inst=self._inst)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if self._name:
|
if self._name:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue