diff options
author | Victor Stinner <vstinner@python.org> | 2020-12-14 22:07:54 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-14 22:07:54 (GMT) |
commit | 357704c9f2375f29ed5b3a93adac086fa714538d (patch) | |
tree | 8b69403f419da52a6728c8557e1169aaf23c9e2e /Modules | |
parent | 83d52044ae4def1e8611a4b1b9263b850ca5c458 (diff) | |
download | cpython-357704c9f2375f29ed5b3a93adac086fa714538d.zip cpython-357704c9f2375f29ed5b3a93adac086fa714538d.tar.gz cpython-357704c9f2375f29ed5b3a93adac086fa714538d.tar.bz2 |
bpo-42639: atexit now logs callbacks exceptions (GH-23771)
At Python exit, if a callback registered with atexit.register()
fails, its exception is now logged. Previously, only some exceptions
were logged, and the last exception was always silently ignored.
Add _PyAtExit_Call() function and remove
PyInterpreterState.atexit_func member. call_py_exitfuncs() now calls
directly _PyAtExit_Call().
The atexit module must now always be built as a built-in module.
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/atexitmodule.c | 53 |
1 files changed, 35 insertions, 18 deletions
diff --git a/Modules/atexitmodule.c b/Modules/atexitmodule.c index f1fc871..90ed7d5 100644 --- a/Modules/atexitmodule.c +++ b/Modules/atexitmodule.c @@ -65,7 +65,7 @@ atexit_cleanup(struct atexit_state *state) /* Installed into pylifecycle.c's atexit mechanism */ static void -atexit_callfuncs(PyObject *module) +atexit_callfuncs(PyObject *module, int ignore_exc) { assert(!PyErr_Occurred()); @@ -87,18 +87,23 @@ atexit_callfuncs(PyObject *module) PyObject *res = PyObject_Call(cb->func, cb->args, cb->kwargs); if (res == NULL) { - /* Maintain the last exception, but don't leak if there are - multiple exceptions. */ - if (exc_type) { - Py_DECREF(exc_type); - Py_XDECREF(exc_value); - Py_XDECREF(exc_tb); + if (ignore_exc) { + _PyErr_WriteUnraisableMsg("in atexit callback", cb->func); } - PyErr_Fetch(&exc_type, &exc_value, &exc_tb); - if (!PyErr_GivenExceptionMatches(exc_type, PyExc_SystemExit)) { - PySys_WriteStderr("Error in atexit._run_exitfuncs:\n"); - PyErr_NormalizeException(&exc_type, &exc_value, &exc_tb); - PyErr_Display(exc_type, exc_value, exc_tb); + else { + /* Maintain the last exception, but don't leak if there are + multiple exceptions. */ + if (exc_type) { + Py_DECREF(exc_type); + Py_XDECREF(exc_value); + Py_XDECREF(exc_tb); + } + PyErr_Fetch(&exc_type, &exc_value, &exc_tb); + if (!PyErr_GivenExceptionMatches(exc_type, PyExc_SystemExit)) { + PySys_WriteStderr("Error in atexit._run_exitfuncs:\n"); + PyErr_NormalizeException(&exc_type, &exc_value, &exc_tb); + PyErr_Display(exc_type, exc_value, exc_tb); + } } } else { @@ -108,11 +113,24 @@ atexit_callfuncs(PyObject *module) atexit_cleanup(state); - if (exc_type) { - PyErr_Restore(exc_type, exc_value, exc_tb); + if (ignore_exc) { + assert(!PyErr_Occurred()); + } + else { + if (exc_type) { + PyErr_Restore(exc_type, exc_value, exc_tb); + } } } + +void +_PyAtExit_Call(PyObject *module) +{ + atexit_callfuncs(module, 1); +} + + /* ===================================================================== */ /* Module methods. */ @@ -180,7 +198,7 @@ Run all registered exit functions."); static PyObject * atexit_run_exitfuncs(PyObject *module, PyObject *unused) { - atexit_callfuncs(module); + atexit_callfuncs(module, 0); if (PyErr_Occurred()) { return NULL; } @@ -308,9 +326,8 @@ atexit_exec(PyObject *module) return -1; } - PyInterpreterState *is = _PyInterpreterState_GET(); - is->atexit_func = atexit_callfuncs; - is->atexit_module = module; + PyInterpreterState *interp = _PyInterpreterState_GET(); + interp->atexit_module = module; return 0; } |