Skip to content

Commit 9243a4b

Browse files
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.
1 parent e3d9bd6 commit 9243a4b

File tree

3 files changed

+34
-2
lines changed

3 files changed

+34
-2
lines changed

Lib/test/test_interpreters/test_api.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import contextlib
22
import os
33
import pickle
4+
import signal
45
import sys
56
from textwrap import dedent
67
import threading
@@ -11,7 +12,7 @@
1112
from test.support import os_helper
1213
from test.support import script_helper
1314
from test.support import import_helper
14-
from test.support.script_helper import assert_python_ok
15+
from test.support.script_helper import assert_python_ok, spawn_python
1516
# Raise SkipTest if subinterpreters not supported.
1617
_interpreters = import_helper.import_module('_interpreters')
1718
from concurrent import interpreters
@@ -434,6 +435,31 @@ def test_cleanup_in_repl(self):
434435
self.assertIn(b"remaining subinterpreters", stdout)
435436
self.assertNotIn(b"Traceback", stdout)
436437

438+
@support.requires_subprocess()
439+
@unittest.skipIf(os.name == 'nt', "signals don't work well on windows")
440+
def test_keyboard_interrupt_in_thread_running_interp(self):
441+
import subprocess
442+
source = f"""if True:
443+
from concurrent import interpreters
444+
from threading import Thread
445+
446+
def test():
447+
import time
448+
print('a', flush=True, end='')
449+
time.sleep(10)
450+
451+
interp = interpreters.create()
452+
interp.call_in_thread(test)
453+
"""
454+
455+
with spawn_python("-c", source, stderr=subprocess.PIPE) as proc:
456+
self.assertEqual(proc.stdout.read(1), b'a')
457+
proc.send_signal(signal.SIGINT)
458+
proc.stderr.flush()
459+
error = proc.stderr.read()
460+
self.assertIn(b"KeyboardInterrupt", error)
461+
retcode = proc.wait()
462+
self.assertEqual(retcode, 0)
437463

438464

439465
class TestInterpreterIsRunning(TestBase):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix an assertion failure when sending :exc:`KeyboardInterrupt` to a Python
2+
process running a subinterpreter in a separate thread.

Python/pystate.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1625,7 +1625,11 @@ PyThreadState_Clear(PyThreadState *tstate)
16251625
{
16261626
assert(tstate->_status.initialized && !tstate->_status.cleared);
16271627
assert(current_fast_get()->interp == tstate->interp);
1628-
assert(!_PyThreadState_IsRunningMain(tstate));
1628+
// GH-126016: In the _interpreters module, KeyboardInterrupt exceptions
1629+
// during PyEval_EvalCode() are sent to finalization, which doesn't let us
1630+
// mark threads as "not running main". So, for now this assertion is
1631+
// disabled.
1632+
// XXX assert(!_PyThreadState_IsRunningMain(tstate));
16291633
// XXX assert(!tstate->_status.bound || tstate->_status.unbound);
16301634
tstate->_status.finalizing = 1; // just in case
16311635

0 commit comments

Comments
 (0)