Skip to content

Commit c39ae89

Browse files
authored
gh-128799: Add frame of except* to traceback when wrapping a naked exception (#128971)
1 parent 9e52e55 commit c39ae89

File tree

7 files changed

+44
-6
lines changed

7 files changed

+44
-6
lines changed

Include/internal/pycore_ceval.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ PyAPI_DATA(const size_t) _Py_FunctionAttributeOffsets[];
264264

265265
PyAPI_FUNC(int) _PyEval_CheckExceptStarTypeValid(PyThreadState *tstate, PyObject* right);
266266
PyAPI_FUNC(int) _PyEval_CheckExceptTypeValid(PyThreadState *tstate, PyObject* right);
267-
PyAPI_FUNC(int) _PyEval_ExceptionGroupMatch(PyObject* exc_value, PyObject *match_type, PyObject **match, PyObject **rest);
267+
PyAPI_FUNC(int) _PyEval_ExceptionGroupMatch(_PyInterpreterFrame *, PyObject* exc_value, PyObject *match_type, PyObject **match, PyObject **rest);
268268
PyAPI_FUNC(void) _PyEval_FormatAwaitableError(PyThreadState *tstate, PyTypeObject *type, int oparg);
269269
PyAPI_FUNC(void) _PyEval_FormatExcCheckArg(PyThreadState *tstate, PyObject *exc, const char *format_str, PyObject *obj);
270270
PyAPI_FUNC(void) _PyEval_FormatExcUnbound(PyThreadState *tstate, PyCodeObject *co, int oparg);

Lib/test/test_traceback.py

+27
Original file line numberDiff line numberDiff line change
@@ -2937,6 +2937,33 @@ def exc():
29372937
report = self.get_report(exc)
29382938
self.assertEqual(report, expected)
29392939

2940+
def test_exception_group_wrapped_naked(self):
2941+
# See gh-128799
2942+
2943+
def exc():
2944+
try:
2945+
raise Exception(42)
2946+
except* Exception as e:
2947+
raise
2948+
2949+
expected = (f' + Exception Group Traceback (most recent call last):\n'
2950+
f' | File "{__file__}", line {self.callable_line}, in get_exception\n'
2951+
f' | exception_or_callable()\n'
2952+
f' | ~~~~~~~~~~~~~~~~~~~~~^^\n'
2953+
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 3}, in exc\n'
2954+
f' | except* Exception as e:\n'
2955+
f' | raise\n'
2956+
f' | ExceptionGroup: (1 sub-exception)\n'
2957+
f' +-+---------------- 1 ----------------\n'
2958+
f' | Traceback (most recent call last):\n'
2959+
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 2}, in exc\n'
2960+
f' | raise Exception(42)\n'
2961+
f' | Exception: 42\n'
2962+
f' +------------------------------------\n')
2963+
2964+
report = self.get_report(exc)
2965+
self.assertEqual(report, expected)
2966+
29402967
def test_KeyboardInterrupt_at_first_line_of_frame(self):
29412968
# see GH-93249
29422969
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
@@ -2717,7 +2717,7 @@ dummy_func(
27172717

27182718
PyObject *match_o = NULL;
27192719
PyObject *rest_o = NULL;
2720-
int res = _PyEval_ExceptionGroupMatch(exc_value, match_type,
2720+
int res = _PyEval_ExceptionGroupMatch(frame, exc_value, match_type,
27212721
&match_o, &rest_o);
27222722
DECREF_INPUTS();
27232723
ERROR_IF(res < 0, error);

Python/ceval.c

+12-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "pycore_range.h" // _PyRangeIterObject
2828
#include "pycore_setobject.h" // _PySet_Update()
2929
#include "pycore_sliceobject.h" // _PyBuildSlice_ConsumeRefs
30+
#include "pycore_traceback.h" // _PyTraceBack_FromFrame
3031
#include "pycore_tuple.h" // _PyTuple_ITEMS()
3132
#include "pycore_uop_ids.h" // Uops
3233
#include "pycore_pyerrors.h"
@@ -2074,8 +2075,8 @@ do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause)
20742075
*/
20752076

20762077
int
2077-
_PyEval_ExceptionGroupMatch(PyObject* exc_value, PyObject *match_type,
2078-
PyObject **match, PyObject **rest)
2078+
_PyEval_ExceptionGroupMatch(_PyInterpreterFrame *frame, PyObject* exc_value,
2079+
PyObject *match_type, PyObject **match, PyObject **rest)
20792080
{
20802081
if (Py_IsNone(exc_value)) {
20812082
*match = Py_NewRef(Py_None);
@@ -2101,6 +2102,15 @@ _PyEval_ExceptionGroupMatch(PyObject* exc_value, PyObject *match_type,
21012102
if (wrapped == NULL) {
21022103
return -1;
21032104
}
2105+
PyFrameObject *f = _PyFrame_GetFrameObject(frame);
2106+
if (f != NULL) {
2107+
PyObject *tb = _PyTraceBack_FromFrame(NULL, f);
2108+
if (tb == NULL) {
2109+
return -1;
2110+
}
2111+
PyException_SetTraceback(wrapped, tb);
2112+
Py_DECREF(tb);
2113+
}
21042114
*match = wrapped;
21052115
}
21062116
*rest = Py_NewRef(Py_None);

Python/executor_cases.c.h

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

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)