]> granicus.if.org Git - python/commitdiff
bpo-36829: PyErr_WriteUnraisable() normalizes exception (GH-13507)
authorVictor Stinner <vstinner@redhat.com>
Wed, 22 May 2019 23:00:58 +0000 (01:00 +0200)
committerGitHub <noreply@github.com>
Wed, 22 May 2019 23:00:58 +0000 (01:00 +0200)
PyErr_WriteUnraisable() now creates a traceback object if there is no
current traceback. Moreover, call PyErr_NormalizeException() and
PyException_SetTraceback() to normalize the exception value. Ignore
silently any error.

Include/internal/pycore_traceback.h
Lib/test/test_sys.py
Misc/NEWS.d/next/Core and Builtins/2019-05-22-23-01-29.bpo-36829.MfOcUg.rst [new file with mode: 0644]
Python/errors.c
Python/traceback.c

index a96199bda5f9f3f92695518924756724b22b702c..bf4d7fe5851f96fa58170068ba23955926fcc2a4 100644 (file)
@@ -86,6 +86,10 @@ PyAPI_FUNC(void) _Py_DumpHexadecimal(
     unsigned long value,
     Py_ssize_t width);
 
+PyAPI_FUNC(PyObject*) _PyTraceBack_FromFrame(
+    PyObject *tb_next,
+    struct _frame *frame);
+
 #ifdef __cplusplus
 }
 #endif
index 67a952d9b4544ccc223e339b150ba0c2cb43b078..1e5f168f30bdf1076220ca07793655f1975b7233 100644 (file)
@@ -882,19 +882,14 @@ class UnraisableHookTest(unittest.TestCase):
         import _testcapi
         import types
         try:
-            # raise the exception to get a traceback in the except block
-            try:
-                raise exc
-            except Exception as exc2:
-                _testcapi.write_unraisable_exc(exc2, obj)
-                return types.SimpleNamespace(exc_type=type(exc2),
-                                             exc_value=exc2,
-                                             exc_traceback=exc2.__traceback__,
-                                             object=obj)
+            _testcapi.write_unraisable_exc(exc, obj)
+            return types.SimpleNamespace(exc_type=type(exc),
+                                         exc_value=exc,
+                                         exc_traceback=exc.__traceback__,
+                                         object=obj)
         finally:
             # Explicitly break any reference cycle
             exc = None
-            exc2 = None
 
     def test_original_unraisablehook(self):
         obj = "an object"
diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-05-22-23-01-29.bpo-36829.MfOcUg.rst b/Misc/NEWS.d/next/Core and Builtins/2019-05-22-23-01-29.bpo-36829.MfOcUg.rst
new file mode 100644 (file)
index 0000000..957a485
--- /dev/null
@@ -0,0 +1,4 @@
+:c:func:`PyErr_WriteUnraisable` now creates a traceback object if there is
+no current traceback. Moreover, call :c:func:`PyErr_NormalizeException` and
+:c:func:`PyException_SetTraceback` to normalize the exception value. Ignore any
+error.
index 9622b5a1067df921c5945fc87661aaf117b464d9..1b8b7eeb0e743fce596520213291be75b686dee9 100644 (file)
@@ -4,6 +4,7 @@
 #include "Python.h"
 #include "pycore_coreconfig.h"
 #include "pycore_pystate.h"
+#include "pycore_traceback.h"
 
 #ifndef __STDC__
 #ifndef MS_WINDOWS
@@ -1048,7 +1049,7 @@ write_unraisable_exc_file(PyObject *exc_type, PyObject *exc_value,
         }
     }
 
-    if (!exc_type) {
+    if (exc_type == NULL || exc_type == Py_None) {
         return -1;
     }
 
@@ -1106,6 +1107,7 @@ write_unraisable_exc_file(PyObject *exc_type, PyObject *exc_value,
             }
         }
     }
+
     if (PyFile_WriteString("\n", file) < 0) {
         return -1;
     }
@@ -1177,6 +1179,24 @@ PyErr_WriteUnraisable(PyObject *obj)
         goto default_hook;
     }
 
+    if (exc_tb == NULL) {
+        struct _frame *frame = _PyThreadState_GET()->frame;
+        if (frame != NULL) {
+            exc_tb = _PyTraceBack_FromFrame(NULL, frame);
+            if (exc_tb == NULL) {
+                PyErr_Clear();
+            }
+        }
+    }
+
+    PyErr_NormalizeException(&exc_type, &exc_value, &exc_tb);
+
+    if (exc_tb != NULL && exc_tb != Py_None && PyTraceBack_Check(exc_tb)) {
+        if (PyException_SetTraceback(exc_value, exc_tb) < 0) {
+            PyErr_Clear();
+        }
+    }
+
     _Py_IDENTIFIER(unraisablehook);
     PyObject *hook = _PySys_GetObjectId(&PyId_unraisablehook);
     if (hook != NULL && hook != Py_None) {
index 18bd0bf7341ffa17ae3e8b91d8664bcac7a2f610..04b52ad7680a1188903275b8b2274135d69224dc 100644 (file)
@@ -227,13 +227,24 @@ PyTypeObject PyTraceBack_Type = {
     tb_new,                                     /* tp_new */
 };
 
+
+PyObject*
+_PyTraceBack_FromFrame(PyObject *tb_next, PyFrameObject *frame)
+{
+    assert(tb_next == NULL || PyTraceBack_Check(tb_next));
+    assert(frame != NULL);
+
+    return tb_create_raw((PyTracebackObject *)tb_next, frame, frame->f_lasti,
+                         PyFrame_GetLineNumber(frame));
+}
+
+
 int
 PyTraceBack_Here(PyFrameObject *frame)
 {
     PyObject *exc, *val, *tb, *newtb;
     PyErr_Fetch(&exc, &val, &tb);
-    newtb = tb_create_raw((PyTracebackObject *)tb, frame, frame->f_lasti,
-                          PyFrame_GetLineNumber(frame));
+    newtb = _PyTraceBack_FromFrame(tb, frame);
     if (newtb == NULL) {
         _PyErr_ChainExceptions(exc, val, tb);
         return -1;