[Idle-dev] Interruptable code?

Kurt B. Kaiser kbk at shore.net
Tue Dec 19 05:18:47 CET 2006

"Douglas S. Blank" <dblank at brynmawr.edu> writes:

> Just thought I'd post the details of what worked for me in interrupting a
> loop in idle with no subprocesses and no print statements on WindowsXP.

For those tuning in late, we are discussing running IDLE without the
subprocess.  This is not the recommended way to run IDLE.

> Checking on PyShell.flist.pyshell.canceled did not work, because it
> appears that "canceled" doesn't get set until "write()" is called.

It seemed to be working for me.  The 'canceled' attribute was being

But I think I was mistaken and was 'interrupting' the code before it was
actually running: I needed another <enter> :-)

Digging deeper:

pyshell.canceled is set by PyShell.cancel_callback().  But this callback
isn't being run because Tk isn't servicing its events (the GUI is
frozen) while the non-printing program is running as part of
PyShell.enter_callback().  The event loop won't resume until that
callback is completed.

If the program should print (via a re-directed stdout -> PseudoFile ->
OutputWindow -> tkinter text widget) then OutputWindow.write() calls

That update causes the event loop to run.  The pending cancel_callback
sets pyshell.canceled, which is then picked up when PyShell.write()
completes, and a KeyboardInterrupt is generated.

A solution is to update Tkinter.  Include this in your module:
    shell = idlelib.PyShell.flist.pyshell
    root = idlelib.PyShell.root

    def break_check():
        if shell.canceled:
            raise KeyboardInterrupt
except NameError: # idlelib not in namespace: running in subprocess
    def break_check():
        # subprocess responds to KBI

and use break_check in your code where it might loop.

while True: break_check()

Another solution when running without the subprocess is to hit a Ctrl-C in
the console window from which you started IDLE.  (On Windows, you would
need to start IDLE from a command window.  cd to ..../Lib/idlelib and

..\..\python .\PyShell.py


The ModifiedInterpreter.runcode() has called

exec code in self.locals

and the interrupt in the console window is received by python (which
without the subprocess is the only python involved).  There's an except:
statement in runcode() which traps this and causes IDLE to reset.

> But with that hint, I just add a bunch of write's in my code, like so:
> def func():
>     PyShell.flist.pyshell.write("")
>     return None
> Then I can control+c the following:
>>>> while 1: func()
> If you remove the write(), you won't be able to stop the program (short of
> killing IDLE).

This is also a reasonable solution.  A little magical, but reasonable :-)

All of this is needed because without the subprocess, it's a little tricky
to interrupt user code without interrupting IDLE itself.


More information about the IDLE-dev mailing list