From cc1f1e98c0a68fdf526339c42a83ca411db22b1d Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Tue, 4 Nov 2025 01:03:44 +0300 Subject: [PATCH 1/5] Possible solution --- Python/pylifecycle.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 8fcb31cfd12299..65f3a2898490f8 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1708,8 +1708,7 @@ finalize_modules(PyThreadState *tstate) { PyInterpreterState *interp = tstate->interp; - // Invalidate all executors and turn off JIT: - interp->jit = false; + // Invalidate all executors interp->compiling = false; #ifdef _Py_TIER2 _Py_Executors_InvalidateAll(interp, 0); From 937b9903dfa5cbddd80d497e55c1644decb1bcd2 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Tue, 4 Nov 2025 01:37:38 +0300 Subject: [PATCH 2/5] Test added --- Lib/test/test_capi/test_opt.py | 35 +++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 4e94f62d35eba2..b9d0bc6f382b2f 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -6,12 +6,14 @@ import gc import os import types +import tempfile +import subprocess import _opcode from test.support import (script_helper, requires_specialization, import_helper, Py_GIL_DISABLED, requires_jit_enabled, - reset_code) + reset_code, requires_subprocess) _testinternalcapi = import_helper.import_module("_testinternalcapi") @@ -2660,7 +2662,38 @@ def f(): f() + @requires_subprocess() + def test_interpreter_finalization_with_generator_alive(self): + code = textwrap.dedent(""" + import sys + def simple_for(): + for x in (1, 2): + x + def gen(): + try: + yield + except: + simple_for() + + sys.settrace(lambda *args: None) + simple_for() + g = gen() + next(g) + print("finished") + """) + + tmp = tempfile.NamedTemporaryFile(delete=False, suffix='.py') + tmp.write(code.encode('utf-8')) + tmp.close() + try: + p = subprocess.Popen([sys.executable, tmp.name], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + p.wait() + out = p.stdout.read() + finally: + os.remove(tmp.name) + p.stdout.close() + self.assertEqual(b"finished\n", out) def global_identity(x): return x From 688f063f66451112b1c92de1f202d2f5f4fd9648 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Tue, 4 Nov 2025 02:12:20 +0300 Subject: [PATCH 3/5] Another solution --- Python/optimizer.c | 4 +++- Python/pylifecycle.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index f44f8a9614b846..c26a51e2326b08 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -118,7 +118,9 @@ _PyOptimizer_Optimize( { _PyStackRef *stack_pointer = frame->stackpointer; PyInterpreterState *interp = _PyInterpreterState_GET(); - assert(interp->jit); + if (!interp->jit) { + return 0; + } assert(!interp->compiling); #ifndef Py_GIL_DISABLED interp->compiling = true; diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 65f3a2898490f8..8fcb31cfd12299 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1708,7 +1708,8 @@ finalize_modules(PyThreadState *tstate) { PyInterpreterState *interp = tstate->interp; - // Invalidate all executors + // Invalidate all executors and turn off JIT: + interp->jit = false; interp->compiling = false; #ifdef _Py_TIER2 _Py_Executors_InvalidateAll(interp, 0); From 39f9bc3cb8179777f202673413ab41321c6b3738 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Tue, 4 Nov 2025 02:44:28 +0300 Subject: [PATCH 4/5] New line fix in test --- Lib/test/test_capi/test_opt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index b9d0bc6f382b2f..b28806b7721aa8 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2680,7 +2680,7 @@ def gen(): simple_for() g = gen() next(g) - print("finished") + print("finished", end='') """) tmp = tempfile.NamedTemporaryFile(delete=False, suffix='.py') @@ -2693,7 +2693,7 @@ def gen(): finally: os.remove(tmp.name) p.stdout.close() - self.assertEqual(b"finished\n", out) + self.assertEqual(b"finished", out) def global_identity(x): return x From 28c07d6a800609f824a0054c5a0e68c1c81be58d Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Tue, 4 Nov 2025 11:25:54 +0300 Subject: [PATCH 5/5] Simplify test --- Lib/test/test_capi/test_opt.py | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index b28806b7721aa8..9c879f12471d3f 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -6,14 +6,12 @@ import gc import os import types -import tempfile -import subprocess import _opcode from test.support import (script_helper, requires_specialization, import_helper, Py_GIL_DISABLED, requires_jit_enabled, - reset_code, requires_subprocess) + reset_code) _testinternalcapi = import_helper.import_module("_testinternalcapi") @@ -2662,9 +2660,8 @@ def f(): f() - @requires_subprocess() def test_interpreter_finalization_with_generator_alive(self): - code = textwrap.dedent(""" + script_helper.assert_python_ok("-c", textwrap.dedent(""" import sys def simple_for(): for x in (1, 2): @@ -2680,20 +2677,8 @@ def gen(): simple_for() g = gen() next(g) - print("finished", end='') - """) + """)) - tmp = tempfile.NamedTemporaryFile(delete=False, suffix='.py') - tmp.write(code.encode('utf-8')) - tmp.close() - try: - p = subprocess.Popen([sys.executable, tmp.name], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - p.wait() - out = p.stdout.read() - finally: - os.remove(tmp.name) - p.stdout.close() - self.assertEqual(b"finished", out) def global_identity(x): return x