[Python-checkins] bpo-36829: Enhance PyErr_WriteUnraisable() (GH-13487)

Victor Stinner webhook-mailer at python.org
Wed May 22 12:23:34 EDT 2019


https://github.com/python/cpython/commit/a58db9628d0c96cc5b863137fed4e432238f8027
commit: a58db9628d0c96cc5b863137fed4e432238f8027
branch: 3.7
author: Victor Stinner <vstinner at redhat.com>
committer: GitHub <noreply at github.com>
date: 2019-05-22T18:23:28+02:00
summary:

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.

files:
A Misc/NEWS.d/next/Core and Builtins/2019-05-22-11-44-41.bpo-36829.ZmpHR9.rst
M Doc/c-api/exceptions.rst
M Python/errors.c

diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst
index cd06096ef7bb..79e6f97a44c7 100644
--- a/Doc/c-api/exceptions.rst
+++ b/Doc/c-api/exceptions.rst
@@ -81,6 +81,8 @@ Printing and clearing
    in which the unraisable exception occurred. If possible,
    the repr of *obj* will be printed in the warning message.
 
+   An exception must be set when calling this function.
+
 
 Raising exceptions
 ==================
diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-05-22-11-44-41.bpo-36829.ZmpHR9.rst b/Misc/NEWS.d/next/Core and Builtins/2019-05-22-11-44-41.bpo-36829.ZmpHR9.rst
new file mode 100644
index 000000000000..790dc47ad064
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2019-05-22-11-44-41.bpo-36829.ZmpHR9.rst	
@@ -0,0 +1,4 @@
+:c:func:`PyErr_WriteUnraisable` now displays the exception even if
+displaying the traceback failed. Moreover, hold a strong reference to
+:data:`sys.stderr` while using it. Document that an exception must be set when
+calling :c:func:`PyErr_WriteUnraisable`.
diff --git a/Python/errors.c b/Python/errors.c
index 8c8ea1cd5772..6b8bac2fe172 100644
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -940,90 +940,121 @@ PyErr_NewExceptionWithDoc(const char *name, const char *doc,
 }
 
 
-/* Call when an exception has occurred but there is no way for Python
-   to handle it.  Examples: exception in __del__ or during GC. */
-void
-PyErr_WriteUnraisable(PyObject *obj)
+static void
+write_unraisable_exc_file(PyObject *exc_type, PyObject *exc_value,
+                          PyObject *exc_tb, PyObject *obj, PyObject *file)
 {
-    _Py_IDENTIFIER(__module__);
-    PyObject *f, *t, *v, *tb;
-    PyObject *moduleName = NULL;
-    char* className;
-
-    PyErr_Fetch(&t, &v, &tb);
-
-    f = _PySys_GetObjectId(&PyId_stderr);
-    if (f == NULL || f == Py_None)
-        goto done;
-
     if (obj) {
-        if (PyFile_WriteString("Exception ignored in: ", f) < 0)
-            goto done;
-        if (PyFile_WriteObject(obj, f, 0) < 0) {
+        if (PyFile_WriteString("Exception ignored in: ", file) < 0) {
+            return;
+        }
+        if (PyFile_WriteObject(obj, file, 0) < 0) {
             PyErr_Clear();
-            if (PyFile_WriteString("<object repr() failed>", f) < 0) {
-                goto done;
+            if (PyFile_WriteString("<object repr() failed>", file) < 0) {
+                return;
             }
         }
-        if (PyFile_WriteString("\n", f) < 0)
-            goto done;
+        if (PyFile_WriteString("\n", file) < 0) {
+            return;
+        }
     }
 
-    if (PyTraceBack_Print(tb, f) < 0)
-        goto done;
+    if (exc_tb != NULL) {
+        if (PyTraceBack_Print(exc_tb, file) < 0) {
+            /* continue even if writing the traceback failed */
+            PyErr_Clear();
+        }
+    }
 
-    if (!t)
-        goto done;
+    if (!exc_type) {
+        return;
+    }
 
-    assert(PyExceptionClass_Check(t));
-    className = PyExceptionClass_Name(t);
+    assert(PyExceptionClass_Check(exc_type));
+    char* className = PyExceptionClass_Name(exc_type);
     if (className != NULL) {
         char *dot = strrchr(className, '.');
-        if (dot != NULL)
+        if (dot != NULL) {
             className = dot+1;
+        }
     }
 
-    moduleName = _PyObject_GetAttrId(t, &PyId___module__);
+    _Py_IDENTIFIER(__module__);
+    PyObject *moduleName = _PyObject_GetAttrId(exc_type, &PyId___module__);
     if (moduleName == NULL || !PyUnicode_Check(moduleName)) {
+        Py_XDECREF(moduleName);
         PyErr_Clear();
-        if (PyFile_WriteString("<unknown>", f) < 0)
-            goto done;
+        if (PyFile_WriteString("<unknown>", file) < 0) {
+            return;
+        }
     }
     else {
         if (!_PyUnicode_EqualToASCIIId(moduleName, &PyId_builtins)) {
-            if (PyFile_WriteObject(moduleName, f, Py_PRINT_RAW) < 0)
-                goto done;
-            if (PyFile_WriteString(".", f) < 0)
-                goto done;
+            if (PyFile_WriteObject(moduleName, file, Py_PRINT_RAW) < 0) {
+                Py_DECREF(moduleName);
+                return;
+            }
+            Py_DECREF(moduleName);
+            if (PyFile_WriteString(".", file) < 0) {
+                return;
+            }
+        }
+        else {
+            Py_DECREF(moduleName);
         }
     }
+
     if (className == NULL) {
-        if (PyFile_WriteString("<unknown>", f) < 0)
-            goto done;
+        if (PyFile_WriteString("<unknown>", file) < 0) {
+            return;
+        }
     }
     else {
-        if (PyFile_WriteString(className, f) < 0)
-            goto done;
+        if (PyFile_WriteString(className, file) < 0) {
+            return;
+        }
     }
 
-    if (v && v != Py_None) {
-        if (PyFile_WriteString(": ", f) < 0)
-            goto done;
-        if (PyFile_WriteObject(v, f, Py_PRINT_RAW) < 0) {
+    if (exc_value && exc_value != Py_None) {
+        if (PyFile_WriteString(": ", file) < 0) {
+            return;
+        }
+        if (PyFile_WriteObject(exc_value, file, Py_PRINT_RAW) < 0) {
             PyErr_Clear();
-            if (PyFile_WriteString("<exception str() failed>", f) < 0) {
-                goto done;
+            if (PyFile_WriteString("<exception str() failed>", file) < 0) {
+                return;
             }
         }
     }
-    if (PyFile_WriteString("\n", f) < 0)
-        goto done;
+    if (PyFile_WriteString("\n", file) < 0) {
+        return;
+    }
+}
 
-done:
-    Py_XDECREF(moduleName);
-    Py_XDECREF(t);
-    Py_XDECREF(v);
-    Py_XDECREF(tb);
+
+/* Display an unraisable exception into sys.stderr.
+
+   Called when an exception has occurred but there is no way for Python to
+   handle it. For example, when a destructor raises an exception or during
+   garbage collection (gc.collect()).
+
+   An exception must be set when calling this function. */
+void
+PyErr_WriteUnraisable(PyObject *obj)
+{
+    PyObject *f, *exc_type, *exc_value, *exc_tb;
+
+    PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
+
+    f = _PySys_GetObjectId(&PyId_stderr);
+    /* Do nothing if sys.stderr is not available or set to None */
+    if (f != NULL && f != Py_None) {
+        write_unraisable_exc_file(exc_type, exc_value, exc_tb, obj, f);
+    }
+
+    Py_XDECREF(exc_type);
+    Py_XDECREF(exc_value);
+    Py_XDECREF(exc_tb);
     PyErr_Clear(); /* Just in case */
 }
 



More information about the Python-checkins mailing list