]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-36829: Enhance PyErr_WriteUnraisable() (GH-13487)
authorVictor Stinner <vstinner@redhat.com>
Wed, 22 May 2019 16:23:28 +0000 (18:23 +0200)
committerGitHub <noreply@github.com>
Wed, 22 May 2019 16:23:28 +0000 (18:23 +0200)
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.

Doc/c-api/exceptions.rst
Misc/NEWS.d/next/Core and Builtins/2019-05-22-11-44-41.bpo-36829.ZmpHR9.rst [new file with mode: 0644]
Python/errors.c

index cd06096ef7bbfb1e7bc429140de77a07411bbed9..79e6f97a44c7d88a385bf48a42953429216add63 100644 (file)
@@ -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 (file)
index 0000000..790dc47
--- /dev/null
@@ -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`.
index 8c8ea1cd5772b562ebe3f4fd2ca9f45d00620082..6b8bac2fe172d89cf6822a9a91d7d5f553443245 100644 (file)
@@ -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 */
 }