Skip to content

Commit d2f551d

Browse files
authored
[3.12] gh-128799: Add frame of except* to traceback when wrapping a naked exception (GH-128971) (#129328)
1 parent ddb314f commit d2f551d

File tree

5 files changed

+40
-3
lines changed

5 files changed

+40
-3
lines changed

Lib/test/test_traceback.py

+25
Original file line numberDiff line numberDiff line change
@@ -2157,6 +2157,31 @@ def exc():
21572157
report = self.get_report(exc)
21582158
self.assertEqual(report, expected)
21592159

2160+
def test_exception_group_wrapped_naked(self):
2161+
# See gh-128799
2162+
2163+
def exc():
2164+
try:
2165+
raise Exception(42)
2166+
except* Exception as e:
2167+
raise
2168+
2169+
expected = (f' + Exception Group Traceback (most recent call last):\n'
2170+
f' | File "{__file__}", line {self.callable_line}, in get_exception\n'
2171+
f' | exception_or_callable()\n'
2172+
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 3}, in exc\n'
2173+
f' | except* Exception as e:\n'
2174+
f' | ExceptionGroup: (1 sub-exception)\n'
2175+
f' +-+---------------- 1 ----------------\n'
2176+
f' | Traceback (most recent call last):\n'
2177+
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 2}, in exc\n'
2178+
f' | raise Exception(42)\n'
2179+
f' | Exception: 42\n'
2180+
f' +------------------------------------\n')
2181+
2182+
report = self.get_report(exc)
2183+
self.assertEqual(report, expected)
2184+
21602185
def test_KeyboardInterrupt_at_first_line_of_frame(self):
21612186
# see GH-93249
21622187
def f():
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add frame of ``except*`` to traceback when it wraps a naked exception.

Python/bytecodes.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -2114,7 +2114,7 @@ dummy_func(
21142114

21152115
match = NULL;
21162116
rest = NULL;
2117-
int res = exception_group_match(exc_value, match_type,
2117+
int res = exception_group_match(frame, exc_value, match_type,
21182118
&match, &rest);
21192119
DECREF_INPUTS();
21202120
ERROR_IF(res < 0, error);

Python/ceval.c

+12-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "pycore_range.h" // _PyRangeIterObject
2121
#include "pycore_sliceobject.h" // _PyBuildSlice_ConsumeRefs
2222
#include "pycore_sysmodule.h" // _PySys_Audit()
23+
#include "pycore_traceback.h" // _PyTraceBack_FromFrame
2324
#include "pycore_tuple.h" // _PyTuple_ITEMS()
2425
#include "pycore_typeobject.h" // _PySuper_Lookup()
2526
#include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS
@@ -544,6 +545,7 @@ match_class(PyThreadState *tstate, PyObject *subject, PyObject *type,
544545

545546
static int do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause);
546547
static int exception_group_match(
548+
_PyInterpreterFrame *frame,
547549
PyObject* exc_value, PyObject *match_type,
548550
PyObject **match, PyObject **rest);
549551

@@ -1856,7 +1858,7 @@ do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause)
18561858
*/
18571859

18581860
static int
1859-
exception_group_match(PyObject* exc_value, PyObject *match_type,
1861+
exception_group_match(_PyInterpreterFrame *frame, PyObject* exc_value, PyObject *match_type,
18601862
PyObject **match, PyObject **rest)
18611863
{
18621864
if (Py_IsNone(exc_value)) {
@@ -1883,6 +1885,15 @@ exception_group_match(PyObject* exc_value, PyObject *match_type,
18831885
if (wrapped == NULL) {
18841886
return -1;
18851887
}
1888+
PyFrameObject *f = _PyFrame_GetFrameObject(frame);
1889+
if (f != NULL) {
1890+
PyObject *tb = _PyTraceBack_FromFrame(NULL, f);
1891+
if (tb == NULL) {
1892+
return -1;
1893+
}
1894+
PyException_SetTraceback(wrapped, tb);
1895+
Py_DECREF(tb);
1896+
}
18861897
*match = wrapped;
18871898
}
18881899
*rest = Py_NewRef(Py_None);

Python/generated_cases.c.h

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)