summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/internal/pycore_traceback.h4
-rw-r--r--Lib/test/test_sys.py15
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2019-05-22-23-01-29.bpo-36829.MfOcUg.rst4
-rw-r--r--Python/errors.c22
-rw-r--r--Python/traceback.c15
5 files changed, 47 insertions, 13 deletions
diff --git a/Include/internal/pycore_traceback.h b/Include/internal/pycore_traceback.h
index a96199b..bf4d7fe 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 67a952d..1e5f168 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -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
index 0000000..957a485
--- /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 9622b5a..1b8b7ee 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 18bd0bf..04b52ad 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;