diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 9ba5f68fd4a53c..3ed7fdb3fabde6 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1849,6 +1849,23 @@ def modify_file(): t.start() t.join() + def test_dummy_thread_on_interpreter_shutdown(self): + # GH-130522: When `threading` held a reference to itself and then a + # _DummyThread() object was created, destruction of the dummy thread + # would emit an unraisable exception at shutdown, due to a lock being + # destroyed. + code = """if True: + import sys + import threading + + threading.x = sys.modules[__name__] + x = threading._DummyThread() + """ + rc, out, err = assert_python_ok("-c", code) + self.assertEqual(rc, 0) + self.assertEqual(out, b"") + self.assertEqual(err, b"") + class ThreadRunFail(threading.Thread): def run(self): diff --git a/Lib/threading.py b/Lib/threading.py index 11f1b5af6ac1b3..acf5090716c9be 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -1381,7 +1381,7 @@ def __init__(self, dummy_thread): # the related _DummyThread will be kept forever! _thread_local_info._track_dummy_thread_ref = self - def __del__(self): + def __del__(self, _active_limbo_lock=_active_limbo_lock, _active=_active): with _active_limbo_lock: if _active.get(self._tident) is self._dummy_thread: _active.pop(self._tident, None) diff --git a/Misc/NEWS.d/next/Library/2025-07-25-09-21-56.gh-issue-130522.Crwq68.rst b/Misc/NEWS.d/next/Library/2025-07-25-09-21-56.gh-issue-130522.Crwq68.rst new file mode 100644 index 00000000000000..6c2246631dda9e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-07-25-09-21-56.gh-issue-130522.Crwq68.rst @@ -0,0 +1,2 @@ +Fix unraisable :exc:`TypeError` raised during :term:`interpreter shutdown` +in the :mod:`threading` module.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: