Skip to content

Commit

Permalink
pythongh-128799: Add frame of except* to traceback when wrapping a na…
Browse files Browse the repository at this point in the history
…ked exception (python#128971)

(cherry picked from commit c39ae89)
  • Loading branch information
iritkatriel committed Jan 25, 2025
1 parent b2ae234 commit 73f520d
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ PyAPI_DATA(const conversion_func) _PyEval_ConversionFuncs[];

PyAPI_FUNC(int) _PyEval_CheckExceptStarTypeValid(PyThreadState *tstate, PyObject* right);
PyAPI_FUNC(int) _PyEval_CheckExceptTypeValid(PyThreadState *tstate, PyObject* right);
PyAPI_FUNC(int) _PyEval_ExceptionGroupMatch(PyObject* exc_value, PyObject *match_type, PyObject **match, PyObject **rest);
PyAPI_FUNC(int) _PyEval_ExceptionGroupMatch(_PyInterpreterFrame *, PyObject* exc_value, PyObject *match_type, PyObject **match, PyObject **rest);
PyAPI_FUNC(void) _PyEval_FormatAwaitableError(PyThreadState *tstate, PyTypeObject *type, int oparg);
PyAPI_FUNC(void) _PyEval_FormatExcCheckArg(PyThreadState *tstate, PyObject *exc, const char *format_str, PyObject *obj);
PyAPI_FUNC(void) _PyEval_FormatExcUnbound(PyThreadState *tstate, PyCodeObject *co, int oparg);
Expand Down
27 changes: 27 additions & 0 deletions Lib/test/test_traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -2938,6 +2938,33 @@ def exc():
report = self.get_report(exc)
self.assertEqual(report, expected)

def test_exception_group_wrapped_naked(self):
# See gh-128799

def exc():
try:
raise Exception(42)
except* Exception as e:
raise

expected = (f' + Exception Group Traceback (most recent call last):\n'
f' | File "{__file__}", line {self.callable_line}, in get_exception\n'
f' | exception_or_callable()\n'
f' | ~~~~~~~~~~~~~~~~~~~~~^^\n'
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 3}, in exc\n'
f' | except* Exception as e:\n'
f' | raise\n'
f' | ExceptionGroup: (1 sub-exception)\n'
f' +-+---------------- 1 ----------------\n'
f' | Traceback (most recent call last):\n'
f' | File "{__file__}", line {exc.__code__.co_firstlineno + 2}, in exc\n'
f' | raise Exception(42)\n'
f' | Exception: 42\n'
f' +------------------------------------\n')

report = self.get_report(exc)
self.assertEqual(report, expected)

def test_KeyboardInterrupt_at_first_line_of_frame(self):
# see GH-93249
def f():
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add frame of ``except*`` to traceback when it wraps a naked exception.
2 changes: 1 addition & 1 deletion Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2340,7 +2340,7 @@ dummy_func(

match = NULL;
rest = NULL;
int res = _PyEval_ExceptionGroupMatch(exc_value, match_type,
int res = _PyEval_ExceptionGroupMatch(frame, exc_value, match_type,
&match, &rest);
DECREF_INPUTS();
ERROR_IF(res < 0, error);
Expand Down
14 changes: 12 additions & 2 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "pycore_setobject.h" // _PySet_Update()
#include "pycore_sliceobject.h" // _PyBuildSlice_ConsumeRefs
#include "pycore_sysmodule.h" // _PySys_Audit()
#include "pycore_traceback.h" // _PyTraceBack_FromFrame
#include "pycore_tuple.h" // _PyTuple_ITEMS()
#include "pycore_typeobject.h" // _PySuper_Lookup()
#include "pycore_uop_ids.h" // Uops
Expand Down Expand Up @@ -1991,8 +1992,8 @@ do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause)
*/

int
_PyEval_ExceptionGroupMatch(PyObject* exc_value, PyObject *match_type,
PyObject **match, PyObject **rest)
_PyEval_ExceptionGroupMatch(_PyInterpreterFrame *frame, PyObject* exc_value,
PyObject *match_type, PyObject **match, PyObject **rest)
{
if (Py_IsNone(exc_value)) {
*match = Py_NewRef(Py_None);
Expand All @@ -2018,6 +2019,15 @@ _PyEval_ExceptionGroupMatch(PyObject* exc_value, PyObject *match_type,
if (wrapped == NULL) {
return -1;
}
PyFrameObject *f = _PyFrame_GetFrameObject(frame);
if (f != NULL) {
PyObject *tb = _PyTraceBack_FromFrame(NULL, f);
if (tb == NULL) {
return -1;
}
PyException_SetTraceback(wrapped, tb);
Py_DECREF(tb);
}
*match = wrapped;
}
*rest = Py_NewRef(Py_None);
Expand Down
2 changes: 1 addition & 1 deletion Python/executor_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Python/generated_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 73f520d

Please sign in to comment.