-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
bpo-1230540: Add threading.excepthook() #13515
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 and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
cc @vlasovskikh |
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. |
I rewrote my PR, see the details in the issue: https://bugs.python.org/issue1230540#msg343443 |
Oops, I didn't revert properly my change which added "stderr" into arguments passed to threading.excepthook(). It's now fixed. |
@serhiy-storchaka @pablogsal @pitrou: Would you mind to review the updated PR? See https://bugs.python.org/issue1230540 for the discussion. |
From the doc: What if some C extension wants to report uncaught exceptions in C-created threads? Does it have to create a dummy For example, how about threads created by |
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.
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 :-( |
from traceback import print_exception as _print_exception | ||
from collections import namedtuple | ||
|
||
_ExceptHookArgs = namedtuple( |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 :-(
There was a problem hiding this comment.
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. |
Thanks for making the requested changes! @pitrou: please review the changes made to this pull request. |
Thanks for the review @pitrou! |
I created https://bugs.python.org/issue37076 for this specific function ;-) |
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.
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.
'file' parameter.
calling _PyErr_Display().
details to ensure that everything remains alive during Python
shutdown.
https://bugs.python.org/issue1230540