Skip to content

Commit f65ac86

Browse files
[3.12] gh-128049: Fix type confusion bug with the return value of a custom ExceptionGroup split function (GH-128079) (#128140)
gh-128049: Fix type confusion bug with the return value of a custom ExceptionGroup split function (GH-128079) (cherry picked from commit 3879ca0) Co-authored-by: Nico-Posada <[email protected]>
1 parent 23e8fc2 commit f65ac86

File tree

3 files changed

+67
-2
lines changed

3 files changed

+67
-2
lines changed

Lib/test/test_except_star.py

+43
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,49 @@ def derive(self, excs):
952952
self.assertExceptionIsLike(tes, FalsyEG("eg", [TypeError(1)]))
953953
self.assertExceptionIsLike(ves, FalsyEG("eg", [ValueError(2)]))
954954

955+
def test_exception_group_subclass_with_bad_split_func(self):
956+
# see gh-128049.
957+
class BadEG1(ExceptionGroup):
958+
def split(self, *args):
959+
return "NOT A 2-TUPLE!"
960+
961+
class BadEG2(ExceptionGroup):
962+
def split(self, *args):
963+
return ("NOT A 2-TUPLE!",)
964+
965+
eg_list = [
966+
(BadEG1("eg", [OSError(123), ValueError(456)]),
967+
r"split must return a tuple, not str"),
968+
(BadEG2("eg", [OSError(123), ValueError(456)]),
969+
r"split must return a 2-tuple, got tuple of size 1")
970+
]
971+
972+
for eg_class, msg in eg_list:
973+
with self.assertRaisesRegex(TypeError, msg) as m:
974+
try:
975+
raise eg_class
976+
except* ValueError:
977+
pass
978+
except* OSError:
979+
pass
980+
981+
self.assertExceptionIsLike(m.exception.__context__, eg_class)
982+
983+
# we allow tuples of length > 2 for backwards compatibility
984+
class WeirdEG(ExceptionGroup):
985+
def split(self, *args):
986+
return super().split(*args) + ("anything", 123456, None)
987+
988+
try:
989+
raise WeirdEG("eg", [OSError(123), ValueError(456)])
990+
except* OSError as e:
991+
oeg = e
992+
except* ValueError as e:
993+
veg = e
994+
995+
self.assertExceptionIsLike(oeg, WeirdEG("eg", [OSError(123)]))
996+
self.assertExceptionIsLike(veg, WeirdEG("eg", [ValueError(456)]))
997+
955998

956999
class TestExceptStarCleanup(ExceptStarTest):
9571000
def test_sys_exception_restored(self):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fix a bug where :keyword:`except* <except_star>` does not properly check the
2+
return value of an :exc:`ExceptionGroup`'s :meth:`~BaseExceptionGroup.split`
3+
function, leading to a crash in some cases. Now when :meth:`~BaseExceptionGroup.split`
4+
returns an invalid object, :keyword:`except* <except_star>` raises a :exc:`TypeError`
5+
with the original raised :exc:`ExceptionGroup` object chained to it.

Python/ceval.c

+19-2
Original file line numberDiff line numberDiff line change
@@ -1898,8 +1898,25 @@ exception_group_match(PyObject* exc_value, PyObject *match_type,
18981898
if (pair == NULL) {
18991899
return -1;
19001900
}
1901-
assert(PyTuple_CheckExact(pair));
1902-
assert(PyTuple_GET_SIZE(pair) == 2);
1901+
1902+
if (!PyTuple_CheckExact(pair)) {
1903+
PyErr_Format(PyExc_TypeError,
1904+
"%.200s.split must return a tuple, not %.200s",
1905+
Py_TYPE(exc_value)->tp_name, Py_TYPE(pair)->tp_name);
1906+
Py_DECREF(pair);
1907+
return -1;
1908+
}
1909+
1910+
// allow tuples of length > 2 for backwards compatibility
1911+
if (PyTuple_GET_SIZE(pair) < 2) {
1912+
PyErr_Format(PyExc_TypeError,
1913+
"%.200s.split must return a 2-tuple, "
1914+
"got tuple of size %zd",
1915+
Py_TYPE(exc_value)->tp_name, PyTuple_GET_SIZE(pair));
1916+
Py_DECREF(pair);
1917+
return -1;
1918+
}
1919+
19031920
*match = Py_NewRef(PyTuple_GET_ITEM(pair, 0));
19041921
*rest = Py_NewRef(PyTuple_GET_ITEM(pair, 1));
19051922
Py_DECREF(pair);

0 commit comments

Comments
 (0)