bpo-41435: Add sys._current_exceptions() function (GH-21689)

This adds a new function named sys._current_exceptions() which is equivalent ot
sys._current_frames() except that it returns the exceptions currently handled
by other threads. It is equivalent to calling sys.exc_info() for each running
thread.
This commit is contained in:
Julien Danjou 2020-11-02 15:16:25 +01:00 committed by GitHub
parent 3d86d090dc
commit 64366fa9b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 185 additions and 1 deletions

View file

@ -801,6 +801,26 @@ sys__current_frames(PyObject *module, PyObject *Py_UNUSED(ignored))
return sys__current_frames_impl(module);
}
PyDoc_STRVAR(sys__current_exceptions__doc__,
"_current_exceptions($module, /)\n"
"--\n"
"\n"
"Return a dict mapping each thread\'s identifier to its current raised exception.\n"
"\n"
"This function should be used for specialized purposes only.");
#define SYS__CURRENT_EXCEPTIONS_METHODDEF \
{"_current_exceptions", (PyCFunction)sys__current_exceptions, METH_NOARGS, sys__current_exceptions__doc__},
static PyObject *
sys__current_exceptions_impl(PyObject *module);
static PyObject *
sys__current_exceptions(PyObject *module, PyObject *Py_UNUSED(ignored))
{
return sys__current_exceptions_impl(module);
}
PyDoc_STRVAR(sys_call_tracing__doc__,
"call_tracing($module, func, args, /)\n"
"--\n"
@ -945,4 +965,4 @@ sys_getandroidapilevel(PyObject *module, PyObject *Py_UNUSED(ignored))
#ifndef SYS_GETANDROIDAPILEVEL_METHODDEF
#define SYS_GETANDROIDAPILEVEL_METHODDEF
#endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */
/*[clinic end generated code: output=87baa3357293ea65 input=a9049054013a1b77]*/
/*[clinic end generated code: output=bbc4963fe86a29d9 input=a9049054013a1b77]*/

View file

@ -1222,6 +1222,69 @@ done:
return result;
}
PyObject *
_PyThread_CurrentExceptions(void)
{
PyThreadState *tstate = _PyThreadState_GET();
_Py_EnsureTstateNotNULL(tstate);
if (_PySys_Audit(tstate, "sys._current_exceptions", NULL) < 0) {
return NULL;
}
PyObject *result = PyDict_New();
if (result == NULL) {
return NULL;
}
/* for i in all interpreters:
* for t in all of i's thread states:
* if t's frame isn't NULL, map t's id to its frame
* Because these lists can mutate even when the GIL is held, we
* need to grab head_mutex for the duration.
*/
_PyRuntimeState *runtime = tstate->interp->runtime;
HEAD_LOCK(runtime);
PyInterpreterState *i;
for (i = runtime->interpreters.head; i != NULL; i = i->next) {
PyThreadState *t;
for (t = i->tstate_head; t != NULL; t = t->next) {
_PyErr_StackItem *err_info = _PyErr_GetTopmostException(t);
if (err_info == NULL) {
continue;
}
PyObject *id = PyLong_FromUnsignedLong(t->thread_id);
if (id == NULL) {
goto fail;
}
PyObject *exc_info = PyTuple_Pack(
3,
err_info->exc_type != NULL ? err_info->exc_type : Py_None,
err_info->exc_value != NULL ? err_info->exc_value : Py_None,
err_info->exc_traceback != NULL ? err_info->exc_traceback : Py_None);
if (exc_info == NULL) {
Py_DECREF(id);
goto fail;
}
int stat = PyDict_SetItem(result, id, exc_info);
Py_DECREF(id);
Py_DECREF(exc_info);
if (stat < 0) {
goto fail;
}
}
}
goto done;
fail:
Py_CLEAR(result);
done:
HEAD_UNLOCK(runtime);
return result;
}
/* Python "auto thread state" API. */
/* Keep this as a static, as it is not reliable! It can only

View file

@ -1837,6 +1837,21 @@ sys__current_frames_impl(PyObject *module)
return _PyThread_CurrentFrames();
}
/*[clinic input]
sys._current_exceptions
Return a dict mapping each thread's identifier to its current raised exception.
This function should be used for specialized purposes only.
[clinic start generated code]*/
static PyObject *
sys__current_exceptions_impl(PyObject *module)
/*[clinic end generated code: output=2ccfd838c746f0ba input=0e91818fbf2edc1f]*/
{
return _PyThread_CurrentExceptions();
}
/*[clinic input]
sys.call_tracing
@ -1953,6 +1968,7 @@ static PyMethodDef sys_methods[] = {
METH_FASTCALL | METH_KEYWORDS, breakpointhook_doc},
SYS__CLEAR_TYPE_CACHE_METHODDEF
SYS__CURRENT_FRAMES_METHODDEF
SYS__CURRENT_EXCEPTIONS_METHODDEF
SYS_DISPLAYHOOK_METHODDEF
SYS_EXC_INFO_METHODDEF
SYS_EXCEPTHOOK_METHODDEF