gh-102755: Add PyErr_DisplayException(exc) (#102756)

This commit is contained in:
Irit Katriel 2023-03-16 22:18:04 +00:00 committed by GitHub
parent 405739f916
commit 3f9285a8c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 76 additions and 78 deletions

View File

@ -86,6 +86,12 @@ Printing and clearing
An exception must be set when calling this function. An exception must be set when calling this function.
.. c:function: void PyErr_DisplayException(PyObject *exc)
Print the standard traceback display of ``exc`` to ``sys.stderr``, including
chained exceptions and notes.
.. versionadded:: 3.12
Raising exceptions Raising exceptions
================== ==================

View File

@ -133,6 +133,7 @@ function,PyErr_BadInternalCall,3.2,,
function,PyErr_CheckSignals,3.2,, function,PyErr_CheckSignals,3.2,,
function,PyErr_Clear,3.2,, function,PyErr_Clear,3.2,,
function,PyErr_Display,3.2,, function,PyErr_Display,3.2,,
function,PyErr_DisplayException,3.12,,
function,PyErr_ExceptionMatches,3.2,, function,PyErr_ExceptionMatches,3.2,,
function,PyErr_Fetch,3.2,, function,PyErr_Fetch,3.2,,
function,PyErr_Format,3.2,, function,PyErr_Format,3.2,,

View File

@ -944,6 +944,10 @@ New Features
the :attr:`~BaseException.args` passed to the exception's constructor. the :attr:`~BaseException.args` passed to the exception's constructor.
(Contributed by Mark Shannon in :gh:`101578`.) (Contributed by Mark Shannon in :gh:`101578`.)
* Add :c:func:`PyErr_DisplayException`, which takes an exception instance,
to replace the legacy-api :c:func:`PyErr_Display`. (Contributed by
Irit Katriel in :gh:`102755`).
Porting to Python 3.12 Porting to Python 3.12
---------------------- ----------------------
@ -1077,6 +1081,9 @@ Deprecated
:c:func:`PyErr_SetRaisedException` instead. :c:func:`PyErr_SetRaisedException` instead.
(Contributed by Mark Shannon in :gh:`101578`.) (Contributed by Mark Shannon in :gh:`101578`.)
* :c:func:`PyErr_Display` is deprecated. Use :c:func:`PyErr_DisplayException`
instead. (Contributed by Irit Katriel in :gh:`102755`).
Removed Removed
------- -------

View File

@ -87,6 +87,7 @@ PyAPI_FUNC(PyObject*) _PyErr_WriteUnraisableDefaultHook(PyObject *unraisable);
PyAPI_FUNC(void) _PyErr_Print(PyThreadState *tstate); PyAPI_FUNC(void) _PyErr_Print(PyThreadState *tstate);
PyAPI_FUNC(void) _PyErr_Display(PyObject *file, PyObject *exception, PyAPI_FUNC(void) _PyErr_Display(PyObject *file, PyObject *exception,
PyObject *value, PyObject *tb); PyObject *value, PyObject *tb);
PyAPI_FUNC(void) _PyErr_DisplayException(PyObject *file, PyObject *exc);
PyAPI_FUNC(void) _PyThreadState_DeleteCurrent(PyThreadState *tstate); PyAPI_FUNC(void) _PyThreadState_DeleteCurrent(PyThreadState *tstate);

View File

@ -12,6 +12,7 @@ PyAPI_FUNC(PyObject *) Py_CompileString(const char *, const char *, int);
PyAPI_FUNC(void) PyErr_Print(void); PyAPI_FUNC(void) PyErr_Print(void);
PyAPI_FUNC(void) PyErr_PrintEx(int); PyAPI_FUNC(void) PyErr_PrintEx(int);
PyAPI_FUNC(void) PyErr_Display(PyObject *, PyObject *, PyObject *); PyAPI_FUNC(void) PyErr_Display(PyObject *, PyObject *, PyObject *);
PyAPI_FUNC(void) PyErr_DisplayException(PyObject *);
/* Stuff with no proper home (yet) */ /* Stuff with no proper home (yet) */

View File

@ -166,6 +166,7 @@ SYMBOL_NAMES = (
"PyErr_CheckSignals", "PyErr_CheckSignals",
"PyErr_Clear", "PyErr_Clear",
"PyErr_Display", "PyErr_Display",
"PyErr_DisplayException",
"PyErr_ExceptionMatches", "PyErr_ExceptionMatches",
"PyErr_Fetch", "PyErr_Fetch",
"PyErr_Format", "PyErr_Format",

View File

@ -0,0 +1,3 @@
Add :c:func:`PyErr_DisplayException` which takes just an exception instance,
to replace the legacy :c:func:`PyErr_Display` which takes the ``(typ, exc,
tb)`` triplet.

View File

@ -609,6 +609,8 @@
added = '3.2' added = '3.2'
[function.PyErr_Display] [function.PyErr_Display]
added = '3.2' added = '3.2'
[function.PyErr_DisplayException]
added = '3.12'
[function.PyErr_ExceptionMatches] [function.PyErr_ExceptionMatches]
added = '3.2' added = '3.2'
[function.PyErr_Fetch] [function.PyErr_Fetch]

View File

@ -39,20 +39,13 @@ err_restore(PyObject *self, PyObject *args) {
static PyObject * static PyObject *
exception_print(PyObject *self, PyObject *args) exception_print(PyObject *self, PyObject *args)
{ {
PyObject *value; PyObject *exc;
PyObject *tb = NULL;
if (!PyArg_ParseTuple(args, "O:exception_print", &value)) { if (!PyArg_ParseTuple(args, "O:exception_print", &exc)) {
return NULL; return NULL;
} }
if (PyExceptionInstance_Check(value)) { PyErr_DisplayException(exc);
tb = PyException_GetTraceback(value);
}
PyErr_Display((PyObject *) Py_TYPE(value), value, tb);
Py_XDECREF(tb);
Py_RETURN_NONE; Py_RETURN_NONE;
} }

1
PC/python3dll.c generated
View File

@ -192,6 +192,7 @@ EXPORT_FUNC(PyErr_BadInternalCall)
EXPORT_FUNC(PyErr_CheckSignals) EXPORT_FUNC(PyErr_CheckSignals)
EXPORT_FUNC(PyErr_Clear) EXPORT_FUNC(PyErr_Clear)
EXPORT_FUNC(PyErr_Display) EXPORT_FUNC(PyErr_Display)
EXPORT_FUNC(PyErr_DisplayException)
EXPORT_FUNC(PyErr_ExceptionMatches) EXPORT_FUNC(PyErr_ExceptionMatches)
EXPORT_FUNC(PyErr_Fetch) EXPORT_FUNC(PyErr_Fetch)
EXPORT_FUNC(PyErr_Format) EXPORT_FUNC(PyErr_Format)

View File

@ -2537,41 +2537,28 @@ _Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp,
static int static int
_Py_FatalError_PrintExc(PyThreadState *tstate) _Py_FatalError_PrintExc(PyThreadState *tstate)
{ {
PyObject *ferr, *res; PyObject *exc = _PyErr_GetRaisedException(tstate);
PyObject *exception, *v, *tb; if (exc == NULL) {
int has_tb;
_PyErr_Fetch(tstate, &exception, &v, &tb);
if (exception == NULL) {
/* No current exception */ /* No current exception */
return 0; return 0;
} }
ferr = _PySys_GetAttr(tstate, &_Py_ID(stderr)); PyObject *ferr = _PySys_GetAttr(tstate, &_Py_ID(stderr));
if (ferr == NULL || ferr == Py_None) { if (ferr == NULL || ferr == Py_None) {
/* sys.stderr is not set yet or set to None, /* sys.stderr is not set yet or set to None,
no need to try to display the exception */ no need to try to display the exception */
return 0; return 0;
} }
_PyErr_NormalizeException(tstate, &exception, &v, &tb); PyErr_DisplayException(exc);
if (tb == NULL) {
tb = Py_NewRef(Py_None);
}
PyException_SetTraceback(v, tb);
if (exception == NULL) {
/* PyErr_NormalizeException() failed */
return 0;
}
has_tb = (tb != Py_None); PyObject *tb = PyException_GetTraceback(exc);
PyErr_Display(exception, v, tb); int has_tb = (tb != NULL) && (tb != Py_None);
Py_XDECREF(exception);
Py_XDECREF(v);
Py_XDECREF(tb); Py_XDECREF(tb);
Py_XDECREF(exc);
/* sys.stderr may be buffered: call sys.stderr.flush() */ /* 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) { if (res == NULL) {
_PyErr_Clear(tstate); _PyErr_Clear(tstate);
} }

View File

@ -761,39 +761,34 @@ handle_system_exit(void)
static void static void
_PyErr_PrintEx(PyThreadState *tstate, int set_sys_last_vars) _PyErr_PrintEx(PyThreadState *tstate, int set_sys_last_vars)
{ {
PyObject *exception, *v, *tb, *hook; PyObject *typ = NULL, *tb = NULL;
handle_system_exit(); handle_system_exit();
_PyErr_Fetch(tstate, &exception, &v, &tb); PyObject *exc = _PyErr_GetRaisedException(tstate);
if (exception == NULL) { if (exc == NULL) {
goto done; goto done;
} }
assert(PyExceptionInstance_Check(exc));
_PyErr_NormalizeException(tstate, &exception, &v, &tb); typ = Py_NewRef(Py_TYPE(exc));
tb = PyException_GetTraceback(exc);
if (tb == NULL) { if (tb == NULL) {
tb = Py_NewRef(Py_None); 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 (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); _PyErr_Clear(tstate);
} }
if (_PySys_SetAttr(&_Py_ID(last_value), v) < 0) { if (_PySys_SetAttr(&_Py_ID(last_value), exc) < 0) {
_PyErr_Clear(tstate); _PyErr_Clear(tstate);
} }
if (_PySys_SetAttr(&_Py_ID(last_traceback), tb) < 0) { if (_PySys_SetAttr(&_Py_ID(last_traceback), tb) < 0) {
_PyErr_Clear(tstate); _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, if (_PySys_Audit(tstate, "sys.excepthook", "OOOO", hook ? hook : Py_None,
exception, v, tb) < 0) { typ, exc, tb) < 0) {
if (PyErr_ExceptionMatches(PyExc_RuntimeError)) { if (PyErr_ExceptionMatches(PyExc_RuntimeError)) {
PyErr_Clear(); PyErr_Clear();
goto done; goto done;
@ -802,46 +797,34 @@ _PyErr_PrintEx(PyThreadState *tstate, int set_sys_last_vars)
} }
if (hook) { if (hook) {
PyObject* stack[3]; PyObject* stack[3];
PyObject *result; stack[0] = typ;
stack[1] = exc;
stack[0] = exception;
stack[1] = v;
stack[2] = tb; stack[2] = tb;
result = _PyObject_FastCall(hook, stack, 3); PyObject *result = _PyObject_FastCall(hook, stack, 3);
if (result == NULL) { if (result == NULL) {
handle_system_exit(); handle_system_exit();
PyObject *exception2, *v2, *tb2; PyObject *exc2 = _PyErr_GetRaisedException(tstate);
_PyErr_Fetch(tstate, &exception2, &v2, &tb2); assert(exc2 && PyExceptionInstance_Check(exc2));
_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);
}
fflush(stdout); fflush(stdout);
PySys_WriteStderr("Error in sys.excepthook:\n"); PySys_WriteStderr("Error in sys.excepthook:\n");
PyErr_Display(exception2, v2, tb2); PyErr_DisplayException(exc2);
PySys_WriteStderr("\nOriginal exception was:\n"); PySys_WriteStderr("\nOriginal exception was:\n");
PyErr_Display(exception, v, tb); PyErr_DisplayException(exc);
Py_DECREF(exception2); Py_DECREF(exc2);
Py_DECREF(v2); }
Py_XDECREF(tb2); else {
Py_DECREF(result);
} }
Py_XDECREF(result);
} }
else { else {
PySys_WriteStderr("sys.excepthook is missing\n"); PySys_WriteStderr("sys.excepthook is missing\n");
PyErr_Display(exception, v, tb); PyErr_DisplayException(exc);
} }
done: done:
Py_XDECREF(exception); Py_XDECREF(typ);
Py_XDECREF(v); Py_XDECREF(exc);
Py_XDECREF(tb); Py_XDECREF(tb);
} }
@ -1505,7 +1488,7 @@ error:
#define PyErr_MAX_GROUP_DEPTH 10 #define PyErr_MAX_GROUP_DEPTH 10
void 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); assert(file != NULL && file != Py_None);
if (PyExceptionInstance_Check(value) if (PyExceptionInstance_Check(value)
@ -1513,10 +1496,12 @@ _PyErr_Display(PyObject *file, PyObject *exception, PyObject *value, PyObject *t
/* Put the traceback on the exception, otherwise it won't get /* Put the traceback on the exception, otherwise it won't get
displayed. See issue #18776. */ displayed. See issue #18776. */
PyObject *cur_tb = PyException_GetTraceback(value); PyObject *cur_tb = PyException_GetTraceback(value);
if (cur_tb == NULL) if (cur_tb == NULL) {
PyException_SetTraceback(value, tb); PyException_SetTraceback(value, tb);
else }
else {
Py_DECREF(cur_tb); Py_DECREF(cur_tb);
}
} }
struct exception_print_context ctx; struct exception_print_context ctx;
@ -1552,7 +1537,7 @@ _PyErr_Display(PyObject *file, PyObject *exception, PyObject *value, PyObject *t
} }
void void
PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb) PyErr_Display(PyObject *unused, PyObject *value, PyObject *tb)
{ {
PyThreadState *tstate = _PyThreadState_GET(); PyThreadState *tstate = _PyThreadState_GET();
PyObject *file = _PySys_GetAttr(tstate, &_Py_ID(stderr)); PyObject *file = _PySys_GetAttr(tstate, &_Py_ID(stderr));
@ -1565,10 +1550,20 @@ PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb)
return; return;
} }
Py_INCREF(file); Py_INCREF(file);
_PyErr_Display(file, exception, value, tb); _PyErr_Display(file, NULL, value, tb);
Py_DECREF(file); 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 * PyObject *
PyRun_StringFlags(const char *str, int start, PyObject *globals, PyRun_StringFlags(const char *str, int start, PyObject *globals,
PyObject *locals, PyCompilerFlags *flags) PyObject *locals, PyCompilerFlags *flags)

View File

@ -742,7 +742,7 @@ sys_excepthook_impl(PyObject *module, PyObject *exctype, PyObject *value,
PyObject *traceback) PyObject *traceback)
/*[clinic end generated code: output=18d99fdda21b6b5e input=ecf606fa826f19d9]*/ /*[clinic end generated code: output=18d99fdda21b6b5e input=ecf606fa826f19d9]*/
{ {
PyErr_Display(exctype, value, traceback); PyErr_Display(NULL, value, traceback);
Py_RETURN_NONE; Py_RETURN_NONE;
} }