diff options
author | Kurt B. Kaiser <kbk@shore.net> | 2003-02-17 18:57:16 (GMT) |
---|---|---|
committer | Kurt B. Kaiser <kbk@shore.net> | 2003-02-17 18:57:16 (GMT) |
commit | 003091cd51c5278e3ef76b6db01bd719b8b1c416 (patch) | |
tree | afc0ea2b00d259d40f2eebd7beb33bd96a4b9eba /Lib/idlelib/PyShell.py | |
parent | f927f14edab27b128b1962314c1cf7c10be73ac5 (diff) | |
download | cpython-003091cd51c5278e3ef76b6db01bd719b8b1c416.zip cpython-003091cd51c5278e3ef76b6db01bd719b8b1c416.tar.gz cpython-003091cd51c5278e3ef76b6db01bd719b8b1c416.tar.bz2 |
M NEWS.txt
M PyShell.py
M ScriptBinding.py
M rpc.py
M run.py
Clean up the way IDLEfork handles termination of the subprocess, restore
ability to interrupt user code in Windows (so long as it's doing terminal
I/O).
1. Handle subprocess interrupts in Windows with an RPC message.
2. Run/F5 will restart the subprocess even if user code is running.
3. Restart the subprocess if the link is dropped.
4. Exit IDLE cleanly even during I/O.
4. In rpc.py, remove explicit calls to statelock, let the condition
variable handle acquire() and release().
Diffstat (limited to 'Lib/idlelib/PyShell.py')
-rw-r--r-- | Lib/idlelib/PyShell.py | 109 |
1 files changed, 75 insertions, 34 deletions
diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py index 98e0918..f329162 100644 --- a/Lib/idlelib/PyShell.py +++ b/Lib/idlelib/PyShell.py @@ -8,6 +8,7 @@ import getopt import re import socket import time +import threading import traceback import types import exceptions @@ -361,9 +362,23 @@ class ModifiedInterpreter(InteractiveInterpreter): # close only the subprocess debugger debug = self.getdebugger() if debug: - RemoteDebugger.close_subprocess_debugger(self.rpcclt) - # kill subprocess, spawn a new one, accept connection - self.rpcclt.close() + try: + RemoteDebugger.close_subprocess_debugger(self.rpcclt) + except: + pass + # Kill subprocess, spawn a new one, accept connection. + if hasattr(os, 'kill'): + # We can interrupt any loop if we can use SIGINT. This doesn't + # work in Windows, currently we can only interrupt loops doing I/O. + self.__signal_interrupt() + # XXX KBK 13Feb03 Don't close the socket until the interrupt thread + # finishes. + self.tkconsole.executing = False + try: + self.rpcclt.close() + os.wait() + except: + pass self.spawn_subprocess() self.rpcclt.accept() self.transfer_path() @@ -374,12 +389,30 @@ class ModifiedInterpreter(InteractiveInterpreter): console.write(halfbar + ' RESTART ' + halfbar) console.text.mark_set("restart", "end-1c") console.text.mark_gravity("restart", "left") - # restart remote debugger + # restart subprocess debugger if debug: gui = RemoteDebugger.restart_subprocess_debugger(self.rpcclt) # reload remote debugger breakpoints for all PyShellEditWindows debug.load_breakpoints() + def __signal_interrupt(self): + try: + from signal import SIGINT + except ImportError: + SIGINT = 2 + os.kill(self.rpcpid, SIGINT) + + def __request_interrupt(self): + self.rpcclt.asynccall("exec", "interrupt_the_server", (), {}) + + def interrupt_subprocess(self): + if hasattr(os, "kill"): + self.__signal_interrupt() + else: + # Windows has no os.kill(), use an RPC message. + # This is async, must be done in a thread. + threading.Thread(target=self.__request_interrupt).start() + def transfer_path(self): self.runcommand("""if 1: import sys as _sys @@ -393,7 +426,20 @@ class ModifiedInterpreter(InteractiveInterpreter): clt = self.rpcclt if clt is None: return - response = clt.pollresponse(self.active_seq) + try: + response = clt.pollresponse(self.active_seq) + except (EOFError, IOError): + # lost connection: subprocess terminated itself, restart + if self.tkconsole.closing: + return + response = None + try: + # stake any zombie before restarting + os.wait() + except (AttributeError, OSError): + pass + self.restart_subprocess() + self.tkconsole.endexecuting() # Reschedule myself in 50 ms self.tkconsole.text.after(50, self.poll_subprocess) if response: @@ -571,8 +617,7 @@ class ModifiedInterpreter(InteractiveInterpreter): def runcode(self, code): "Override base class method" if self.tkconsole.executing: - self.display_executing_dialog() - return + self.interp.restart_subprocess() self.checklinecache() if self.save_warnings_filters is not None: warnings.filters[:] = self.save_warnings_filters @@ -670,10 +715,11 @@ class PyShell(OutputWindow): if use_subprocess: self.interp.start_subprocess() - reading = 0 - executing = 0 - canceled = 0 - endoffile = 0 + reading = False + executing = False + canceled = False + endoffile = False + closing = False def toggle_debugger(self, event=None): if self.executing: @@ -748,17 +794,17 @@ class PyShell(OutputWindow): def close(self): "Extend EditorWindow.close()" if self.executing: - # XXX Need to ask a question here - if not tkMessageBox.askokcancel( + response = tkMessageBox.askokcancel( "Kill?", - "The program is still running; do you want to kill it?", + "The program is still running!\n Do you want to kill it?", default="ok", - master=self.text): + master=self.text) + if response == False: return "cancel" - self.canceled = 1 - if self.reading: - self.top.quit() - return "cancel" + # interrupt the subprocess + self.closing = True + self.cancel_callback() + self.endexecuting() return EditorWindow.close(self) def _close(self): @@ -819,7 +865,7 @@ class PyShell(OutputWindow): def isatty(self): return True - def cancel_callback(self, event): + def cancel_callback(self, event=None): try: if self.text.compare("sel.first", "!=", "sel.last"): return # Active selection -- always use default binding @@ -831,18 +877,11 @@ class PyShell(OutputWindow): self.showprompt() return "break" self.endoffile = 0 + self.canceled = 1 if self.reading: - self.canceled = 1 self.top.quit() - elif (self.executing and self.interp.rpcclt and - self.interp.rpcpid and hasattr(os, "kill")): - try: - from signal import SIGINT - except ImportError: - SIGINT = 2 - os.kill(self.interp.rpcpid, SIGINT) - else: - self.canceled = 1 + elif (self.executing and self.interp.rpcclt): + self.interp.interrupt_subprocess() return "break" def eof_callback(self, event): @@ -1020,12 +1059,14 @@ class PyShell(OutputWindow): sys.stdout.softspace = 0 def write(self, s, tags=()): - self.text.mark_gravity("iomark", "right") - OutputWindow.write(self, s, tags, "iomark") - self.text.mark_gravity("iomark", "left") + try: + self.text.mark_gravity("iomark", "right") + OutputWindow.write(self, s, tags, "iomark") + self.text.mark_gravity("iomark", "left") + except: + pass if self.canceled: self.canceled = 0 - raise KeyboardInterrupt class PseudoFile: |