gh-102755: Add PyErr_DisplayException(exc) by iritkatriel · Pull Request #102756 · python/cpython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Doc/c-api/exceptions.rst
1 change: 1 addition & 0 deletions Doc/data/stable_abi.dat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------------------

Expand Down Expand Up @@ -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
-------
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_pylifecycle.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
1 change: 1 addition & 0 deletions Include/pythonrun.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) */
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_stable_abi_ctypes.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -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.
2 changes: 2 additions & 0 deletions Misc/stable_abi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
13 changes: 3 additions & 10 deletions Modules/_testcapi/exceptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
1 change: 1 addition & 0 deletions PC/python3dll.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 8 additions & 21 deletions Python/pylifecycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Comment thread
iritkatriel marked this conversation as resolved.
/* 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);
}
Expand Down
87 changes: 41 additions & 46 deletions Python/pythonrun.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}

Expand Down Expand Up @@ -1505,18 +1488,20 @@ 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)
&& tb != NULL && PyTraceBack_Check(tb)) {
/* 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;
Expand Down Expand Up @@ -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));
Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion Python/sysmodule.c