summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIrit Katriel <1055913+iritkatriel@users.noreply.github.com>2023-03-16 22:18:04 (GMT)
committerGitHub <noreply@github.com>2023-03-16 22:18:04 (GMT)
commit3f9285a8c52bf776c364f0cf4aecdd8f514ac4e1 (patch)
tree22d14446b3d59b7402b59bbad2c153b4ddcb1b21
parent405739f9166592104a5b0b945de92e28415ae972 (diff)
downloadcpython-3f9285a8c52bf776c364f0cf4aecdd8f514ac4e1.zip
cpython-3f9285a8c52bf776c364f0cf4aecdd8f514ac4e1.tar.gz
cpython-3f9285a8c52bf776c364f0cf4aecdd8f514ac4e1.tar.bz2
gh-102755: Add PyErr_DisplayException(exc) (#102756)
-rw-r--r--Doc/c-api/exceptions.rst6
-rw-r--r--Doc/data/stable_abi.dat1
-rw-r--r--Doc/whatsnew/3.12.rst7
-rw-r--r--Include/internal/pycore_pylifecycle.h1
-rw-r--r--Include/pythonrun.h1
-rw-r--r--Lib/test/test_stable_abi_ctypes.py1
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-03-16-14-44-29.gh-issue-102755.j1GxlV.rst3
-rw-r--r--Misc/stable_abi.toml2
-rw-r--r--Modules/_testcapi/exceptions.c13
-rwxr-xr-xPC/python3dll.c1
-rw-r--r--Python/pylifecycle.c29
-rw-r--r--Python/pythonrun.c87
-rw-r--r--Python/sysmodule.c2
13 files changed, 76 insertions, 78 deletions
diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst
index 8836c97..ddf7dc7 100644
--- a/Doc/c-api/exceptions.rst
+++ b/Doc/c-api/exceptions.rst
@@ -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
==================
diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat
index 68ab0b5..4cc06d2 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/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index 03b1f97..a398cd9 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -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
-------
diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h
index e7a3180..a899e84 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 1b208b7..41d82e8 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/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py
index e77c1c8..2feaaf8 100644
--- a/Lib/test/test_stable_abi_ctypes.py
+++ b/Lib/test/test_stable_abi_ctypes.py
@@ -166,6 +166,7 @@ SYMBOL_NAMES = (
"PyErr_CheckSignals",
"PyErr_Clear",
"PyErr_Display",
+ "PyErr_DisplayException",
"PyErr_ExceptionMatches",
"PyErr_Fetch",
"PyErr_Format",
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-03-16-14-44-29.gh-issue-102755.j1GxlV.rst b/Misc/NEWS.d/next/Core and Builtins/2023-03-16-14-44-29.gh-issue-102755.j1GxlV.rst
new file mode 100644
index 0000000..d09af8d
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-03-16-14-44-29.gh-issue-102755.j1GxlV.rst
@@ -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.
diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml
index 21ff961..23baeee 100644
--- a/Misc/stable_abi.toml
+++ b/Misc/stable_abi.toml
@@ -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]
diff --git a/Modules/_testcapi/exceptions.c b/Modules/_testcapi/exceptions.c
index c64b823..1922ca3b 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/PC/python3dll.c b/PC/python3dll.c
index e300819..706affa 100755
--- a/PC/python3dll.c
+++ b/PC/python3dll.c
@@ -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)
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 82e9409..d0c65cc 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 5381b10..07d119a 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -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,10 +1496,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;
@@ -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)
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index d282104..cc5b9a6 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;
}