diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index e05486f7d08..690735469d0 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -71,6 +71,13 @@ This module defines the following functions: .. versionadded:: 3.8 +.. data:: __excepthook__ + + Holds the original value of :func:`threading.excepthook`. It is saved so that the + original value can be restored in case they happen to get replaced with + broken or alternative objects. + + .. versionadded:: 3.10 .. function:: get_ident() diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 74c1c28ec0f..4d772005581 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -263,6 +263,11 @@ retrieve the functions set by :func:`threading.settrace` and :func:`threading.setprofile` respectively. (Contributed by Mario Corchero in :issue:`42251`.) +Add :data:`threading.__excepthook__` to allow retrieving the original value +of :func:`threading.excepthook` in case it is set to a broken or a different +value. +(Contributed by Mario Corchero in :issue:`42308`.) + traceback --------- diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index e0e5406ac26..db440d42f81 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1352,6 +1352,27 @@ class ExceptHookTests(BaseTestCase): 'Exception in threading.excepthook:\n') self.assertEqual(err_str, 'threading_hook failed') + def test_original_excepthook(self): + def run_thread(): + with support.captured_output("stderr") as output: + thread = ThreadRunFail(name="excepthook thread") + thread.start() + thread.join() + return output.getvalue() + + def threading_hook(args): + print("Running a thread failed", file=sys.stderr) + + default_output = run_thread() + with support.swap_attr(threading, 'excepthook', threading_hook): + custom_hook_output = run_thread() + threading.excepthook = threading.__excepthook__ + recovered_output = run_thread() + + self.assertEqual(default_output, recovered_output) + self.assertNotEqual(default_output, custom_hook_output) + self.assertEqual(custom_hook_output, "Running a thread failed\n") + class TimerTests(BaseTestCase): diff --git a/Lib/threading.py b/Lib/threading.py index d4fe649e4f0..7dae77dfd4d 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -1200,6 +1200,10 @@ except ImportError: stderr.flush() +# Original value of threading.excepthook +__excepthook__ = excepthook + + def _make_invoke_excepthook(): # Create a local namespace to ensure that variables remain alive # when _invoke_excepthook() is called, even if it is called late during diff --git a/Misc/NEWS.d/next/Library/2020-11-10-12-09-13.bpo-42308.yaJHH9.rst b/Misc/NEWS.d/next/Library/2020-11-10-12-09-13.bpo-42308.yaJHH9.rst new file mode 100644 index 00000000000..3460b0c92b1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-11-10-12-09-13.bpo-42308.yaJHH9.rst @@ -0,0 +1,3 @@ +Add :data:`threading.__excepthook__` to allow retrieving the original value +of :func:`threading.excepthook` in case it is set to a broken or a different +value. Patch by Mario Corchero.