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.
This commit is contained in:
Victor Stinner 2019-05-23 01:00:58 +02:00 committed by GitHub
parent 5edcf26358
commit df22c03b93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 47 additions and 13 deletions

View 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

View 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"

View File

@ -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.

View 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) {

View 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;