From 9716caa4b53ef8319e439d4e5a86e4867f460362 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Sat, 18 Jan 2025 01:06:04 +0000 Subject: [PATCH 1/4] gh-128799: Add frame of except* to traceback when wrapping a naked exception --- Include/internal/pycore_ceval.h | 2 +- .../2025-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 +- 6 files changed, 17 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 80bd19a887871c..fea8665ae39ab5 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -264,7 +264,7 @@ PyAPI_DATA(const size_t) _Py_FunctionAttributeOffsets[]; 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/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..a4cdb5eedd35fa --- /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 c0ef767a9dd68b..0d1084de7a8119 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2717,7 +2717,7 @@ dummy_func( PyObject *match_o = NULL; PyObject *rest_o = NULL; - int res = _PyEval_ExceptionGroupMatch(exc_value, match_type, + int res = _PyEval_ExceptionGroupMatch(frame, exc_value, match_type, &match_o, &rest_o); DECREF_INPUTS(); ERROR_IF(res < 0, error); diff --git a/Python/ceval.c b/Python/ceval.c index 28b0b4c6de39a7..7ee71bef6d9a04 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -27,6 +27,7 @@ #include "pycore_range.h" // _PyRangeIterObject #include "pycore_setobject.h" // _PySet_Update() #include "pycore_sliceobject.h" // _PyBuildSlice_ConsumeRefs +#include "pycore_traceback.h" // _PyTraceBack_FromFrame #include "pycore_tuple.h" // _PyTuple_ITEMS() #include "pycore_uop_ids.h" // Uops #include "pycore_pyerrors.h" @@ -2094,8 +2095,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); @@ -2121,6 +2122,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 e2eaca2c90fa76..52f16fd8ee572a 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -3452,7 +3452,7 @@ PyObject *match_o = NULL; PyObject *rest_o = NULL; _PyFrame_SetStackPointer(frame, stack_pointer); - int res = _PyEval_ExceptionGroupMatch(exc_value, match_type, + int res = _PyEval_ExceptionGroupMatch(frame, exc_value, match_type, &match_o, &rest_o); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(exc_value_st); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index dc90f75f2645e1..ef2438c6506e11 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3205,7 +3205,7 @@ PyObject *match_o = NULL; PyObject *rest_o = NULL; _PyFrame_SetStackPointer(frame, stack_pointer); - int res = _PyEval_ExceptionGroupMatch(exc_value, match_type, + int res = _PyEval_ExceptionGroupMatch(frame, exc_value, match_type, &match_o, &rest_o); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(exc_value_st); From 1443bf8323c5080233610b413349cdea7471e2ea Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Sat, 18 Jan 2025 01:18:17 +0000 Subject: [PATCH 2/4] Update Misc/NEWS.d/next/Core_and_Builtins/2025-01-18-01-06-58.gh-issue-128799.vSNagk.rst --- .../2025-01-18-01-06-58.gh-issue-128799.vSNagk.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index a4cdb5eedd35fa..eb2361bb5d4525 100644 --- 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 @@ -1 +1 @@ -Add frame of `except*` to traceback when it wraps a naked exception. +Add frame of ``except*`` to traceback when it wraps a naked exception. From 65cf6e77cac5d91b09051fe40022446f69c99524 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Sat, 18 Jan 2025 23:52:47 +0000 Subject: [PATCH 3/4] add test --- Lib/test/test_traceback.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index abdfc4638f2e9c..fa9354ded6019c 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -2919,6 +2919,32 @@ 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' | 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(): From 54a6d26a17016aad9e3d92b6092007c500199dec Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Wed, 22 Jan 2025 22:55:26 +0000 Subject: [PATCH 4/4] re-break traceback --- Lib/test/test_traceback.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 9038caea32199d..c1fd469d79c6d0 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -2928,6 +2928,7 @@ def exc(): 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' pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy