Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Merge branch 'main' into typevar-subst2
  • Loading branch information
serhiy-storchaka committed Mar 10, 2022
commit e4345da2d774b403d3a147e6ec67a54593e4e0b7
1 change: 1 addition & 0 deletions Include/internal/pycore_global_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(__subclasshook__)
STRUCT_FOR_ID(__truediv__)
STRUCT_FOR_ID(__trunc__)
STRUCT_FOR_ID(__typing_subst__)
STRUCT_FOR_ID(__warningregistry__)
STRUCT_FOR_ID(__weakref__)
STRUCT_FOR_ID(__xor__)
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_runtime_init.h
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,7 @@ extern "C" {
INIT_ID(__subclasshook__), \
INIT_ID(__truediv__), \
INIT_ID(__trunc__), \
INIT_ID(__typing_subst__), \
INIT_ID(__warningregistry__), \
INIT_ID(__weakref__), \
INIT_ID(__xor__), \
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ def test_bad_var_substitution(self):
T = TypeVar('T')
P = ParamSpec("P")
bad_args = (
42, ..., [int], (), (int, str), P, Union,
42, ..., [int], (), (int, str), Union,
Generic, Generic[T], Protocol, Protocol[T],
Final, Final[int], ClassVar, ClassVar[int],
)
Expand Down
85 changes: 47 additions & 38 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,14 @@ def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms=
if (isinstance(arg, _GenericAlias) and
arg.__origin__ in invalid_generic_forms):
raise TypeError(f"{arg} is not valid as type argument")
if arg in (Any, NoReturn):
if arg in (Any, NoReturn, Never, Self, TypeAlias):
return arg
if allow_special_forms and arg in (ClassVar, Final):
return arg
if isinstance(arg, _SpecialForm) or arg in (Generic, Protocol):
raise TypeError(f"Plain {arg} is not valid as type argument")
if isinstance(arg, (type, TypeVar, ForwardRef, types.UnionType)):
if isinstance(arg, (type, TypeVar, ForwardRef, types.UnionType, ParamSpec,
ParamSpecArgs, ParamSpecKwargs, TypeVarTuple)):
return arg
if not callable(arg):
raise TypeError(f"{msg} Got {arg!r:.100}.")
Expand Down Expand Up @@ -947,6 +948,9 @@ def __iter__(self):
def __repr__(self):
return self._name

def __typing_subst__(self, arg):
raise AssertionError


class ParamSpecArgs(_Final, _Immutable, _root=True):
"""The args for a ParamSpec object.
Expand Down Expand Up @@ -1186,15 +1190,40 @@ def _is_unpacked_arbitrary_length_tuple(x: Any) -> bool:


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,
_paramspec_tvars=False):
super().__init__(origin, inst=inst, name=name)
if not isinstance(args, tuple):
args = (args,)
self.__args__ = tuple(... if a is _TypingEllipsis else
() if a is _TypingEmpty else
a for a in params)
self.__parameters__ = _collect_parameters(params)
a for a in args)
self.__parameters__ = _collect_parameters(args)
self._paramspec_tvars = _paramspec_tvars
if not name:
self.__module__ = origin.__module__
Expand Down Expand Up @@ -1275,42 +1304,22 @@ def _determine_new_args(self, args):
new_arg_by_param = dict(zip(self.__parameters__, args))

new_args = []
for arg in self.__args__:
substfunc = getattr(arg, '__typing_subst__', None)
if substfunc:
arg = substfunc(subst[arg])
else:
subparams = getattr(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))

if isinstance(old_arg, ParamSpec):
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 (TypeVarTuple in self._typevar_types
and _is_unpacked_typevartuple(old_arg)):
for old_arg in self.__args__:

if _is_unpacked_typevartuple(old_arg):
original_typevartuple = old_arg.__parameters__[0]
new_arg = new_arg_by_param[original_typevartuple]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is not covered by tests.

It perhaps could be simplified too, but since it is not covered by tests I left it as is.

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
substfunc = getattr(old_arg, '__typing_subst__', None)
if substfunc:
new_arg = substfunc(new_arg_by_param[old_arg])
else:
subparams = getattr(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]

if self.__origin__ == collections.abc.Callable and isinstance(new_arg, tuple):
# Consider the following `Callable`.
Expand Down
6 changes: 2 additions & 4 deletions Objects/genericaliasobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,8 @@ _Py_make_parameters(PyObject *args)
Py_ssize_t iparam = 0;
for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) {
PyObject *t = PyTuple_GET_ITEM(args, iarg);
_Py_IDENTIFIER(__typing_subst__);
PyObject *subst;
if (_PyObject_LookupAttrId(t, &PyId___typing_subst__, &subst) < 0) {
if (_PyObject_LookupAttr(t, &_Py_ID(__typing_subst__), &subst) < 0) {
Py_DECREF(parameters);
return NULL;
}
Expand Down Expand Up @@ -303,9 +302,8 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
}
for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) {
PyObject *arg = PyTuple_GET_ITEM(args, iarg);
_Py_IDENTIFIER(__typing_subst__);
PyObject *subst;
if (_PyObject_LookupAttrId(arg, &PyId___typing_subst__, &subst) < 0) {
if (_PyObject_LookupAttr(arg, &_Py_ID(__typing_subst__), &subst) < 0) {
Py_DECREF(newargs);
return NULL;
}
Expand Down
You are viewing a condensed version of this merge commit. You can view the full changes here.