]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.10] gh-98852: Fix subscription of types.GenericAlias instances (GH-98920) (GH...
authorSerhiy Storchaka <storchaka@gmail.com>
Tue, 1 Nov 2022 18:14:38 +0000 (20:14 +0200)
committerGitHub <noreply@github.com>
Tue, 1 Nov 2022 18:14:38 +0000 (20:14 +0200)
Fix subscription of 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.

Lib/_collections_abc.py
Lib/test/test_typing.py
Misc/NEWS.d/next/Core and Builtins/2022-10-31-21-01-35.gh-issue-98852.MYaRN6.rst [new file with mode: 0644]
Objects/genericaliasobject.c

index 40417dc1d3133dafc7c9183681aa1da5535c6f01..72fd633cf9ac2f94327b5c48c45b6344e8dad5aa 100644 (file)
@@ -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]
index 34f944416070cd5951c9dc67bf7071681bf2516a..6c53154686c914a37b3896d74fe0012ce00069b6 100644 (file)
@@ -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 (file)
index 0000000..25c4737
--- /dev/null
@@ -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.
index f52bc974f4d810a5d8bcdce4577a18dd1cf9ae84..9edb6d23725d718bfb605f7da9928bf4854062e1 100644 (file)
@@ -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) {