bpo-1230540: Add threading.excepthook() by vstinner · Pull Request #13515 · python/cpython (original) (raw)
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Conversation37 Commits3 Checks0 Files changed
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
[ Show hidden characters]({{ revealButtonHref }})
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.
https://bugs.python.org/issue1230540
See PR #8610 for a different approach: reuse sys.excepthook.
In my current implementation, I chose to continue to rely on t_bootstrap() of Modules/_threadmodule.c to log exception in threading.excepthook().
Maybe another option would be to attempt to reuse sys.excepthook() to log threading.excepthook() new failure?
Maybe the best option would be to later reimplement threading.excepthook() in C which reduces a lot the risk of error during Python shutdown. In C, it's more reliable to call functions during Python shutdown. Especially functions which don't rely on Python modules, but only access the interpreter state, thread state and the current exception.
I prefer to start with a simple PR to introduce the new API, and later experiment the different options to make the code even more reliable.
Oops, I didn't revert properly my change which added "stderr" into arguments passed to threading.excepthook(). It's now fixed.
From the doc: Handle uncaught :func:``Thread.run`` exception.
What if some C extension wants to report uncaught exceptions in C-created threads? Does it have to create a dummy Thread
object?
For example, how about threads created by _thread.start_new_thread
?
I modified my PR to use the threading identifier (threading.get_ident()) as the thread name if args.thread is None.
As you can see in test_excepthook_thread_None(), it's not possible to call directly threading.excepthook() directly only using the public API: threading._make_excepthook_args() is needed.
args = threading._make_excepthook_args([*sys.exc_info(), None])
Should it be made public? Maybe as threading.ExceptHookArgs?
Maybe it can be made public but not documented?
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.
I squashed my commits, rebased my PR on master and I renamed _make_excepthook_args as threading.ExceptHookArgs.
I chose to not document ExceptHookArgs. In the Python implementation, threading.ExceptHookArgs is a wrapper to threading._ExceptHookArgs: threading._ExceptHookArgs constructor takes N arguments, whereas ExceptHookArgs takes 1 argument.
Using threading.ExceptHookArgs, it's now possible to call directly threading.excepthook(threading.ExceptHook(...)) to log an error from a thread not created using threading.Thread, but using directly _thread.start_new_thread.
@pitrou: Does it look better to you?
I plan to merge this PR wednesday (in 2 days).
I don't see a strong opposition, and nobody came up with a working implementation for the idea of reusing sys.excepthook.
There is PR #8610 but I dislike it: threading.Thread doesn't call sys.excepthook to handle run() exception by default, it only calls sys.excepthook if it's overridden. Moreover, when sys.excepthook is called, the hook doesn't get access to the thread object :-(
https://bugs.python.org/issue1230540#msg343260
from traceback import print_exception as _print_exception |
---|
from collections import namedtuple |
_ExceptHookArgs = namedtuple( |
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is namedtuple for possible future extending a list of provided arguments?
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly. See sys.unraisablehook: I used the same approach and today I added a new "err_msg" attribute ;-)
@@ -953,10 +953,11 @@ print_exception_recursive(PyObject *f, PyObject *value, PyObject *seen) |
---|
} |
void |
PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb) |
_PyErr_Display(PyObject *file, PyObject *exception, PyObject *value, PyObject *tb) |
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make it public maybe? PyErr_DisplayToFile
or something like this?
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to not add a new function to the public C API unless there is a clear need from users. In the past, the C API evolved organically and now it's a mess because nobody knows what is really used or not :-(
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks mostly good to me. Some comments below.
When you're done making the requested changes, leave the comment: I have made the requested changes; please review again
.
I have made the requested changes; please review again.
Thanks for making the requested changes!
@pitrou: please review the changes made to this pull request.
vstinner deleted the threading_excepthook branch
lazka mentioned this pull request
DinoV pushed a commit to DinoV/cpython that referenced this pull request
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.
Reviewers
asvetlov asvetlov left review comments
graingert graingert left review comments
pitrou pitrou approved these changes
auvipy auvipy approved these changes
serhiy-storchaka Awaiting requested review from serhiy-storchaka