Skip to content

Commit 5b49a29

Browse files
[3.13] gh-155997: Fix list_all() if an interpreter is destroyed during the call (GH-155998) (GH-156006)
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 60dff5a) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 7a2d6e0 commit 5b49a29

3 files changed

Lines changed: 26 additions & 2 deletions

File tree

Lib/test/support/interpreters/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,14 @@ def create():
7979

8080
def list_all():
8181
"""Return all existing interpreters."""
82-
return [Interpreter(id, _whence=whence)
83-
for id, whence in _interpreters.list_all(require_ready=True)]
82+
interps = []
83+
for id, whence in _interpreters.list_all(require_ready=True):
84+
try:
85+
interps.append(Interpreter(id, _whence=whence))
86+
except InterpreterNotFoundError:
87+
# It was destroyed after it was listed.
88+
pass
89+
return interps
8490

8591

8692
def get_current():

Lib/test/test_interpreters/test_api.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,21 @@ def test_idempotent(self):
231231
for interp1, interp2 in zip(actual, expected):
232232
self.assertIs(interp1, interp2)
233233

234+
def test_destroyed_by_gc(self):
235+
# gh-155997: the interpreter is destroyed while list_all() runs.
236+
interp = interpreters.create()
237+
interpid = interp.id
238+
cycle = []
239+
cycle.append(cycle)
240+
cycle.append(interp)
241+
# The cycle holds the only reference, so only the collector frees it.
242+
with support.disable_gc():
243+
del interp, cycle
244+
245+
with support.gc_threshold(1):
246+
ids = [i.id for i in interpreters.list_all()]
247+
self.assertNotIn(interpid, ids)
248+
234249
def test_created_with_capi(self):
235250
mainid, *_ = _interpreters.get_main()
236251
interpid1 = _interpreters.create()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :func:`!test.support.interpreters.list_all`. It failed if an interpreter
2+
was destroyed during the call, in particular by a garbage collection which
3+
finalized the object owning the last reference to it.

0 commit comments

Comments
 (0)