You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
IDLE: in shell, time.sleep ignores _thread.interrupt_main() #74112
This will sleep for a bit over one and a half weeks. If this was typed in error, you may want to interrupt it. If using the command line, this is easy: just use Ctrl-C. If using IDLE, Ctrl-C has no effect. One could attempt to restart the shell with Ctrl-F6, which seems to work, but in fact the process remains in the background, hung until the timeout expires. There are two obvious workarounds: one is to sleep in a separate thread, so as to avoid blocking the main thread, and the other is to use a loop with smaller sleep increments:
foriiinrange(1e5): sleep(10)
Now it only takes 10 seconds to interrupt a sleep. But these are both clumsy workarounds. They're so clumsy that I think I'm not going to use IDLE for this particular program and just use python -I. Would be nice if this were fixed.
I verified on Win 10 with 3.6.1. What system are you running on?
An automatic unittest test would be nice, but I cannot imagine how to write one. Even a human-verified htest (IDLE private term) would be good. Maybe I should write a live-interaction test script.
^C is bound to pyshell.PyShell.cancel_callback. When code is executing, this calls pyshell.ModifiedInterpreter.interrupt_subprocess. In a new thread, this starts pyshell.ModifiedInterpreter.__request_interrupt. I verified the calls thus far, with debug prints, while the user execution thread is sleeping. __request_interrupt sends vua rpc ('exec', 'interrupt_the_server').
'interrupt_the_server' refers to a method of run.Executor:
if interruptable:
_thread.interrupt_main()
Here I am unsure which thread this executes in and when. Interrupting "while True: a=1" is no problem. Does interrup_main not work while the main thread is sleeping, or does the above not get executed?
Scratch my confusion. I added "print('after')" immediately after _thread.interrupt_main() in idlelib.run.Executor.interrupt_the_server() and 'after' is printed in Shell right after I hit ^C but a time.sleep(10) is not immediately interrupted and 'KeyboardInterrupt' does not show for nearly 10 seconds. I don't know if this is a bug or unavoidable limitation not mentioned in the _thread doc. I might be system dependent. https://docs.python.org/3/library/_thread.html#_thread.interrupt_main
It's simple to fix this in Python 3 on Windows. PyErr_SetInterrupt in Modules/signalmodule.c needs the following addition:
#ifdefMS_WINDOWSSetEvent(sigint_event);
#endif
In the main thread on Windows, time.sleep() waits on this event.
On Unix, time.sleep() uses select(). We could interrupt it by signaling the process, or explicitly the main thread, via kill() or pthread_kill(). See bpo-21895 (#66094) for a related discussion.
Eryk, would the addition go before or after "trip_signal(SIGINT);"
I posted "Issue with _thread.interrupt_main (29926)" on pydev list. Steven D'Aprano verified delayed response on *nix.
Martin Panter said
"Looking at the implementation, _thread.interrupt_main just calls PyErr_SetInterrupt. It doesn’t appear to send a signal. I played with “strace” and couldn’t see any evidence of a signal. I guess it just sets a flag that will be polled. To actually interrupt the “sleep” call, you might need to use “pthread_kill” or similar (at least on Unix)."
#157662 sends a real SIGINT to the main thread of the user process (signal.pthread_kill(), or signal.raise_signal() on Windows) instead of calling _thread.interrupt_main(), so time.sleep() and other blocking calls are interrupted. It also fixes the deadlock in input() observed in #2466: the interrupted wait for a response now releases its lock. The signal is sent while holding a new lock which protects sending a message, otherwise the connection could be corrupted.
@encukou This should be fixed by gh-158914: the test started the SIGINT timer before entering assertRaises(), so on a loaded machine the interrupt could arrive outside the block.
Activity
Mark commentedon Mar 27, 2017
Consider the following code, typed interactively:
This will sleep for a bit over one and a half weeks. If this was typed in error, you may want to interrupt it. If using the command line, this is easy: just use Ctrl-C. If using IDLE, Ctrl-C has no effect. One could attempt to restart the shell with Ctrl-F6, which seems to work, but in fact the process remains in the background, hung until the timeout expires. There are two obvious workarounds: one is to sleep in a separate thread, so as to avoid blocking the main thread, and the other is to use a loop with smaller sleep increments:
Now it only takes 10 seconds to interrupt a sleep. But these are both clumsy workarounds. They're so clumsy that I think I'm not going to use IDLE for this particular program and just use python -I. Would be nice if this were fixed.
terryjreedy commentedon Mar 28, 2017
I verified on Win 10 with 3.6.1. What system are you running on?
An automatic unittest test would be nice, but I cannot imagine how to write one. Even a human-verified htest (IDLE private term) would be good. Maybe I should write a live-interaction test script.
^C is bound to pyshell.PyShell.cancel_callback. When code is executing, this calls pyshell.ModifiedInterpreter.interrupt_subprocess. In a new thread, this starts pyshell.ModifiedInterpreter.__request_interrupt. I verified the calls thus far, with debug prints, while the user execution thread is sleeping. __request_interrupt sends vua rpc ('exec', 'interrupt_the_server').
'interrupt_the_server' refers to a method of run.Executor:
if interruptable:
_thread.interrupt_main()
Here I am unsure which thread this executes in and when. Interrupting "while True: a=1" is no problem. Does interrup_main not work while the main thread is sleeping, or does the above not get executed?
terryjreedy commentedon Mar 28, 2017
Scratch my confusion. I added "print('after')" immediately after _thread.interrupt_main() in idlelib.run.Executor.interrupt_the_server() and 'after' is printed in Shell right after I hit ^C but a time.sleep(10) is not immediately interrupted and 'KeyboardInterrupt' does not show for nearly 10 seconds. I don't know if this is a bug or unavoidable limitation not mentioned in the _thread doc. I might be system dependent.
https://docs.python.org/3/library/_thread.html#_thread.interrupt_main
[-]time.sleep ignores keyboard interrupt in IDLE[/-][+]time.sleep ignores _thread.interrupt_main()[/+]eryksun commentedon Mar 28, 2017
It's simple to fix this in Python 3 on Windows. PyErr_SetInterrupt in Modules/signalmodule.c needs the following addition:
In the main thread on Windows, time.sleep() waits on this event.
On Unix, time.sleep() uses select(). We could interrupt it by signaling the process, or explicitly the main thread, via kill() or pthread_kill(). See bpo-21895 (#66094) for a related discussion.
terryjreedy commentedon Mar 28, 2017
Eryk, would the addition go before or after "trip_signal(SIGINT);"
I posted "Issue with _thread.interrupt_main (29926)" on pydev list. Steven D'Aprano verified delayed response on *nix.
Martin Panter said
"Looking at the implementation, _thread.interrupt_main just calls PyErr_SetInterrupt. It doesn’t appear to send a signal. I played with “strace” and couldn’t see any evidence of a signal. I guess it just sets a flag that will be polled. To actually interrupt the “sleep” call, you might need to use “pthread_kill” or similar (at least on Unix)."
38 remaining items
serhiy-storchaka commentedon Sep 17, 2026
#157662 sends a real SIGINT to the main thread of the user process (
signal.pthread_kill(), orsignal.raise_signal()on Windows) instead of calling_thread.interrupt_main(), sotime.sleep()and other blocking calls are interrupted. It also fixes the deadlock ininput()observed in #2466: the interrupted wait for a response now releases its lock. The signal is sent while holding a new lock which protects sending a message, otherwise the connection could be corrupted.Merge branch 'main' into pythongh-74112-interrupt-signal
gh-74112: Make Ctrl-C in the IDLE Shell interrupt blocking calls (GH-…
[3.13] gh-74112: Make Ctrl-C in the IDLE Shell interrupt blocking cal…
[3.14] gh-74112: Make Ctrl-C in the IDLE Shell interrupt blocking cal…
terryjreedy commentedon Sep 27, 2026
3.15 backport must be generated after prior 3.15 backports are merged after 3.15 branch is unblocked.
encukou commentedon Oct 6, 2026
The new test failed on the AMD64 Windows Server 2022 NoGIL buildbot: https://buildbot.python.org/#/builders/1241/builds/9341
christianaurichzm commentedon Oct 6, 2026
@encukou This should be fixed by gh-158914: the test started the SIGINT timer before entering
assertRaises(), so on a loaded machine the interrupt could arrive outside the block.gh-74112: Fix race in test_interrupt_blocking_call (GH-158914)
pythongh-74112: Fix race in test_interrupt_blocking_call (pythonGH-15…
serhiy-storchaka commentedon Oct 6, 2026
Thanks, @christianaurichzm.
[3.14] gh-74112: Fix race in test_interrupt_blocking_call (GH-158914) (…
[3.15] gh-74112: Make Ctrl-C in the IDLE Shell interrupt blocking cal…