Python Idle and KeyboardInterrupts
Asked Answered
N

2

12

KeyboardInterrupts in Idle work for me 90% of the time, but I was wondering why they don't always work. In Idle, if I do

import time
time.sleep(10)

and then attempt a KeyboardInterrupt using Ctrl+C, it does not interrupt the process until after sleeping for 10 seconds.

The same code and a KeyboardInterrupt via Ctrl+C works immediately in the shell.

Noseband answered 6/7, 2011 at 15:28 Comment(3)
Which release of Python? Which platform and operating system?Aldarcy
Python 2.7 on Windows XP. Thanks.Noseband
On my linux system I found that when CTRL+C doesn't work, entering the terminal command kill -2 <PID-of-Idle> will stop Idle.Bethanybethe
D
8

A quick glance at the IDLE source reveals that KeyboardInterrupts have some special case handling: http://svn.python.org/view/python/tags/r267/Lib/idlelib/PyShell.py?annotate=88851

On top of that, code is actually executed in a separate process which the main IDLE gui process communicates with via RPC. You're going to get different behavior under that model - it's best to just test with the canonical interpreter (via command line, interactive, etc.)

============

Digging deeper...

The socket on the RPC server is managed in a secondary thread which is supposed to propagate a KeyboardInterrupt using a call to thread.interrupt_main() ( http://svn.python.org/view/python/tags/r267/Lib/idlelib/run.py?annotate=88851 ). Behavior is not as expected there... This posting hints that for some reason, interrupt_main doesn't provide the level of granularity that you would expect: http://bytes.com/topic/python/answers/38386-thread-interrupt_main-doesnt-seem-work

Async API functions in cPython are a little goofy (from my experience) due to how the interpreter loop is handled, so it doesn't surprise me. interrupt_main() calls PyErr_SetInterrupt() to asynchronously notify the interpreter to handle a SIGINT in the main thread. From http://docs.python.org/c-api/exceptions.html#PyErr_SetInterrupt:

This function simulates the effect of a SIGINT signal arriving — the next time PyErr_CheckSignals() is called, KeyboardInterrupt will be raised

That would require the interpreter to go though whatever number of bytecode instructions before PyErr_CheckSignals() is called again - something that probably doesn't happen during a time.sleep(). I would venture to say that's a wart of simulating a SIGINT rather than actually signaling a SIGINT.

Diatonic answered 6/7, 2011 at 18:3 Comment(1)
+1 for "test with the canonical interpreter". Especially when dealing with Idle, which has a real tendency to have a lot of magic behaviors. My recommendation to anyone is that if you really want to use Idle is still have a terminal/command window open to do the real testing.Orme
A
1

See This article:

I quote:

If you try to stop a CPython program using Control-C, the interpreter throws a KeyboardInterrupt exception.

It makes some sense, because the thread is asleep for 10 seconds and so exceptions cannot be thrown until the 10 seconds pass. However, ctrl + c always work in the shell because you are trying to stop a process, not throw a python KeyboardInterrupt exception.

Also, see this previously answered question.

I hope this helps!

Acinaciform answered 6/7, 2011 at 16:20 Comment(3)
So why does the KeyboardInterrupt via Ctrl+C work in the shell but not in Idle?Noseband
I don't think I understand what you're saying... what are you saying Ctrl+C does in Idle? Also, Ctrl+C in the shell does result in a KeyboardInterrupt ExceptionNoseband
@ccsndrx: Could it be that, because IDLE is itself a Python program, using time.sleep in IDLE will result in the interpreter not receiving the key event until the sleep period is over, as IDLE has to send the key event to the interpreter itself, while when you use time.sleep from the command-line, it's processed by the interpreter immediately as there's no proxy in between?Gearwheel

© 2022 - 2024 — McMap. All rights reserved.