[Python-checkins] bpo-36829: PyErr_WriteUnraisable() normalizes exception (GH-13507)

Victor Stinner webhook-mailer at python.org
Wed May 22 19:01:02 EDT 2019


https://github.com/python/cpython/commit/df22c03b93ea4620fdf4a0b3cbbbfa7c645af783
commit: df22c03b93ea4620fdf4a0b3cbbbfa7c645af783
branch: master
author: Victor Stinner <vstinner at redhat.com>
committer: GitHub <noreply at github.com>
date: 2019-05-23T01:00:58+02:00
summary:

bpo-36829: PyErr_WriteUnraisable() normalizes exception (GH-13507)

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.

files:
A Misc/NEWS.d/next/Core and Builtins/2019-05-22-23-01-29.bpo-36829.MfOcUg.rst
M Include/internal/pycore_traceback.h
M Lib/test/test_sys.py
M Python/errors.c
M Python/traceback.c

diff --git a/Include/internal/pycore_traceback.h b/Include/internal/pycore_traceback.h
index a96199bda5f9..bf4d7fe5851f 100644
--- a/Include/internal/pycore_traceback.h
+++ b/Include/internal/pycore_traceback.h
@@ -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
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index 67a952d9b454..1e5f168f30bd 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -882,19 +882,14 @@ def write_unraisable_exc(self, exc, obj):
         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
index 000000000000..957a4857e408
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2019-05-22-23-01-29.bpo-36829.MfOcUg.rst	
@@ -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.
diff --git a/Python/errors.c b/Python/errors.c
index 9622b5a1067d..1b8b7eeb0e74 100644
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -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) {
diff --git a/Python/traceback.c b/Python/traceback.c
index 18bd0bf7341f..04b52ad7680a 100644
--- a/Python/traceback.c
+++ b/Python/traceback.c
@@ -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;



More information about the Python-checkins mailing list