diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 2bf16c30e1bce2..73fbfadadafe75 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -204,7 +204,6 @@ struct _Py_global_strings { STRUCT_FOR_ID(__trunc__) STRUCT_FOR_ID(__typing_is_unpacked_typevartuple__) STRUCT_FOR_ID(__typing_prepare_subst__) - STRUCT_FOR_ID(__typing_subst__) STRUCT_FOR_ID(__typing_unpacked_tuple_args__) STRUCT_FOR_ID(__warningregistry__) STRUCT_FOR_ID(__weaklistoffset__) diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index b4ce4e325a4d8e..4d19285b243701 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -826,7 +826,6 @@ extern "C" { INIT_ID(__trunc__), \ INIT_ID(__typing_is_unpacked_typevartuple__), \ INIT_ID(__typing_prepare_subst__), \ - INIT_ID(__typing_subst__), \ INIT_ID(__typing_unpacked_tuple_args__), \ INIT_ID(__warningregistry__), \ INIT_ID(__weaklistoffset__), \ diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index dfbe2d9d34c8ff..3c28babbbef92b 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -448,16 +448,18 @@ def test_no_bivariant(self): def test_var_substitution(self): T = TypeVar('T') - subst = T.__typing_subst__ - self.assertIs(subst(int), int) - self.assertEqual(subst(list[int]), list[int]) - self.assertEqual(subst(List[int]), List[int]) - self.assertEqual(subst(List), List) - self.assertIs(subst(Any), Any) - self.assertIs(subst(None), type(None)) - self.assertIs(subst(T), T) - self.assertEqual(subst(int|str), int|str) - self.assertEqual(subst(Union[int, str]), Union[int, str]) + self.assertEqual(T.__parameters__, (T,)) + self.assertIs(T.__parameters__[0], T) + self.assertIs(T[int,], int) + self.assertEqual(T[list[int],], list[int]) + self.assertEqual(T[List[int],], List[int]) + self.assertEqual(T[List,], List) + self.assertIs(T[Any,], Any) + self.assertIs(T[None,], type(None)) + self.assertIs(T[T,], T) + self.assertIs(T[(int,)], int) + self.assertEqual(T[int|str,], int|str) + self.assertEqual(T[Union[int, str],], Union[int, str]) def test_bad_var_substitution(self): T = TypeVar('T') @@ -470,7 +472,7 @@ def test_bad_var_substitution(self): for arg in bad_args: with self.subTest(arg=arg): with self.assertRaises(TypeError): - T.__typing_subst__(arg) + T[arg,] with self.assertRaises(TypeError): List[T][arg] with self.assertRaises(TypeError): @@ -6819,13 +6821,14 @@ class X(Generic[P, P2]): def test_var_substitution(self): T = TypeVar("T") P = ParamSpec("P") - subst = P.__typing_subst__ - self.assertEqual(subst((int, str)), (int, str)) - self.assertEqual(subst([int, str]), (int, str)) - self.assertEqual(subst([None]), (type(None),)) - self.assertIs(subst(...), ...) - self.assertIs(subst(P), P) - self.assertEqual(subst(Concatenate[int, P]), Concatenate[int, P]) + self.assertEqual(P.__parameters__, (P,)) + self.assertIs(P.__parameters__[0], P) + self.assertEqual(P[(int, str),], (int, str)) + self.assertEqual(P[[int, str],], (int, str)) + self.assertEqual(P[[None],], (type(None),)) + self.assertIs(P[...,], ...) + self.assertIs(P[P,], P) + self.assertEqual(P[Concatenate[int, P],], Concatenate[int, P]) def test_bad_var_substitution(self): T = TypeVar('T') @@ -6834,7 +6837,7 @@ def test_bad_var_substitution(self): for arg in bad_args: with self.subTest(arg=arg): with self.assertRaises(TypeError): - P.__typing_subst__(arg) + P[arg,] with self.assertRaises(TypeError): typing.Callable[P, T][arg, str] with self.assertRaises(TypeError): diff --git a/Lib/typing.py b/Lib/typing.py index 25cae7ffb8d788..aa9edda91fa40c 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -250,13 +250,9 @@ def _collect_parameters(args): """ parameters = [] for t in args: - if hasattr(t, '__typing_subst__'): - if t not in parameters: - parameters.append(t) - else: - for x in getattr(t, '__parameters__', ()): - if x not in parameters: - parameters.append(x) + for x in getattr(t, '__parameters__', ()): + if x not in parameters: + parameters.append(x) return tuple(parameters) @@ -954,6 +950,9 @@ def __repr__(self): prefix = '~' return prefix + self.__name__ + @property + def __parameters__(self): + return (self,) class TypeVar(_Final, _Immutable, _BoundVarianceMixin, _PickleUsingNameMixin, _root=True): @@ -1014,7 +1013,9 @@ def __init__(self, name, *constraints, bound=None, if def_mod != 'typing': self.__module__ = def_mod - def __typing_subst__(self, arg): + def __getitem__(self, arg): + if isinstance(arg, tuple) and len(arg) == 1: + arg, = arg msg = "Parameters to generic types must be types." arg = _type_check(arg, msg, is_argument=True) if ((isinstance(arg, _GenericAlias) and arg.__origin__ is Unpack) or @@ -1062,9 +1063,13 @@ def __iter__(self): def __repr__(self): return self.__name__ - def __typing_subst__(self, arg): + def __getitem__(self, arg): raise TypeError("Substitution of bare TypeVarTuple is not supported") + @property + def __parameters__(self): + return (self,) + def __typing_prepare_subst__(self, alias, args): params = alias.__parameters__ typevartuple_index = params.index(self) @@ -1212,7 +1217,9 @@ def __init__(self, name, *, bound=None, covariant=False, contravariant=False): if def_mod != 'typing': self.__module__ = def_mod - def __typing_subst__(self, arg): + def __getitem__(self, arg): + if isinstance(arg, tuple) and len(arg) == 1: + arg, = arg if isinstance(arg, (list, tuple)): arg = tuple(_type_check(a, "Expected a type.") for a in arg) elif not _is_param_expr(arg): @@ -1420,21 +1427,17 @@ def _determine_new_args(self, args): new_args = [] for old_arg in self.__args__: - substfunc = getattr(old_arg, '__typing_subst__', None) - if substfunc: - new_arg = substfunc(new_arg_by_param[old_arg]) + subparams = getattr(old_arg, '__parameters__', ()) + if not subparams: + new_arg = old_arg else: - subparams = getattr(old_arg, '__parameters__', ()) - if not subparams: - new_arg = old_arg - else: - subargs = [] - for x in subparams: - if isinstance(x, TypeVarTuple): - subargs.extend(new_arg_by_param[x]) - else: - subargs.append(new_arg_by_param[x]) - new_arg = old_arg[tuple(subargs)] + subargs = [] + for x in subparams: + if isinstance(x, TypeVarTuple): + subargs.extend(new_arg_by_param[x]) + else: + subargs.append(new_arg_by_param[x]) + new_arg = old_arg[tuple(subargs)] if self.__origin__ == collections.abc.Callable and isinstance(new_arg, tuple): # Consider the following `Callable`. diff --git a/Misc/NEWS.d/next/Library/2021-07-31-16-33-33.bpo-44796.-pyVhM.rst b/Misc/NEWS.d/next/Library/2021-07-31-16-33-33.bpo-44796.-pyVhM.rst new file mode 100644 index 00000000000000..bdd4569008f261 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-07-31-16-33-33.bpo-44796.-pyVhM.rst @@ -0,0 +1,2 @@ +Add ``__parameters__`` and ``__getitem__`` in :class:`~typing.TypeVar` and +:class:`~ParamSpec`. diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index 0a0d0cc4c15b68..b60a03157b6c61 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -218,40 +218,29 @@ _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); - PyObject *subst; - if (_PyObject_LookupAttr(t, &_Py_ID(__typing_subst__), &subst) < 0) { + PyObject *subparams; + if (_PyObject_LookupAttr(t, &_Py_ID(__parameters__), + &subparams) < 0) { Py_DECREF(parameters); return NULL; } - if (subst) { - iparam += tuple_add(parameters, iparam, t); - Py_DECREF(subst); - } - else { - PyObject *subparams; - if (_PyObject_LookupAttr(t, &_Py_ID(__parameters__), - &subparams) < 0) { - Py_DECREF(parameters); - return NULL; - } - if (subparams && PyTuple_Check(subparams)) { - Py_ssize_t len2 = PyTuple_GET_SIZE(subparams); - Py_ssize_t needed = len2 - 1 - (iarg - iparam); - if (needed > 0) { - len += needed; - if (_PyTuple_Resize(¶meters, len) < 0) { - Py_DECREF(subparams); - Py_DECREF(parameters); - return NULL; - } - } - for (Py_ssize_t j = 0; j < len2; j++) { - PyObject *t2 = PyTuple_GET_ITEM(subparams, j); - iparam += tuple_add(parameters, iparam, t2); + if (subparams && PyTuple_Check(subparams)) { + Py_ssize_t len2 = PyTuple_GET_SIZE(subparams); + Py_ssize_t needed = len2 - 1 - (iarg - iparam); + if (needed > 0) { + len += needed; + if (_PyTuple_Resize(¶meters, len) < 0) { + Py_DECREF(subparams); + Py_DECREF(parameters); + return NULL; } } - Py_XDECREF(subparams); + for (Py_ssize_t j = 0; j < len2; j++) { + PyObject *t2 = PyTuple_GET_ITEM(subparams, j); + iparam += tuple_add(parameters, iparam, t2); + } } + Py_XDECREF(subparams); } if (iparam < len) { if (_PyTuple_Resize(¶meters, iparam) < 0) { @@ -460,21 +449,7 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje Py_DECREF(item); return NULL; } - PyObject *subst; - if (_PyObject_LookupAttr(arg, &_Py_ID(__typing_subst__), &subst) < 0) { - Py_DECREF(newargs); - Py_DECREF(item); - return NULL; - } - if (subst) { - Py_ssize_t iparam = tuple_index(parameters, nparams, arg); - assert(iparam >= 0); - arg = PyObject_CallOneArg(subst, argitems[iparam]); - Py_DECREF(subst); - } - else { - arg = subs_tvars(arg, parameters, argitems, nitems); - } + arg = subs_tvars(arg, parameters, argitems, nitems); if (arg == NULL) { Py_DECREF(newargs); Py_DECREF(item);