diff options
author | Julien Danjou <julien@danjou.info> | 2020-11-02 14:16:25 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-11-02 14:16:25 (GMT) |
commit | 64366fa9b3ba71b8a503a8719eff433f4ea49eb9 (patch) | |
tree | 8aa6d76484553d2f29bfe5d03709a86c0abe547f /Python | |
parent | 3d86d090dcbbdfdd3e5a5951cab30612d6131222 (diff) | |
download | cpython-64366fa9b3ba71b8a503a8719eff433f4ea49eb9.zip cpython-64366fa9b3ba71b8a503a8719eff433f4ea49eb9.tar.gz cpython-64366fa9b3ba71b8a503a8719eff433f4ea49eb9.tar.bz2 |
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.
Diffstat (limited to 'Python')
-rw-r--r-- | Python/clinic/sysmodule.c.h | 22 | ||||
-rw-r--r-- | Python/pystate.c | 63 | ||||
-rw-r--r-- | Python/sysmodule.c | 16 |
3 files changed, 100 insertions, 1 deletions
diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index c1a9a2d..addd589 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -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]*/ diff --git a/Python/pystate.c b/Python/pystate.c index e888986..e37cbd5 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -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 diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 749b964..945e639 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1838,6 +1838,21 @@ sys__current_frames_impl(PyObject *module) } /*[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 func: object @@ -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 |