]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-138479: Ensure that `__typing_subst__` returns a tuple (GH-138482) (GH...
authorPeter Bierma <zintensitydev@gmail.com>
Thu, 11 Sep 2025 11:08:57 +0000 (07:08 -0400)
committerGitHub <noreply@github.com>
Thu, 11 Sep 2025 11:08:57 +0000 (11:08 +0000)
* gh-138479: Ensure that `__typing_subst__` returns a tuple (GH-138482)

Raise an exception if __typing_subst__ returns a non-tuple object.

(cherry picked from commit 1da989be74eaa94590ec28e750e5352de1ead227)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Lib/test/test_typing.py
Misc/NEWS.d/next/Core_and_Builtins/2025-09-03-17-00-30.gh-issue-138479.qUxgWs.rst [new file with mode: 0644]
Objects/genericaliasobject.c

index e1115809d4f390f70728dffc0bfb7f07b0f0245d..5f49bf757096f069f058e53d06b754eb0531728b 100644 (file)
@@ -5706,6 +5706,23 @@ class GenericTests(BaseTestCase):
                     with self.assertRaises(TypeError):
                         a[int]
 
+    def test_return_non_tuple_while_unpacking(self):
+        # GH-138497: GenericAlias objects didn't ensure that __typing_subst__ actually
+        # returned a tuple
+        class EvilTypeVar:
+            __typing_is_unpacked_typevartuple__ = True
+            def __typing_prepare_subst__(*_):
+                return None  # any value
+            def __typing_subst__(*_):
+                return 42  # not tuple
+
+        evil = EvilTypeVar()
+        # Create a dummy TypeAlias that will be given the evil generic from
+        # above.
+        type type_alias[*_] = 0
+        with self.assertRaisesRegex(TypeError, ".+__typing_subst__.+tuple.+int.*"):
+            type_alias[evil][0]
+
 
 class ClassVarTests(BaseTestCase):
 
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-09-03-17-00-30.gh-issue-138479.qUxgWs.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-03-17-00-30.gh-issue-138479.qUxgWs.rst
new file mode 100644 (file)
index 0000000..c94640a
--- /dev/null
@@ -0,0 +1,2 @@
+Fix a crash when a generic object's ``__typing_subst__`` returns an object
+that isn't a :class:`tuple`.
index bf1838061e9eb1988687f8b0b960b07d85c76b05..4bb84f054dd3dd64d45feb59ee7c7bfa5b5c3962 100644 (file)
@@ -517,11 +517,22 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
             return NULL;
         }
         if (unpack) {
+            if (!PyTuple_Check(arg)) {
+                Py_DECREF(newargs);
+                Py_DECREF(item);
+                PyObject *original = PyTuple_GET_ITEM(args, iarg);
+                PyErr_Format(PyExc_TypeError,
+                             "expected __typing_subst__ of %T objects to return a tuple, not %T",
+                             original, arg);
+                Py_DECREF(arg);
+                return NULL;
+            }
             jarg = tuple_extend(&newargs, jarg,
                     &PyTuple_GET_ITEM(arg, 0), PyTuple_GET_SIZE(arg));
             Py_DECREF(arg);
             if (jarg < 0) {
                 Py_DECREF(item);
+                assert(newargs == NULL);
                 return NULL;
             }
         }