Skip to content

Commit

Permalink
gh-128049: Fix type confusion bug with the return value of a custom E…
Browse files Browse the repository at this point in the history
…xceptionGroup split function (#128079)
  • Loading branch information
Nico-Posada authored Dec 20, 2024
1 parent 5a584c8 commit 3879ca0
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 2 deletions.
43 changes: 43 additions & 0 deletions Lib/test/test_except_star.py
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,49 @@ def derive(self, excs):
self.assertExceptionIsLike(tes, FalsyEG("eg", [TypeError(1)]))
self.assertExceptionIsLike(ves, FalsyEG("eg", [ValueError(2)]))

def test_exception_group_subclass_with_bad_split_func(self):
# see gh-128049.
class BadEG1(ExceptionGroup):
def split(self, *args):
return "NOT A 2-TUPLE!"

class BadEG2(ExceptionGroup):
def split(self, *args):
return ("NOT A 2-TUPLE!",)

eg_list = [
(BadEG1("eg", [OSError(123), ValueError(456)]),
r"split must return a tuple, not str"),
(BadEG2("eg", [OSError(123), ValueError(456)]),
r"split must return a 2-tuple, got tuple of size 1")
]

for eg_class, msg in eg_list:
with self.assertRaisesRegex(TypeError, msg) as m:
try:
raise eg_class
except* ValueError:
pass
except* OSError:
pass

self.assertExceptionIsLike(m.exception.__context__, eg_class)

# we allow tuples of length > 2 for backwards compatibility
class WeirdEG(ExceptionGroup):
def split(self, *args):
return super().split(*args) + ("anything", 123456, None)

try:
raise WeirdEG("eg", [OSError(123), ValueError(456)])
except* OSError as e:
oeg = e
except* ValueError as e:
veg = e

self.assertExceptionIsLike(oeg, WeirdEG("eg", [OSError(123)]))
self.assertExceptionIsLike(veg, WeirdEG("eg", [ValueError(456)]))


class TestExceptStarCleanup(ExceptStarTest):
def test_sys_exception_restored(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Fix a bug where :keyword:`except* <except_star>` does not properly check the
return value of an :exc:`ExceptionGroup`'s :meth:`~BaseExceptionGroup.split`
function, leading to a crash in some cases. Now when :meth:`~BaseExceptionGroup.split`
returns an invalid object, :keyword:`except* <except_star>` raises a :exc:`TypeError`
with the original raised :exc:`ExceptionGroup` object chained to it.
21 changes: 19 additions & 2 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -2134,8 +2134,25 @@ _PyEval_ExceptionGroupMatch(PyObject* exc_value, PyObject *match_type,
if (pair == NULL) {
return -1;
}
assert(PyTuple_CheckExact(pair));
assert(PyTuple_GET_SIZE(pair) == 2);

if (!PyTuple_CheckExact(pair)) {
PyErr_Format(PyExc_TypeError,
"%.200s.split must return a tuple, not %.200s",
Py_TYPE(exc_value)->tp_name, Py_TYPE(pair)->tp_name);
Py_DECREF(pair);
return -1;
}

// allow tuples of length > 2 for backwards compatibility
if (PyTuple_GET_SIZE(pair) < 2) {
PyErr_Format(PyExc_TypeError,
"%.200s.split must return a 2-tuple, "
"got tuple of size %zd",
Py_TYPE(exc_value)->tp_name, PyTuple_GET_SIZE(pair));
Py_DECREF(pair);
return -1;
}

*match = Py_NewRef(PyTuple_GET_ITEM(pair, 0));
*rest = Py_NewRef(PyTuple_GET_ITEM(pair, 1));
Py_DECREF(pair);
Expand Down

0 comments on commit 3879ca0

Please sign in to comment.