mirror of https://github.com/python/cpython
gh-102755: Add PyErr_DisplayException(exc) (#102756)
This commit is contained in:
parent
405739f916
commit
3f9285a8c5
|
@ -86,6 +86,12 @@ Printing and clearing
|
|||
|
||||
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
|
||||
==================
|
||||
|
|
|
@ -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,,
|
||||
|
|
|
@ -944,6 +944,10 @@ New Features
|
|||
the :attr:`~BaseException.args` passed to the exception's constructor.
|
||||
(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
|
||||
----------------------
|
||||
|
||||
|
@ -1077,6 +1081,9 @@ Deprecated
|
|||
:c:func:`PyErr_SetRaisedException` instead.
|
||||
(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
|
||||
-------
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -166,6 +166,7 @@ SYMBOL_NAMES = (
|
|||
"PyErr_CheckSignals",
|
||||
"PyErr_Clear",
|
||||
"PyErr_Display",
|
||||
"PyErr_DisplayException",
|
||||
"PyErr_ExceptionMatches",
|
||||
"PyErr_Fetch",
|
||||
"PyErr_Format",
|
||||
|
|
|
@ -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.
|
|
@ -609,6 +609,8 @@
|
|||
added = '3.2'
|
||||
[function.PyErr_Display]
|
||||
added = '3.2'
|
||||
[function.PyErr_DisplayException]
|
||||
added = '3.12'
|
||||
[function.PyErr_ExceptionMatches]
|
||||
added = '3.2'
|
||||
[function.PyErr_Fetch]
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -192,6 +192,7 @@ EXPORT_FUNC(PyErr_BadInternalCall)
|
|||
EXPORT_FUNC(PyErr_CheckSignals)
|
||||
EXPORT_FUNC(PyErr_Clear)
|
||||
EXPORT_FUNC(PyErr_Display)
|
||||
EXPORT_FUNC(PyErr_DisplayException)
|
||||
EXPORT_FUNC(PyErr_ExceptionMatches)
|
||||
EXPORT_FUNC(PyErr_Fetch)
|
||||
EXPORT_FUNC(PyErr_Format)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -761,39 +761,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;
|
||||
|
@ -802,46 +797,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);
|
||||
}
|
||||
|
||||
|
@ -1505,7 +1488,7 @@ error:
|
|||
#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)
|
||||
|
@ -1513,11 +1496,13 @@ _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;
|
||||
ctx.file = file;
|
||||
|
@ -1552,7 +1537,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));
|
||||
|
@ -1565,10 +1550,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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue