diff options
-rw-r--r-- | Lib/_collections_abc.py | 5 | ||||
-rw-r--r-- | Lib/test/test_typing.py | 55 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2022-10-31-21-01-35.gh-issue-98852.MYaRN6.rst | 3 | ||||
-rw-r--r-- | Objects/genericaliasobject.c | 8 |
4 files changed, 71 insertions, 0 deletions
diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py index 40417dc..72fd633 100644 --- a/Lib/_collections_abc.py +++ b/Lib/_collections_abc.py @@ -441,6 +441,8 @@ class _CallableGenericAlias(GenericAlias): def __parameters__(self): params = [] for arg in self.__args__: + if isinstance(arg, type) and not isinstance(arg, GenericAlias): + continue # Looks like a genericalias if hasattr(arg, "__parameters__") and isinstance(arg.__parameters__, tuple): params.extend(arg.__parameters__) @@ -486,6 +488,9 @@ class _CallableGenericAlias(GenericAlias): subst = dict(zip(self.__parameters__, item)) new_args = [] for arg in self.__args__: + if isinstance(arg, type) and not isinstance(arg, GenericAlias): + new_args.append(arg) + continue if _is_typevarlike(arg): if _is_param_expr(arg): arg = subst[arg] diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 34f9444..6c53154 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -2498,6 +2498,61 @@ class GenericTests(BaseTestCase): class Foo(obj): pass + def test_complex_subclasses(self): + T_co = TypeVar("T_co", covariant=True) + + class Base(Generic[T_co]): + ... + + T = TypeVar("T") + + # see gh-94607: this fails in that bug + class Sub(Base, Generic[T]): + ... + + def test_parameter_detection(self): + self.assertEqual(List[T].__parameters__, (T,)) + self.assertEqual(List[List[T]].__parameters__, (T,)) + class A: + __parameters__ = (T,) + # Bare classes should be skipped + for a in (List, list): + for b in (int, TypeVar, ParamSpec, types.GenericAlias, types.UnionType): + with self.subTest(generic=a, sub=b): + with self.assertRaisesRegex(TypeError, + '.* is not a generic class|' + 'no type variables left'): + a[b][str] + # Duck-typing anything that looks like it has __parameters__. + # C version of GenericAlias + self.assertEqual(list[A()].__parameters__, (T,)) + + def test_non_generic_subscript(self): + T = TypeVar('T') + class G(Generic[T]): + pass + + for s in (int, G, List, list, + TypeVar, ParamSpec, + types.GenericAlias, types.UnionType): + + for t in Tuple, tuple: + with self.subTest(tuple=t, sub=s): + self.assertEqual(t[s, T][int], t[s, int]) + self.assertEqual(t[T, s][int], t[int, s]) + a = t[s] + with self.assertRaises(TypeError): + a[int] + + for c in Callable, collections.abc.Callable: + with self.subTest(callable=c, sub=s): + self.assertEqual(c[[s], T][int], c[[s], int]) + self.assertEqual(c[[T], s][int], c[[int], s]) + a = c[[s], s] + with self.assertRaises(TypeError): + a[int] + + class ClassVarTests(BaseTestCase): def test_basics(self): diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-31-21-01-35.gh-issue-98852.MYaRN6.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-31-21-01-35.gh-issue-98852.MYaRN6.rst new file mode 100644 index 0000000..25c4737 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-31-21-01-35.gh-issue-98852.MYaRN6.rst @@ -0,0 +1,3 @@ +Fix subscription of :class:`types.GenericAlias` instances containing bare +generic types: for example ``tuple[A, T][int]``, +where ``A`` is a generic type, and ``T`` is a type variable. diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index f52bc97..9edb6d2 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -209,6 +209,9 @@ _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); + if (PyType_Check(t)) { + continue; + } int typevar = is_typevar(t); if (typevar < 0) { Py_DECREF(parameters); @@ -260,6 +263,11 @@ _Py_make_parameters(PyObject *args) static PyObject * subs_tvars(PyObject *obj, PyObject *params, PyObject **argitems) { + if (PyType_Check(obj)) { + Py_INCREF(obj); + return obj; + } + _Py_IDENTIFIER(__parameters__); PyObject *subparams; if (_PyObject_LookupAttrId(obj, &PyId___parameters__, &subparams) < 0) { |