Skip to content

Commit 649e421

Browse files
[3.13] gh-155752: Do not crash when GenericAlias parameters change during substitution (GH-155761) (#155772)
gh-155752: Do not crash when GenericAlias parameters change during substitution (GH-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. (cherry picked from commit c0006fa) Co-authored-by: Darius Houle <dariushoule@gmail.com>
1 parent 91245cb commit 649e421

3 files changed

Lines changed: 30 additions & 2 deletions

File tree

Lib/test/test_typing.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5771,6 +5771,22 @@ class A:
57715771
with self.assertRaises(TypeError):
57725772
a[int]
57735773

5774+
def test_parameter_added_after_parameters_cached(self):
5775+
# gh-155752: GenericAlias parameters are cached before substitution, so
5776+
# an argument can gain __typing_subst__ after the tuple is calculated.
5777+
class Parameter:
5778+
pass
5779+
5780+
first = Parameter()
5781+
first.__typing_subst__ = lambda value: value
5782+
late = Parameter()
5783+
alias = types.GenericAlias(dict, (first, late))
5784+
self.assertEqual(alias.__parameters__, (first,))
5785+
late.__typing_subst__ = lambda value: value
5786+
5787+
with self.assertRaisesRegex(TypeError, "not found in __parameters__"):
5788+
alias[0]
5789+
57745790
def test_return_non_tuple_while_unpacking(self):
57755791
# GH-138497: GenericAlias objects didn't ensure that __typing_subst__ actually
57765792
# returned a tuple
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a crash when a :class:`types.GenericAlias` argument gains a
2+
``__typing_subst__`` hook after the alias parameters have been cached.

Objects/genericaliasobject.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -520,8 +520,18 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
520520
}
521521
if (subst) {
522522
Py_ssize_t iparam = tuple_index(parameters, nparams, arg);
523-
assert(iparam >= 0);
524-
arg = PyObject_CallOneArg(subst, argitems[iparam]);
523+
if (iparam < 0) {
524+
// __parameters__ may be stale if an argument gained
525+
// __typing_subst__ after the tuple was computed.
526+
PyErr_Format(PyExc_TypeError,
527+
"argument %R with __typing_subst__ was not found "
528+
"in __parameters__",
529+
arg);
530+
arg = NULL;
531+
}
532+
else {
533+
arg = PyObject_CallOneArg(subst, argitems[iparam]);
534+
}
525535
Py_DECREF(subst);
526536
}
527537
else {

0 commit comments

Comments
 (0)