gh-155752: Do not crash when GenericAlias parameters change during substitution (#155761)

JelleZijlstra <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/c0006faf4f48f01126a8a0752da8f159142e410d
commit: c0006faf4f48f01126a8a0752da8f159142e410d
branch: main
author: Darius Houle <[email protected]>
committer: JelleZijlstra <[email protected]>
date: 2026-08-13T22:14:24-07:00
summary:

gh-155752: Do not crash when GenericAlias parameters change during substitution (#155761)

An alias argument can gain __typing_subst__ after __parameters__ has been cached, including during a preparation or substitution callback. Check that the argument is present before indexing the substitution arguments.

files:
A Misc/NEWS.d/next/Core_and_Builtins/2026-08-13-13-50-00.gh-issue-155752.Rp7K2x.rst
M Lib/test/test_typing.py
M Objects/genericaliasobject.c

diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 2875303fb15619..f35f864dce21e8 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -6054,6 +6054,22 @@ class A:
                     with self.assertRaises(TypeError):
                         a[int]
 
+    def test_parameter_added_after_parameters_cached(self):
+        # gh-155752: GenericAlias parameters are cached before substitution, so
+        # an argument can gain __typing_subst__ after the tuple is calculated.
+        class Parameter:
+            pass
+
+        first = Parameter()
+        first.__typing_subst__ = lambda value: value
+        late = Parameter()
+        alias = types.GenericAlias(dict, (first, late))
+        self.assertEqual(alias.__parameters__, (first,))
+        late.__typing_subst__ = lambda value: value
+
+        with self.assertRaisesRegex(TypeError, "not found in __parameters__"):
+            alias[0]
+
     def test_return_non_tuple_while_unpacking(self):
         # GH-138497: GenericAlias objects didn't ensure that __typing_subst__ actually
         # returned a tuple
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-13-13-50-00.gh-issue-155752.Rp7K2x.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-13-13-50-00.gh-issue-155752.Rp7K2x.rst
new file mode 100644
index 00000000000000..300e97ad5d257b
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-13-13-50-00.gh-issue-155752.Rp7K2x.rst
@@ -0,0 +1,2 @@
+Fix a crash when a :class:`types.GenericAlias` argument gains a
+``__typing_subst__`` hook after the alias parameters have been cached.
diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c
index 1504adb950ef44..8bb7cc8c74a592 100644
--- a/Objects/genericaliasobject.c
+++ b/Objects/genericaliasobject.c
@@ -525,8 +525,18 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
         }
         if (subst) {
             Py_ssize_t iparam = tuple_index(parameters, nparams, arg);
-            assert(iparam >= 0);
-            arg = PyObject_CallOneArg(subst, argitems[iparam]);
+            if (iparam < 0) {
+                // __parameters__ may be stale if an argument gained
+                // __typing_subst__ after the tuple was computed.
+                PyErr_Format(PyExc_TypeError,
+                             "argument %R with __typing_subst__ was not found "
+                             "in __parameters__",
+                             arg);
+                arg = NULL;
+            }
+            else {
+                arg = PyObject_CallOneArg(subst, argitems[iparam]);
+            }
             Py_DECREF(subst);
         }
         else {

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.