https://github.com/python/cpython/commit/9243a4b93397f04237a5112011ee03433eda0462
commit: 9243a4b93397f04237a5112011ee03433eda0462
branch: main
author: Peter Bierma <[email protected]>
committer: ZeroIntensity <[email protected]>
date: 2025-09-19T12:17:05Z
summary:
gh-126016: Remove bad assertion in `PyThreadState_Clear` (GH-139158)
In the _interpreters module, we use PyEval_EvalCode() to run Python code in
another interpreter. However, when the process receives a KeyboardInterrupt,
PyEval_EvalCode() will jump straight to finalization rather than returning.
This prevents us from cleaning up and marking the thread as "not running main",
which triggers an assertion in PyThreadState_Clear() on debug builds. Since
everything else works as intended, remove that assertion.
files:
A Misc/NEWS.d/next/Library/2025-09-19-07-41-52.gh-issue-126016.Uz9W6h.rst
M Lib/test/test_interpreters/test_api.py
M Python/pystate.c
diff --git a/Lib/test/test_interpreters/test_api.py
b/Lib/test/test_interpreters/test_api.py
index 1f38a43be51e7a..8e9f1c3204a8bb 100644
--- a/Lib/test/test_interpreters/test_api.py
+++ b/Lib/test/test_interpreters/test_api.py
@@ -1,6 +1,7 @@
import contextlib
import os
import pickle
+import signal
import sys
from textwrap import dedent
import threading
@@ -11,7 +12,7 @@
from test.support import os_helper
from test.support import script_helper
from test.support import import_helper
-from test.support.script_helper import assert_python_ok
+from test.support.script_helper import assert_python_ok, spawn_python
# Raise SkipTest if subinterpreters not supported.
_interpreters = import_helper.import_module('_interpreters')
from concurrent import interpreters
@@ -434,6 +435,31 @@ def test_cleanup_in_repl(self):
self.assertIn(b"remaining subinterpreters", stdout)
self.assertNotIn(b"Traceback", stdout)
+ @support.requires_subprocess()
+ @unittest.skipIf(os.name == 'nt', "signals don't work well on windows")
+ def test_keyboard_interrupt_in_thread_running_interp(self):
+ import subprocess
+ source = f"""if True:
+ from concurrent import interpreters
+ from threading import Thread
+
+ def test():
+ import time
+ print('a', flush=True, end='')
+ time.sleep(10)
+
+ interp = interpreters.create()
+ interp.call_in_thread(test)
+ """
+
+ with spawn_python("-c", source, stderr=subprocess.PIPE) as proc:
+ self.assertEqual(proc.stdout.read(1), b'a')
+ proc.send_signal(signal.SIGINT)
+ proc.stderr.flush()
+ error = proc.stderr.read()
+ self.assertIn(b"KeyboardInterrupt", error)
+ retcode = proc.wait()
+ self.assertEqual(retcode, 0)
class TestInterpreterIsRunning(TestBase):
diff --git
a/Misc/NEWS.d/next/Library/2025-09-19-07-41-52.gh-issue-126016.Uz9W6h.rst
b/Misc/NEWS.d/next/Library/2025-09-19-07-41-52.gh-issue-126016.Uz9W6h.rst
new file mode 100644
index 00000000000000..feb09294bec982
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-09-19-07-41-52.gh-issue-126016.Uz9W6h.rst
@@ -0,0 +1,2 @@
+Fix an assertion failure when sending :exc:`KeyboardInterrupt` to a Python
+process running a subinterpreter in a separate thread.
diff --git a/Python/pystate.c b/Python/pystate.c
index 29c713dccc9fe8..dbed609f29aa07 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -1625,7 +1625,11 @@ PyThreadState_Clear(PyThreadState *tstate)
{
assert(tstate->_status.initialized && !tstate->_status.cleared);
assert(current_fast_get()->interp == tstate->interp);
- assert(!_PyThreadState_IsRunningMain(tstate));
+ // GH-126016: In the _interpreters module, KeyboardInterrupt exceptions
+ // during PyEval_EvalCode() are sent to finalization, which doesn't let us
+ // mark threads as "not running main". So, for now this assertion is
+ // disabled.
+ // XXX assert(!_PyThreadState_IsRunningMain(tstate));
// XXX assert(!tstate->_status.bound || tstate->_status.unbound);
tstate->_status.finalizing = 1; // just in case
_______________________________________________
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]