bpo-42308: Add threading.__excepthook__ (GH-23218)
Add threading.__excepthook__ to allow retrieving the original value
of threading.excepthook in case it is set to a broken or a different
value.
diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst
index e05486f..6907354 100644
--- a/Doc/library/threading.rst
+++ b/Doc/library/threading.rst
@@ -71,6 +71,13 @@
.. 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 74c1c28..4d77200 100644
--- a/Doc/whatsnew/3.10.rst
+++ b/Doc/whatsnew/3.10.rst
@@ -263,6 +263,11 @@
: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 e0e5406..db440d4 100644
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -1352,6 +1352,27 @@ def sys_hook(exc_type, exc_value, exc_traceback):
'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 d4fe649..7dae77d 100644
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -1200,6 +1200,10 @@ def excepthook(args, /):
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 0000000..3460b0c
--- /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.