mirror of
https://github.com/python/cpython.git
synced 2025-08-31 14:07:50 +00:00
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.
This commit is contained in:
parent
23b4b697e5
commit
cd590a7ced
9 changed files with 424 additions and 67 deletions
|
@ -3,6 +3,7 @@
|
|||
/* Interface to Sjoerd's portable C thread library */
|
||||
|
||||
#include "Python.h"
|
||||
#include "pycore_pylifecycle.h"
|
||||
#include "pycore_pystate.h"
|
||||
#include "structmember.h" /* offsetof */
|
||||
#include "pythread.h"
|
||||
|
@ -11,6 +12,7 @@ static PyObject *ThreadError;
|
|||
static PyObject *str_dict;
|
||||
|
||||
_Py_IDENTIFIER(stderr);
|
||||
_Py_IDENTIFIER(flush);
|
||||
|
||||
/* Lock objects */
|
||||
|
||||
|
@ -1309,6 +1311,147 @@ requiring allocation in multiples of the system memory page size\n\
|
|||
(4 KiB pages are common; using multiples of 4096 for the stack size is\n\
|
||||
the suggested approach in the absence of more specific information).");
|
||||
|
||||
static int
|
||||
thread_excepthook_file(PyObject *file, PyObject *exc_type, PyObject *exc_value,
|
||||
PyObject *exc_traceback, PyObject *thread)
|
||||
{
|
||||
/* print(f"Exception in thread {thread.name}:", file=file) */
|
||||
if (PyFile_WriteString("Exception in thread ", file) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject *name = NULL;
|
||||
if (thread != Py_None) {
|
||||
name = PyObject_GetAttrString(thread, "name");
|
||||
}
|
||||
if (name != NULL) {
|
||||
if (PyFile_WriteObject(name, file, Py_PRINT_RAW) < 0) {
|
||||
Py_DECREF(name);
|
||||
return -1;
|
||||
}
|
||||
Py_DECREF(name);
|
||||
}
|
||||
else {
|
||||
PyErr_Clear();
|
||||
|
||||
unsigned long ident = PyThread_get_thread_ident();
|
||||
PyObject *str = PyUnicode_FromFormat("%lu", ident);
|
||||
if (str != NULL) {
|
||||
if (PyFile_WriteObject(str, file, Py_PRINT_RAW) < 0) {
|
||||
Py_DECREF(str);
|
||||
return -1;
|
||||
}
|
||||
Py_DECREF(str);
|
||||
}
|
||||
else {
|
||||
PyErr_Clear();
|
||||
|
||||
if (PyFile_WriteString("<failed to get thread name>", file) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (PyFile_WriteString(":\n", file) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Display the traceback */
|
||||
_PyErr_Display(file, exc_type, exc_value, exc_traceback);
|
||||
|
||||
/* Call file.flush() */
|
||||
PyObject *res = _PyObject_CallMethodId(file, &PyId_flush, NULL);
|
||||
if (!res) {
|
||||
return -1;
|
||||
}
|
||||
Py_DECREF(res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(ExceptHookArgs__doc__,
|
||||
"ExceptHookArgs\n\
|
||||
\n\
|
||||
Type used to pass arguments to threading.excepthook.");
|
||||
|
||||
static PyTypeObject ExceptHookArgsType;
|
||||
|
||||
static PyStructSequence_Field ExceptHookArgs_fields[] = {
|
||||
{"exc_type", "Exception type"},
|
||||
{"exc_value", "Exception value"},
|
||||
{"exc_traceback", "Exception traceback"},
|
||||
{"thread", "Thread"},
|
||||
{0}
|
||||
};
|
||||
|
||||
static PyStructSequence_Desc ExceptHookArgs_desc = {
|
||||
.name = "_thread.ExceptHookArgs",
|
||||
.doc = ExceptHookArgs__doc__,
|
||||
.fields = ExceptHookArgs_fields,
|
||||
.n_in_sequence = 4
|
||||
};
|
||||
|
||||
|
||||
static PyObject *
|
||||
thread_excepthook(PyObject *self, PyObject *args)
|
||||
{
|
||||
if (Py_TYPE(args) != &ExceptHookArgsType) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"_thread.excepthook argument type "
|
||||
"must be ExceptHookArgs");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Borrowed reference */
|
||||
PyObject *exc_type = PyStructSequence_GET_ITEM(args, 0);
|
||||
if (exc_type == PyExc_SystemExit) {
|
||||
/* silently ignore SystemExit */
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/* Borrowed references */
|
||||
PyObject *exc_value = PyStructSequence_GET_ITEM(args, 1);
|
||||
PyObject *exc_tb = PyStructSequence_GET_ITEM(args, 2);
|
||||
PyObject *thread = PyStructSequence_GET_ITEM(args, 3);
|
||||
|
||||
PyObject *file = _PySys_GetObjectId(&PyId_stderr);
|
||||
if (file == NULL || file == Py_None) {
|
||||
if (thread == Py_None) {
|
||||
/* do nothing if sys.stderr is None and thread is None */
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
file = PyObject_GetAttrString(thread, "_stderr");
|
||||
if (file == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (file == Py_None) {
|
||||
Py_DECREF(file);
|
||||
/* do nothing if sys.stderr is None and sys.stderr was None
|
||||
when the thread was created */
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Py_INCREF(file);
|
||||
}
|
||||
|
||||
int res = thread_excepthook_file(file, exc_type, exc_value, exc_tb,
|
||||
thread);
|
||||
Py_DECREF(file);
|
||||
if (res < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(excepthook_doc,
|
||||
"excepthook(exc_type, exc_value, exc_traceback, thread)\n\
|
||||
\n\
|
||||
Handle uncaught Thread.run() exception.");
|
||||
|
||||
static PyMethodDef thread_methods[] = {
|
||||
{"start_new_thread", (PyCFunction)thread_PyThread_start_new_thread,
|
||||
METH_VARARGS, start_new_doc},
|
||||
|
@ -1336,6 +1479,8 @@ static PyMethodDef thread_methods[] = {
|
|||
METH_VARARGS, stack_size_doc},
|
||||
{"_set_sentinel", thread__set_sentinel,
|
||||
METH_NOARGS, _set_sentinel_doc},
|
||||
{"_excepthook", thread_excepthook,
|
||||
METH_O, excepthook_doc},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
|
@ -1388,6 +1533,12 @@ PyInit__thread(void)
|
|||
return NULL;
|
||||
if (PyType_Ready(&RLocktype) < 0)
|
||||
return NULL;
|
||||
if (ExceptHookArgsType.tp_name == NULL) {
|
||||
if (PyStructSequence_InitType2(&ExceptHookArgsType,
|
||||
&ExceptHookArgs_desc) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create the module and add the functions */
|
||||
m = PyModule_Create(&threadmodule);
|
||||
|
@ -1424,6 +1575,11 @@ PyInit__thread(void)
|
|||
if (PyModule_AddObject(m, "_local", (PyObject *)&localtype) < 0)
|
||||
return NULL;
|
||||
|
||||
Py_INCREF(&ExceptHookArgsType);
|
||||
if (PyModule_AddObject(m, "_ExceptHookArgs",
|
||||
(PyObject *)&ExceptHookArgsType) < 0)
|
||||
return NULL;
|
||||
|
||||
interp->num_threads = 0;
|
||||
|
||||
str_dict = PyUnicode_InternFromString("__dict__");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue