[3.13] gh-155997: Fix list_all() if an interpreter is destroyed during the call (GH-155998) (GH-156006)
serhiy-storchaka <[email protected]>
| Newsgroups | gmane.comp.python.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://github.com/python/cpython/commit/5b49a29b0d8d916837e4b9f51649aa1954a5d89b commit: 5b49a29b0d8d916837e4b9f51649aa1954a5d89b branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: serhiy-storchaka <[email protected]> date: 2026-08-18T12:41:07Z summary: [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 60dff5a47b20a7efb6e43c571617b9139474bfe9) Co-authored-by: Serhiy Storchaka <[email protected]> files: A Misc/NEWS.d/next/Tests/2026-08-18-13-41-00.gh-issue-155997.CjbD1F.rst M Lib/test/support/interpreters/__init__.py M Lib/test/test_interpreters/test_api.py diff --git a/Lib/test/support/interpreters/__init__.py b/Lib/test/support/interpreters/__init__.py index e067f259364d2aa..ad988f180f28cb2 100644 --- a/Lib/test/support/interpreters/__init__.py +++ b/Lib/test/support/interpreters/__init__.py @@ -79,8 +79,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 b9c0c45e0160bfd..6e6b9453c24da9f 100644 --- a/Lib/test/test_interpreters/test_api.py +++ b/Lib/test/test_interpreters/test_api.py @@ -231,6 +231,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/Tests/2026-08-18-13-41-00.gh-issue-155997.CjbD1F.rst b/Misc/NEWS.d/next/Tests/2026-08-18-13-41-00.gh-issue-155997.CjbD1F.rst new file mode 100644 index 000000000000000..e55c531130055b0 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2026-08-18-13-41-00.gh-issue-155997.CjbD1F.rst @@ -0,0 +1,3 @@ +Fix :func:`!test.support.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]