Skip to content

Commit b8a9987

Browse files
[3.15] gh-155752: Do not crash when GenericAlias parameters change during substitution (GH-155761) (#155770)
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 71cb165 commit b8a9987

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
@@ -6053,6 +6053,22 @@ class A:
60536053
with self.assertRaises(TypeError):
60546054
a[int]
60556055

6056+
def test_parameter_added_after_parameters_cached(self):
6057+
# gh-155752: GenericAlias parameters are cached before substitution, so
6058+
# an argument can gain __typing_subst__ after the tuple is calculated.
6059+
class Parameter:
6060+
pass
6061+
6062+
first = Parameter()
6063+
first.__typing_subst__ = lambda value: value
6064+
late = Parameter()
6065+
alias = types.GenericAlias(dict, (first, late))
6066+
self.assertEqual(alias.__parameters__, (first,))
6067+
late.__typing_subst__ = lambda value: value
6068+
6069+
with self.assertRaisesRegex(TypeError, "not found in __parameters__"):
6070+
alias[0]
6071+
60566072
def test_return_non_tuple_while_unpacking(self):
60576073
# GH-138497: GenericAlias objects didn't ensure that __typing_subst__ actually
60586074
# 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
@@ -524,8 +524,18 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
524524
}
525525
if (subst) {
526526
Py_ssize_t iparam = tuple_index(parameters, nparams, arg);
527-
assert(iparam >= 0);
528-
arg = PyObject_CallOneArg(subst, argitems[iparam]);
527+
if (iparam < 0) {
528+
// __parameters__ may be stale if an argument gained
529+
// __typing_subst__ after the tuple was computed.
530+
PyErr_Format(PyExc_TypeError,
531+
"argument %R with __typing_subst__ was not found "
532+
"in __parameters__",
533+
arg);
534+
arg = NULL;
535+
}
536+
else {
537+
arg = PyObject_CallOneArg(subst, argitems[iparam]);
538+
}
529539
Py_DECREF(subst);
530540
}
531541
else {

0 commit comments

Comments
 (0)