bpo-1230540: Add threading.excepthook() (GH-13515)
Add a new threading.excepthook() function which handles uncaught
Thread.run() exception. It can be overridden to control how uncaught
exceptions are handled.
threading.ExceptHookArgs is not documented on purpose: it should not
be used directly.
* threading.excepthook() and threading.ExceptHookArgs.
* Add _PyErr_Display(): similar to PyErr_Display(), but accept a
'file' parameter.
* Add _thread._excepthook(): C implementation of the exception hook
calling _PyErr_Display().
* Add _thread._ExceptHookArgs: structseq type.
* Add threading._invoke_excepthook_wrapper() which handles the gory
details to ensure that everything remains alive during Python
shutdown.
* Add unit tests.
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index 665c9c9..ba1d1cf 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -953,10 +953,11 @@
}
void
-PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb)
+_PyErr_Display(PyObject *file, PyObject *exception, PyObject *value, PyObject *tb)
{
+ assert(file != NULL && file != Py_None);
+
PyObject *seen;
- PyObject *f = _PySys_GetObjectId(&PyId_stderr);
if (PyExceptionInstance_Check(value)
&& tb != NULL && PyTraceBack_Check(tb)) {
/* Put the traceback on the exception, otherwise it won't get
@@ -967,23 +968,32 @@
else
Py_DECREF(cur_tb);
}
- if (f == Py_None) {
- /* pass */
+
+ /* We choose to ignore seen being possibly NULL, and report
+ at least the main exception (it could be a MemoryError).
+ */
+ seen = PySet_New(NULL);
+ if (seen == NULL) {
+ PyErr_Clear();
}
- else if (f == NULL) {
+ print_exception_recursive(file, value, seen);
+ Py_XDECREF(seen);
+}
+
+void
+PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb)
+{
+ PyObject *file = _PySys_GetObjectId(&PyId_stderr);
+ if (file == NULL) {
_PyObject_Dump(value);
fprintf(stderr, "lost sys.stderr\n");
+ return;
}
- else {
- /* We choose to ignore seen being possibly NULL, and report
- at least the main exception (it could be a MemoryError).
- */
- seen = PySet_New(NULL);
- if (seen == NULL)
- PyErr_Clear();
- print_exception_recursive(f, value, seen);
- Py_XDECREF(seen);
+ if (file == Py_None) {
+ return;
}
+
+ _PyErr_Display(file, exception, value, tb);
}
PyObject *