From ec00e4cb0562af7ad2f21e795ea8a7c42918d59f Mon Sep 17 00:00:00 2001 From: Tyler Kennedy Date: Fri, 21 Mar 2025 03:39:54 -0400 Subject: [PATCH 1/3] gh-130522: Fix threading errors during garbage collection This issue was introduced in 3.13 (5a1ecc8) and causes spurious errors when threads shutdown. This issue occurs on all platforms, and can be triggered by utilities like `coverage.py`'s pytracer module or the PyCharm debugger (pydevd), which may keep references to internal threading objects around. Issue was reproduced on Linux/OSX/Windows: ``` Exception ignored in: Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/threading.py", line 1383, in __del__ TypeError: 'NoneType' object does not support the context manager protocol ``` ``` Exception ignored in: Traceback (most recent call last): File "/opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/threading.py", line 1383, in __del__ TypeError: 'NoneType' object does not support the context manager protocol ``` ``` Exception ignored in: Traceback (most recent call last): File "C:\hostedtoolcache\windows\Python\3.13.2\x64\Lib\threading.py", line 1383, in __del__ TypeError: 'NoneType' object does not support the context manager protocol ``` --- Lib/threading.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/threading.py b/Lib/threading.py index da9cdf0b09d83c..5cf649cb8b7a99 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -1394,7 +1394,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) From 280f79c2f6ed6617f9af7b13f59de6d74a1ca67a Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Fri, 25 Jul 2025 09:22:01 -0400 Subject: [PATCH 2/3] Add blurb. --- .../next/Library/2025-07-25-09-21-56.gh-issue-130522.Crwq68.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-07-25-09-21-56.gh-issue-130522.Crwq68.rst 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. From 52896bc4172d84259785d65d488cf27640828821 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Fri, 25 Jul 2025 09:33:06 -0400 Subject: [PATCH 3/3] Add a test case. --- Lib/test/test_threading.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 214e1ba0b53dd2..7b21378d5df944 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1887,6 +1887,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):