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]

Reply via email to