Skip to content

Commit a58db96

Browse files
authored
bpo-36829: Enhance PyErr_WriteUnraisable() (GH-13487)
PyErr_WriteUnraisable() now displays the exception even if displaying the traceback failed. Moreover, hold a strong reference to sys.stderr while using it. Document that an exception must be set when calling PyErr_WriteUnraisable(), but don't add an assertion to check it at runtime. Cleanup: use longer names for variables and create write_unraisable_exc_file() subfunction.
1 parent d092caf commit a58db96

File tree

3 files changed

+90
-53
lines changed

3 files changed

+90
-53
lines changed

Doc/c-api/exceptions.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ Printing and clearing
8181
in which the unraisable exception occurred. If possible,
8282
the repr of *obj* will be printed in the warning message.
8383
84+
An exception must be set when calling this function.
85+
8486
8587
Raising exceptions
8688
==================
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:c:func:`PyErr_WriteUnraisable` now displays the exception even if
2+
displaying the traceback failed. Moreover, hold a strong reference to
3+
:data:`sys.stderr` while using it. Document that an exception must be set when
4+
calling :c:func:`PyErr_WriteUnraisable`.

Python/errors.c

Lines changed: 84 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -940,90 +940,121 @@ PyErr_NewExceptionWithDoc(const char *name, const char *doc,
940940
}
941941

942942

943-
/* Call when an exception has occurred but there is no way for Python
944-
to handle it. Examples: exception in __del__ or during GC. */
945-
void
946-
PyErr_WriteUnraisable(PyObject *obj)
943+
static void
944+
write_unraisable_exc_file(PyObject *exc_type, PyObject *exc_value,
945+
PyObject *exc_tb, PyObject *obj, PyObject *file)
947946
{
948-
_Py_IDENTIFIER(__module__);
949-
PyObject *f, *t, *v, *tb;
950-
PyObject *moduleName = NULL;
951-
char* className;
952-
953-
PyErr_Fetch(&t, &v, &tb);
954-
955-
f = _PySys_GetObjectId(&PyId_stderr);
956-
if (f == NULL || f == Py_None)
957-
goto done;
958-
959947
if (obj) {
960-
if (PyFile_WriteString("Exception ignored in: ", f) < 0)
961-
goto done;
962-
if (PyFile_WriteObject(obj, f, 0) < 0) {
948+
if (PyFile_WriteString("Exception ignored in: ", file) < 0) {
949+
return;
950+
}
951+
if (PyFile_WriteObject(obj, file, 0) < 0) {
963952
PyErr_Clear();
964-
if (PyFile_WriteString("<object repr() failed>", f) < 0) {
965-
goto done;
953+
if (PyFile_WriteString("<object repr() failed>", file) < 0) {
954+
return;
966955
}
967956
}
968-
if (PyFile_WriteString("\n", f) < 0)
969-
goto done;
957+
if (PyFile_WriteString("\n", file) < 0) {
958+
return;
959+
}
970960
}
971961

972-
if (PyTraceBack_Print(tb, f) < 0)
973-
goto done;
962+
if (exc_tb != NULL) {
963+
if (PyTraceBack_Print(exc_tb, file) < 0) {
964+
/* continue even if writing the traceback failed */
965+
PyErr_Clear();
966+
}
967+
}
974968

975-
if (!t)
976-
goto done;
969+
if (!exc_type) {
970+
return;
971+
}
977972

978-
assert(PyExceptionClass_Check(t));
979-
className = PyExceptionClass_Name(t);
973+
assert(PyExceptionClass_Check(exc_type));
974+
char* className = PyExceptionClass_Name(exc_type);
980975
if (className != NULL) {
981976
char *dot = strrchr(className, '.');
982-
if (dot != NULL)
977+
if (dot != NULL) {
983978
className = dot+1;
979+
}
984980
}
985981

986-
moduleName = _PyObject_GetAttrId(t, &PyId___module__);
982+
_Py_IDENTIFIER(__module__);
983+
PyObject *moduleName = _PyObject_GetAttrId(exc_type, &PyId___module__);
987984
if (moduleName == NULL || !PyUnicode_Check(moduleName)) {
985+
Py_XDECREF(moduleName);
988986
PyErr_Clear();
989-
if (PyFile_WriteString("<unknown>", f) < 0)
990-
goto done;
987+
if (PyFile_WriteString("<unknown>", file) < 0) {
988+
return;
989+
}
991990
}
992991
else {
993992
if (!_PyUnicode_EqualToASCIIId(moduleName, &PyId_builtins)) {
994-
if (PyFile_WriteObject(moduleName, f, Py_PRINT_RAW) < 0)
995-
goto done;
996-
if (PyFile_WriteString(".", f) < 0)
997-
goto done;
993+
if (PyFile_WriteObject(moduleName, file, Py_PRINT_RAW) < 0) {
994+
Py_DECREF(moduleName);
995+
return;
996+
}
997+
Py_DECREF(moduleName);
998+
if (PyFile_WriteString(".", file) < 0) {
999+
return;
1000+
}
1001+
}
1002+
else {
1003+
Py_DECREF(moduleName);
9981004
}
9991005
}
1006+
10001007
if (className == NULL) {
1001-
if (PyFile_WriteString("<unknown>", f) < 0)
1002-
goto done;
1008+
if (PyFile_WriteString("<unknown>", file) < 0) {
1009+
return;
1010+
}
10031011
}
10041012
else {
1005-
if (PyFile_WriteString(className, f) < 0)
1006-
goto done;
1013+
if (PyFile_WriteString(className, file) < 0) {
1014+
return;
1015+
}
10071016
}
10081017

1009-
if (v && v != Py_None) {
1010-
if (PyFile_WriteString(": ", f) < 0)
1011-
goto done;
1012-
if (PyFile_WriteObject(v, f, Py_PRINT_RAW) < 0) {
1018+
if (exc_value && exc_value != Py_None) {
1019+
if (PyFile_WriteString(": ", file) < 0) {
1020+
return;
1021+
}
1022+
if (PyFile_WriteObject(exc_value, file, Py_PRINT_RAW) < 0) {
10131023
PyErr_Clear();
1014-
if (PyFile_WriteString("<exception str() failed>", f) < 0) {
1015-
goto done;
1024+
if (PyFile_WriteString("<exception str() failed>", file) < 0) {
1025+
return;
10161026
}
10171027
}
10181028
}
1019-
if (PyFile_WriteString("\n", f) < 0)
1020-
goto done;
1029+
if (PyFile_WriteString("\n", file) < 0) {
1030+
return;
1031+
}
1032+
}
10211033

1022-
done:
1023-
Py_XDECREF(moduleName);
1024-
Py_XDECREF(t);
1025-
Py_XDECREF(v);
1026-
Py_XDECREF(tb);
1034+
1035+
/* Display an unraisable exception into sys.stderr.
1036+
1037+
Called when an exception has occurred but there is no way for Python to
1038+
handle it. For example, when a destructor raises an exception or during
1039+
garbage collection (gc.collect()).
1040+
1041+
An exception must be set when calling this function. */
1042+
void
1043+
PyErr_WriteUnraisable(PyObject *obj)
1044+
{
1045+
PyObject *f, *exc_type, *exc_value, *exc_tb;
1046+
1047+
PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
1048+
1049+
f = _PySys_GetObjectId(&PyId_stderr);
1050+
/* Do nothing if sys.stderr is not available or set to None */
1051+
if (f != NULL && f != Py_None) {
1052+
write_unraisable_exc_file(exc_type, exc_value, exc_tb, obj, f);
1053+
}
1054+
1055+
Py_XDECREF(exc_type);
1056+
Py_XDECREF(exc_value);
1057+
Py_XDECREF(exc_tb);
10271058
PyErr_Clear(); /* Just in case */
10281059
}
10291060

0 commit comments

Comments
 (0)