https://github.com/python/cpython/commit/4a8cfacc8b3699b04b2649a9a583b3c07524deb6
commit: 4a8cfacc8b3699b04b2649a9a583b3c07524deb6
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-18T12:40:05Z
summary:

[3.14] gh-155997: Fix list_all() if an interpreter is destroyed during the call 
(GH-155998) (GH-156005)

Creating the Interpreter objects can start a garbage collection which
finalizes an object owning the last reference to a listed interpreter.
Skip interpreters which no longer exist instead of failing.
(cherry picked from commit 60dff5a47b20a7efb6e43c571617b9139474bfe9)

Co-authored-by: Serhiy Storchaka <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-08-18-13-41-00.gh-issue-155997.CjbD1F.rst
M Lib/concurrent/interpreters/__init__.py
M Lib/test/test_interpreters/test_api.py

diff --git a/Lib/concurrent/interpreters/__init__.py 
b/Lib/concurrent/interpreters/__init__.py
index ea4147ee9a25da5..335a744b727d100 100644
--- a/Lib/concurrent/interpreters/__init__.py
+++ b/Lib/concurrent/interpreters/__init__.py
@@ -68,8 +68,14 @@ def create():
 
 def list_all():
     """Return all existing interpreters."""
-    return [Interpreter(id, _whence=whence)
-            for id, whence in _interpreters.list_all(require_ready=True)]
+    interps = []
+    for id, whence in _interpreters.list_all(require_ready=True):
+        try:
+            interps.append(Interpreter(id, _whence=whence))
+        except InterpreterNotFoundError:
+            # It was destroyed after it was listed.
+            pass
+    return interps
 
 
 def get_current():
diff --git a/Lib/test/test_interpreters/test_api.py 
b/Lib/test/test_interpreters/test_api.py
index e88fd399dfba382..3a49586ae140edc 100644
--- a/Lib/test/test_interpreters/test_api.py
+++ b/Lib/test/test_interpreters/test_api.py
@@ -288,6 +288,21 @@ def test_idempotent(self):
         for interp1, interp2 in zip(actual, expected):
             self.assertIs(interp1, interp2)
 
+    def test_destroyed_by_gc(self):
+        # gh-155997: the interpreter is destroyed while list_all() runs.
+        interp = interpreters.create()
+        interpid = interp.id
+        cycle = []
+        cycle.append(cycle)
+        cycle.append(interp)
+        # The cycle holds the only reference, so only the collector frees it.
+        with support.disable_gc():
+            del interp, cycle
+
+        with support.gc_threshold(1):
+            ids = [i.id for i in interpreters.list_all()]
+        self.assertNotIn(interpid, ids)
+
     def test_created_with_capi(self):
         mainid, *_ = _interpreters.get_main()
         interpid1 = _interpreters.create()
diff --git 
a/Misc/NEWS.d/next/Library/2026-08-18-13-41-00.gh-issue-155997.CjbD1F.rst 
b/Misc/NEWS.d/next/Library/2026-08-18-13-41-00.gh-issue-155997.CjbD1F.rst
new file mode 100644
index 000000000000000..2727ba34aad8ff5
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-08-18-13-41-00.gh-issue-155997.CjbD1F.rst
@@ -0,0 +1,3 @@
+Fix :func:`concurrent.interpreters.list_all`.  It failed if an interpreter
+was destroyed during the call, in particular by a garbage collection which
+finalized the object owning the last reference to it.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to