Skip to content

Commit 9cd991f

Browse files
iritkatrielwarsaw
authored andcommitted
pythongh-102755: Add PyErr_DisplayException(exc) (python#102756)
1 parent eeb34e5 commit 9cd991f

File tree

13 files changed

+76
-78
lines changed

13 files changed

+76
-78
lines changed

Doc/c-api/exceptions.rst

+6
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ Printing and clearing
8686
8787
An exception must be set when calling this function.
8888
89+
.. c:function: void PyErr_DisplayException(PyObject *exc)
90+
91+
Print the standard traceback display of ``exc`` to ``sys.stderr``, including
92+
chained exceptions and notes.
93+
94+
.. versionadded:: 3.12
8995
9096
Raising exceptions
9197
==================

Doc/data/stable_abi.dat

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Doc/whatsnew/3.12.rst

+7
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,10 @@ New Features
947947
the :attr:`~BaseException.args` passed to the exception's constructor.
948948
(Contributed by Mark Shannon in :gh:`101578`.)
949949

950+
* Add :c:func:`PyErr_DisplayException`, which takes an exception instance,
951+
to replace the legacy-api :c:func:`PyErr_Display`. (Contributed by
952+
Irit Katriel in :gh:`102755`).
953+
950954
Porting to Python 3.12
951955
----------------------
952956

@@ -1080,6 +1084,9 @@ Deprecated
10801084
:c:func:`PyErr_SetRaisedException` instead.
10811085
(Contributed by Mark Shannon in :gh:`101578`.)
10821086

1087+
* :c:func:`PyErr_Display` is deprecated. Use :c:func:`PyErr_DisplayException`
1088+
instead. (Contributed by Irit Katriel in :gh:`102755`).
1089+
10831090

10841091
Removed
10851092
-------

Include/internal/pycore_pylifecycle.h

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ PyAPI_FUNC(PyObject*) _PyErr_WriteUnraisableDefaultHook(PyObject *unraisable);
8787
PyAPI_FUNC(void) _PyErr_Print(PyThreadState *tstate);
8888
PyAPI_FUNC(void) _PyErr_Display(PyObject *file, PyObject *exception,
8989
PyObject *value, PyObject *tb);
90+
PyAPI_FUNC(void) _PyErr_DisplayException(PyObject *file, PyObject *exc);
9091

9192
PyAPI_FUNC(void) _PyThreadState_DeleteCurrent(PyThreadState *tstate);
9293

Include/pythonrun.h

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ PyAPI_FUNC(PyObject *) Py_CompileString(const char *, const char *, int);
1212
PyAPI_FUNC(void) PyErr_Print(void);
1313
PyAPI_FUNC(void) PyErr_PrintEx(int);
1414
PyAPI_FUNC(void) PyErr_Display(PyObject *, PyObject *, PyObject *);
15+
PyAPI_FUNC(void) PyErr_DisplayException(PyObject *);
1516

1617

1718
/* Stuff with no proper home (yet) */

Lib/test/test_stable_abi_ctypes.py

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add :c:func:`PyErr_DisplayException` which takes just an exception instance,
2+
to replace the legacy :c:func:`PyErr_Display` which takes the ``(typ, exc,
3+
tb)`` triplet.

Misc/stable_abi.toml

+2
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@
609609
added = '3.2'
610610
[function.PyErr_Display]
611611
added = '3.2'
612+
[function.PyErr_DisplayException]
613+
added = '3.12'
612614
[function.PyErr_ExceptionMatches]
613615
added = '3.2'
614616
[function.PyErr_Fetch]

Modules/_testcapi/exceptions.c

+3-10
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,13 @@ err_restore(PyObject *self, PyObject *args) {
3939
static PyObject *
4040
exception_print(PyObject *self, PyObject *args)
4141
{
42-
PyObject *value;
43-
PyObject *tb = NULL;
42+
PyObject *exc;
4443

45-
if (!PyArg_ParseTuple(args, "O:exception_print", &value)) {
44+
if (!PyArg_ParseTuple(args, "O:exception_print", &exc)) {
4645
return NULL;
4746
}
4847

49-
if (PyExceptionInstance_Check(value)) {
50-
tb = PyException_GetTraceback(value);
51-
}
52-
53-
PyErr_Display((PyObject *) Py_TYPE(value), value, tb);
54-
Py_XDECREF(tb);
55-
48+
PyErr_DisplayException(exc);
5649
Py_RETURN_NONE;
5750
}
5851

PC/python3dll.c

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/pylifecycle.c

+8-21
Original file line numberDiff line numberDiff line change
@@ -2537,41 +2537,28 @@ _Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp,
25372537
static int
25382538
_Py_FatalError_PrintExc(PyThreadState *tstate)
25392539
{
2540-
PyObject *ferr, *res;
2541-
PyObject *exception, *v, *tb;
2542-
int has_tb;
2543-
2544-
_PyErr_Fetch(tstate, &exception, &v, &tb);
2545-
if (exception == NULL) {
2540+
PyObject *exc = _PyErr_GetRaisedException(tstate);
2541+
if (exc == NULL) {
25462542
/* No current exception */
25472543
return 0;
25482544
}
25492545

2550-
ferr = _PySys_GetAttr(tstate, &_Py_ID(stderr));
2546+
PyObject *ferr = _PySys_GetAttr(tstate, &_Py_ID(stderr));
25512547
if (ferr == NULL || ferr == Py_None) {
25522548
/* sys.stderr is not set yet or set to None,
25532549
no need to try to display the exception */
25542550
return 0;
25552551
}
25562552

2557-
_PyErr_NormalizeException(tstate, &exception, &v, &tb);
2558-
if (tb == NULL) {
2559-
tb = Py_NewRef(Py_None);
2560-
}
2561-
PyException_SetTraceback(v, tb);
2562-
if (exception == NULL) {
2563-
/* PyErr_NormalizeException() failed */
2564-
return 0;
2565-
}
2553+
PyErr_DisplayException(exc);
25662554

2567-
has_tb = (tb != Py_None);
2568-
PyErr_Display(exception, v, tb);
2569-
Py_XDECREF(exception);
2570-
Py_XDECREF(v);
2555+
PyObject *tb = PyException_GetTraceback(exc);
2556+
int has_tb = (tb != NULL) && (tb != Py_None);
25712557
Py_XDECREF(tb);
2558+
Py_XDECREF(exc);
25722559

25732560
/* sys.stderr may be buffered: call sys.stderr.flush() */
2574-
res = PyObject_CallMethodNoArgs(ferr, &_Py_ID(flush));
2561+
PyObject *res = PyObject_CallMethodNoArgs(ferr, &_Py_ID(flush));
25752562
if (res == NULL) {
25762563
_PyErr_Clear(tstate);
25772564
}

Python/pythonrun.c

+41-46
Original file line numberDiff line numberDiff line change
@@ -761,39 +761,34 @@ handle_system_exit(void)
761761
static void
762762
_PyErr_PrintEx(PyThreadState *tstate, int set_sys_last_vars)
763763
{
764-
PyObject *exception, *v, *tb, *hook;
765-
764+
PyObject *typ = NULL, *tb = NULL;
766765
handle_system_exit();
767766

768-
_PyErr_Fetch(tstate, &exception, &v, &tb);
769-
if (exception == NULL) {
767+
PyObject *exc = _PyErr_GetRaisedException(tstate);
768+
if (exc == NULL) {
770769
goto done;
771770
}
772-
773-
_PyErr_NormalizeException(tstate, &exception, &v, &tb);
771+
assert(PyExceptionInstance_Check(exc));
772+
typ = Py_NewRef(Py_TYPE(exc));
773+
tb = PyException_GetTraceback(exc);
774774
if (tb == NULL) {
775775
tb = Py_NewRef(Py_None);
776776
}
777-
PyException_SetTraceback(v, tb);
778-
if (exception == NULL) {
779-
goto done;
780-
}
781777

782-
/* Now we know v != NULL too */
783778
if (set_sys_last_vars) {
784-
if (_PySys_SetAttr(&_Py_ID(last_type), exception) < 0) {
779+
if (_PySys_SetAttr(&_Py_ID(last_type), typ) < 0) {
785780
_PyErr_Clear(tstate);
786781
}
787-
if (_PySys_SetAttr(&_Py_ID(last_value), v) < 0) {
782+
if (_PySys_SetAttr(&_Py_ID(last_value), exc) < 0) {
788783
_PyErr_Clear(tstate);
789784
}
790785
if (_PySys_SetAttr(&_Py_ID(last_traceback), tb) < 0) {
791786
_PyErr_Clear(tstate);
792787
}
793788
}
794-
hook = _PySys_GetAttr(tstate, &_Py_ID(excepthook));
789+
PyObject *hook = _PySys_GetAttr(tstate, &_Py_ID(excepthook));
795790
if (_PySys_Audit(tstate, "sys.excepthook", "OOOO", hook ? hook : Py_None,
796-
exception, v, tb) < 0) {
791+
typ, exc, tb) < 0) {
797792
if (PyErr_ExceptionMatches(PyExc_RuntimeError)) {
798793
PyErr_Clear();
799794
goto done;
@@ -802,46 +797,34 @@ _PyErr_PrintEx(PyThreadState *tstate, int set_sys_last_vars)
802797
}
803798
if (hook) {
804799
PyObject* stack[3];
805-
PyObject *result;
806-
807-
stack[0] = exception;
808-
stack[1] = v;
800+
stack[0] = typ;
801+
stack[1] = exc;
809802
stack[2] = tb;
810-
result = _PyObject_FastCall(hook, stack, 3);
803+
PyObject *result = _PyObject_FastCall(hook, stack, 3);
811804
if (result == NULL) {
812805
handle_system_exit();
813806

814-
PyObject *exception2, *v2, *tb2;
815-
_PyErr_Fetch(tstate, &exception2, &v2, &tb2);
816-
_PyErr_NormalizeException(tstate, &exception2, &v2, &tb2);
817-
/* It should not be possible for exception2 or v2
818-
to be NULL. However PyErr_Display() can't
819-
tolerate NULLs, so just be safe. */
820-
if (exception2 == NULL) {
821-
exception2 = Py_NewRef(Py_None);
822-
}
823-
if (v2 == NULL) {
824-
v2 = Py_NewRef(Py_None);
825-
}
807+
PyObject *exc2 = _PyErr_GetRaisedException(tstate);
808+
assert(exc2 && PyExceptionInstance_Check(exc2));
826809
fflush(stdout);
827810
PySys_WriteStderr("Error in sys.excepthook:\n");
828-
PyErr_Display(exception2, v2, tb2);
811+
PyErr_DisplayException(exc2);
829812
PySys_WriteStderr("\nOriginal exception was:\n");
830-
PyErr_Display(exception, v, tb);
831-
Py_DECREF(exception2);
832-
Py_DECREF(v2);
833-
Py_XDECREF(tb2);
813+
PyErr_DisplayException(exc);
814+
Py_DECREF(exc2);
815+
}
816+
else {
817+
Py_DECREF(result);
834818
}
835-
Py_XDECREF(result);
836819
}
837820
else {
838821
PySys_WriteStderr("sys.excepthook is missing\n");
839-
PyErr_Display(exception, v, tb);
822+
PyErr_DisplayException(exc);
840823
}
841824

842825
done:
843-
Py_XDECREF(exception);
844-
Py_XDECREF(v);
826+
Py_XDECREF(typ);
827+
Py_XDECREF(exc);
845828
Py_XDECREF(tb);
846829
}
847830

@@ -1505,18 +1488,20 @@ print_exception_recursive(struct exception_print_context *ctx, PyObject *value)
15051488
#define PyErr_MAX_GROUP_DEPTH 10
15061489

15071490
void
1508-
_PyErr_Display(PyObject *file, PyObject *exception, PyObject *value, PyObject *tb)
1491+
_PyErr_Display(PyObject *file, PyObject *unused, PyObject *value, PyObject *tb)
15091492
{
15101493
assert(file != NULL && file != Py_None);
15111494
if (PyExceptionInstance_Check(value)
15121495
&& tb != NULL && PyTraceBack_Check(tb)) {
15131496
/* Put the traceback on the exception, otherwise it won't get
15141497
displayed. See issue #18776. */
15151498
PyObject *cur_tb = PyException_GetTraceback(value);
1516-
if (cur_tb == NULL)
1499+
if (cur_tb == NULL) {
15171500
PyException_SetTraceback(value, tb);
1518-
else
1501+
}
1502+
else {
15191503
Py_DECREF(cur_tb);
1504+
}
15201505
}
15211506

15221507
struct exception_print_context ctx;
@@ -1552,7 +1537,7 @@ _PyErr_Display(PyObject *file, PyObject *exception, PyObject *value, PyObject *t
15521537
}
15531538

15541539
void
1555-
PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb)
1540+
PyErr_Display(PyObject *unused, PyObject *value, PyObject *tb)
15561541
{
15571542
PyThreadState *tstate = _PyThreadState_GET();
15581543
PyObject *file = _PySys_GetAttr(tstate, &_Py_ID(stderr));
@@ -1565,10 +1550,20 @@ PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb)
15651550
return;
15661551
}
15671552
Py_INCREF(file);
1568-
_PyErr_Display(file, exception, value, tb);
1553+
_PyErr_Display(file, NULL, value, tb);
15691554
Py_DECREF(file);
15701555
}
15711556

1557+
void _PyErr_DisplayException(PyObject *file, PyObject *exc)
1558+
{
1559+
_PyErr_Display(file, NULL, exc, NULL);
1560+
}
1561+
1562+
void PyErr_DisplayException(PyObject *exc)
1563+
{
1564+
PyErr_Display(NULL, exc, NULL);
1565+
}
1566+
15721567
PyObject *
15731568
PyRun_StringFlags(const char *str, int start, PyObject *globals,
15741569
PyObject *locals, PyCompilerFlags *flags)

Python/sysmodule.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,7 @@ sys_excepthook_impl(PyObject *module, PyObject *exctype, PyObject *value,
742742
PyObject *traceback)
743743
/*[clinic end generated code: output=18d99fdda21b6b5e input=ecf606fa826f19d9]*/
744744
{
745-
PyErr_Display(exctype, value, traceback);
745+
PyErr_Display(NULL, value, traceback);
746746
Py_RETURN_NONE;
747747
}
748748

0 commit comments

Comments
 (0)