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.
|
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
|
||||||
==================
|
==================
|
||||||
|
|
|
@ -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,,
|
||||||
|
|
|
@ -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
|
||||||
-------
|
-------
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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) */
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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'
|
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]
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue