From 7a1932e1143b25af924b34c0ab5086ed15c1f57d Mon Sep 17 00:00:00 2001 From: Darius Houle Date: Thu, 13 Aug 2026 13:12:26 -0600 Subject: [PATCH 1/2] gh-155752: Do not crash when GenericAlias parameters change during substitution 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. --- Lib/test/test_typing.py | 16 ++++++++++++++++ ...026-08-13-13-50-00.gh-issue-155752.Rp7K2x.rst | 2 ++ Objects/genericaliasobject.c | 14 ++++++++++++-- 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-08-13-13-50-00.gh-issue-155752.Rp7K2x.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 2875303fb156197..1bd9cad9367227a 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] + # gh-155752: GenericAlias parameters are cached before substitution, so + # an argument can gain __typing_subst__ after the tuple is calculated. + def test_parameter_added_after_parameters_cached(self): + 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 000000000000000..300e97ad5d257bc --- /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 1504adb950ef44f..8bb7cc8c74a5920 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 { From 3c9c477f508a600becaa1cda077841840b133ddd Mon Sep 17 00:00:00 2001 From: Darius Houle Date: Thu, 13 Aug 2026 16:11:33 -0600 Subject: [PATCH 2/2] Move issue comment --- Lib/test/test_typing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 1bd9cad9367227a..f35f864dce21e86 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -6054,9 +6054,9 @@ class A: with self.assertRaises(TypeError): a[int] - # gh-155752: GenericAlias parameters are cached before substitution, so - # an argument can gain __typing_subst__ after the tuple is calculated. 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