From 73f520d3c1890b1f9434ebf52d28ece37a936686 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Sat, 25 Jan 2025 13:00:23 +0000 Subject: [PATCH] gh-128799: Add frame of except* to traceback when wrapping a naked exception (#128971) (cherry picked from commit c39ae8922bad3e5ceeafa05891536c1584b6f3db) --- Include/internal/pycore_ceval.h | 2 +- Lib/test/test_traceback.py | 27 +++++++++++++++++++ ...-01-18-01-06-58.gh-issue-128799.vSNagk.rst | 1 + Python/bytecodes.c | 2 +- Python/ceval.c | 14 ++++++++-- Python/executor_cases.c.h | 2 +- Python/generated_cases.c.h | 2 +- 7 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-18-01-06-58.gh-issue-128799.vSNagk.rst diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index 043f5957d481e5..746b21ba744596 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -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); diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 2f543bccf566d5..f89fe8b26dc1bf 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -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(): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-18-01-06-58.gh-issue-128799.vSNagk.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-18-01-06-58.gh-issue-128799.vSNagk.rst new file mode 100644 index 00000000000000..eb2361bb5d4525 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-18-01-06-58.gh-issue-128799.vSNagk.rst @@ -0,0 +1 @@ +Add frame of ``except*`` to traceback when it wraps a naked exception. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 1e6185d3c9e489..e9aec83d30b512 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -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); diff --git a/Python/ceval.c b/Python/ceval.c index 2f67d40874ba41..fe5dfc9b98a5d9 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -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 @@ -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); @@ -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); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index df7c19a80e40c5..b7a36c2031cccb 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2447,7 +2447,7 @@ } match = NULL; rest = NULL; - int res = _PyEval_ExceptionGroupMatch(exc_value, match_type, + int res = _PyEval_ExceptionGroupMatch(frame, exc_value, match_type, &match, &rest); Py_DECREF(exc_value); Py_DECREF(match_type); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 1488e4215cf579..73e36182759425 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2109,7 +2109,7 @@ } match = NULL; rest = NULL; - int res = _PyEval_ExceptionGroupMatch(exc_value, match_type, + int res = _PyEval_ExceptionGroupMatch(frame, exc_value, match_type, &match, &rest); Py_DECREF(exc_value); Py_DECREF(match_type);