From 368947d362dff5a37eb47eb80d1c1dff18623d9d Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Thu, 16 Mar 2023 14:35:25 +0000 Subject: [PATCH] gh-102755: Add PyErr_DisplayException(exc) --- Doc/c-api/exceptions.rst | 5 ++ Doc/data/stable_abi.dat | 1 + Include/internal/pycore_pylifecycle.h | 1 + Include/pythonrun.h | 1 + Modules/_testcapi/exceptions.c | 13 +--- Python/pylifecycle.c | 29 +++------ Python/pythonrun.c | 87 +++++++++++++-------------- Python/sysmodule.c | 2 +- 8 files changed, 61 insertions(+), 78 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 8836c973f1f5798..506333bc912175b 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -86,6 +86,11 @@ Printing and clearing An exception must be set when calling this function. +.. c:function: void PyErr_DisplayException(PyObject *exc) + + Print the standard traceback of ``exc`` to ``sys.stderr``. + + .. versionadded:: 3.12 Raising exceptions ================== diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 68ab0b5061f4348..4cc06d22baaa939 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -133,6 +133,7 @@ function,PyErr_BadInternalCall,3.2,, function,PyErr_CheckSignals,3.2,, function,PyErr_Clear,3.2,, function,PyErr_Display,3.2,, +function,PyErr_DisplayException,3.12,, function,PyErr_ExceptionMatches,3.2,, function,PyErr_Fetch,3.2,, function,PyErr_Format,3.2,, diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index e7a318072052549..a899e848bb8b3cb 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -87,6 +87,7 @@ PyAPI_FUNC(PyObject*) _PyErr_WriteUnraisableDefaultHook(PyObject *unraisable); PyAPI_FUNC(void) _PyErr_Print(PyThreadState *tstate); PyAPI_FUNC(void) _PyErr_Display(PyObject *file, PyObject *exception, PyObject *value, PyObject *tb); +PyAPI_FUNC(void) _PyErr_DisplayException(PyObject *file, PyObject *exc); PyAPI_FUNC(void) _PyThreadState_DeleteCurrent(PyThreadState *tstate); diff --git a/Include/pythonrun.h b/Include/pythonrun.h index 1b208b734ab1bfe..41d82e89f848762 100644 --- a/Include/pythonrun.h +++ b/Include/pythonrun.h @@ -12,6 +12,7 @@ PyAPI_FUNC(PyObject *) Py_CompileString(const char *, const char *, int); PyAPI_FUNC(void) PyErr_Print(void); PyAPI_FUNC(void) PyErr_PrintEx(int); PyAPI_FUNC(void) PyErr_Display(PyObject *, PyObject *, PyObject *); +PyAPI_FUNC(void) PyErr_DisplayException(PyObject *); /* Stuff with no proper home (yet) */ diff --git a/Modules/_testcapi/exceptions.c b/Modules/_testcapi/exceptions.c index c64b823663c32f1..1922ca3beb79161 100644 --- a/Modules/_testcapi/exceptions.c +++ b/Modules/_testcapi/exceptions.c @@ -39,20 +39,13 @@ err_restore(PyObject *self, PyObject *args) { static PyObject * exception_print(PyObject *self, PyObject *args) { - PyObject *value; - PyObject *tb = NULL; + PyObject *exc; - if (!PyArg_ParseTuple(args, "O:exception_print", &value)) { + if (!PyArg_ParseTuple(args, "O:exception_print", &exc)) { return NULL; } - if (PyExceptionInstance_Check(value)) { - tb = PyException_GetTraceback(value); - } - - PyErr_Display((PyObject *) Py_TYPE(value), value, tb); - Py_XDECREF(tb); - + PyErr_DisplayException(exc); Py_RETURN_NONE; } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 82e94090a6027ad..d0c65cc1f7fd44b 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -2537,41 +2537,28 @@ _Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp, static int _Py_FatalError_PrintExc(PyThreadState *tstate) { - PyObject *ferr, *res; - PyObject *exception, *v, *tb; - int has_tb; - - _PyErr_Fetch(tstate, &exception, &v, &tb); - if (exception == NULL) { + PyObject *exc = _PyErr_GetRaisedException(tstate); + if (exc == NULL) { /* No current exception */ return 0; } - ferr = _PySys_GetAttr(tstate, &_Py_ID(stderr)); + PyObject *ferr = _PySys_GetAttr(tstate, &_Py_ID(stderr)); if (ferr == NULL || ferr == Py_None) { /* sys.stderr is not set yet or set to None, no need to try to display the exception */ return 0; } - _PyErr_NormalizeException(tstate, &exception, &v, &tb); - if (tb == NULL) { - tb = Py_NewRef(Py_None); - } - PyException_SetTraceback(v, tb); - if (exception == NULL) { - /* PyErr_NormalizeException() failed */ - return 0; - } + PyErr_DisplayException(exc); - has_tb = (tb != Py_None); - PyErr_Display(exception, v, tb); - Py_XDECREF(exception); - Py_XDECREF(v); + PyObject *tb = PyException_GetTraceback(exc); + int has_tb = (tb != NULL) && (tb != Py_None); Py_XDECREF(tb); + Py_XDECREF(exc); /* sys.stderr may be buffered: call sys.stderr.flush() */ - res = PyObject_CallMethodNoArgs(ferr, &_Py_ID(flush)); + PyObject *res = PyObject_CallMethodNoArgs(ferr, &_Py_ID(flush)); if (res == NULL) { _PyErr_Clear(tstate); } diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 34a44dd92847ba9..bb17cfd779a8574 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -764,39 +764,34 @@ handle_system_exit(void) static void _PyErr_PrintEx(PyThreadState *tstate, int set_sys_last_vars) { - PyObject *exception, *v, *tb, *hook; - + PyObject *typ = NULL, *tb = NULL; handle_system_exit(); - _PyErr_Fetch(tstate, &exception, &v, &tb); - if (exception == NULL) { + PyObject *exc = _PyErr_GetRaisedException(tstate); + if (exc == NULL) { goto done; } - - _PyErr_NormalizeException(tstate, &exception, &v, &tb); + assert(PyExceptionInstance_Check(exc)); + typ = Py_NewRef(Py_TYPE(exc)); + tb = PyException_GetTraceback(exc); if (tb == NULL) { tb = Py_NewRef(Py_None); } - PyException_SetTraceback(v, tb); - if (exception == NULL) { - goto done; - } - /* Now we know v != NULL too */ if (set_sys_last_vars) { - if (_PySys_SetAttr(&_Py_ID(last_type), exception) < 0) { + if (_PySys_SetAttr(&_Py_ID(last_type), typ) < 0) { _PyErr_Clear(tstate); } - if (_PySys_SetAttr(&_Py_ID(last_value), v) < 0) { + if (_PySys_SetAttr(&_Py_ID(last_value), exc) < 0) { _PyErr_Clear(tstate); } if (_PySys_SetAttr(&_Py_ID(last_traceback), tb) < 0) { _PyErr_Clear(tstate); } } - hook = _PySys_GetAttr(tstate, &_Py_ID(excepthook)); + PyObject *hook = _PySys_GetAttr(tstate, &_Py_ID(excepthook)); if (_PySys_Audit(tstate, "sys.excepthook", "OOOO", hook ? hook : Py_None, - exception, v, tb) < 0) { + typ, exc, tb) < 0) { if (PyErr_ExceptionMatches(PyExc_RuntimeError)) { PyErr_Clear(); goto done; @@ -805,46 +800,34 @@ _PyErr_PrintEx(PyThreadState *tstate, int set_sys_last_vars) } if (hook) { PyObject* stack[3]; - PyObject *result; - - stack[0] = exception; - stack[1] = v; + stack[0] = typ; + stack[1] = exc; stack[2] = tb; - result = _PyObject_FastCall(hook, stack, 3); + PyObject *result = _PyObject_FastCall(hook, stack, 3); if (result == NULL) { handle_system_exit(); - PyObject *exception2, *v2, *tb2; - _PyErr_Fetch(tstate, &exception2, &v2, &tb2); - _PyErr_NormalizeException(tstate, &exception2, &v2, &tb2); - /* It should not be possible for exception2 or v2 - to be NULL. However PyErr_Display() can't - tolerate NULLs, so just be safe. */ - if (exception2 == NULL) { - exception2 = Py_NewRef(Py_None); - } - if (v2 == NULL) { - v2 = Py_NewRef(Py_None); - } + PyObject *exc2 = _PyErr_GetRaisedException(tstate); + assert(exc2 && PyExceptionInstance_Check(exc2)); fflush(stdout); PySys_WriteStderr("Error in sys.excepthook:\n"); - PyErr_Display(exception2, v2, tb2); + PyErr_DisplayException(exc2); PySys_WriteStderr("\nOriginal exception was:\n"); - PyErr_Display(exception, v, tb); - Py_DECREF(exception2); - Py_DECREF(v2); - Py_XDECREF(tb2); + PyErr_DisplayException(exc); + Py_DECREF(exc2); + } + else { + Py_DECREF(result); } - Py_XDECREF(result); } else { PySys_WriteStderr("sys.excepthook is missing\n"); - PyErr_Display(exception, v, tb); + PyErr_DisplayException(exc); } done: - Py_XDECREF(exception); - Py_XDECREF(v); + Py_XDECREF(typ); + Py_XDECREF(exc); Py_XDECREF(tb); } @@ -1508,7 +1491,7 @@ print_exception_recursive(struct exception_print_context *ctx, PyObject *value) #define PyErr_MAX_GROUP_DEPTH 10 void -_PyErr_Display(PyObject *file, PyObject *exception, PyObject *value, PyObject *tb) +_PyErr_Display(PyObject *file, PyObject *unused, PyObject *value, PyObject *tb) { assert(file != NULL && file != Py_None); if (PyExceptionInstance_Check(value) @@ -1516,10 +1499,12 @@ _PyErr_Display(PyObject *file, PyObject *exception, PyObject *value, PyObject *t /* Put the traceback on the exception, otherwise it won't get displayed. See issue #18776. */ PyObject *cur_tb = PyException_GetTraceback(value); - if (cur_tb == NULL) + if (cur_tb == NULL) { PyException_SetTraceback(value, tb); - else + } + else { Py_DECREF(cur_tb); + } } struct exception_print_context ctx; @@ -1555,7 +1540,7 @@ _PyErr_Display(PyObject *file, PyObject *exception, PyObject *value, PyObject *t } void -PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb) +PyErr_Display(PyObject *unused, PyObject *value, PyObject *tb) { PyThreadState *tstate = _PyThreadState_GET(); PyObject *file = _PySys_GetAttr(tstate, &_Py_ID(stderr)); @@ -1568,10 +1553,20 @@ PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb) return; } Py_INCREF(file); - _PyErr_Display(file, exception, value, tb); + _PyErr_Display(file, NULL, value, tb); Py_DECREF(file); } +void _PyErr_DisplayException(PyObject *file, PyObject *exc) +{ + _PyErr_Display(file, NULL, exc, NULL); +} + +void PyErr_DisplayException(PyObject *exc) +{ + PyErr_Display(NULL, exc, NULL); +} + PyObject * PyRun_StringFlags(const char *str, int start, PyObject *globals, PyObject *locals, PyCompilerFlags *flags) diff --git a/Python/sysmodule.c b/Python/sysmodule.c index d282104dcad4145..cc5b9a6d418bfa3 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -742,7 +742,7 @@ sys_excepthook_impl(PyObject *module, PyObject *exctype, PyObject *value, PyObject *traceback) /*[clinic end generated code: output=18d99fdda21b6b5e input=ecf606fa826f19d9]*/ { - PyErr_Display(exctype, value, traceback); + PyErr_Display(NULL, value, traceback); Py_RETURN_NONE; }