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

serhiy-storchaka <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
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]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.