[Python-checkins] [3.10] gh-98852: Fix subscription of types.GenericAlias instances (GH-98920) (GH-98969)

serhiy-storchaka webhook-mailer at python.org
Tue Nov 1 14:14:43 EDT 2022


https://github.com/python/cpython/commit/9ca7b1561f8a298a1d5917dcaae5a654ac0766b1
commit: 9ca7b1561f8a298a1d5917dcaae5a654ac0766b1
branch: 3.10
author: Serhiy Storchaka <storchaka at gmail.com>
committer: serhiy-storchaka <storchaka at gmail.com>
date: 2022-11-01T20:14:38+02:00
summary:

[3.10] gh-98852: Fix subscription of types.GenericAlias instances (GH-98920) (GH-98969)

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.

files:
A Misc/NEWS.d/next/Core and Builtins/2022-10-31-21-01-35.gh-issue-98852.MYaRN6.rst
M Lib/_collections_abc.py
M Lib/test/test_typing.py
M Objects/genericaliasobject.c

diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
index 40417dc1d313..72fd633cf9ac 100644
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -441,6 +441,8 @@ def __new__(cls, origin, args):
     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 @@ def __getitem__(self, item):
         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 34f944416070..6c53154686c9 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -2498,6 +2498,61 @@ def test_subclass_special_form(self):
                     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 000000000000..25c473717ca2
--- /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 f52bc974f4d8..9edb6d23725d 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) {



More information about the Python-checkins mailing list