1. Stake Freddy.
e.g. further improve subprocess interrupt, exceptions, and termination.
2. Remove the workarounds in PyShell.py and ScriptBinding.py involving
interrupting the subprocess prior to killing it, not necessary anymore.
3. Fix a bug introduced at PyShell Rev 1.66: was getting extra shell menu
every time the shell window was recreated.
M PyShell.py
M ScriptBinding.py
M rpc.py
M run.py
diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py
index dc47f07..cb38411 100644
--- a/Lib/idlelib/PyShell.py
+++ b/Lib/idlelib/PyShell.py
@@ -296,6 +296,14 @@
pass
UndoDelegator.delete(self, index1, index2)
+
+class MyRPCClient(rpc.RPCClient):
+
+ def handle_EOF(self):
+ "Override the base class - just re-raise EOFError"
+ raise EOFError
+
+
class ModifiedInterpreter(InteractiveInterpreter):
def __init__(self, tkconsole):
@@ -329,7 +337,7 @@
for i in range(3):
time.sleep(i)
try:
- self.rpcclt = rpc.RPCClient(addr)
+ self.rpcclt = MyRPCClient(addr)
break
except socket.error, err:
print>>sys.__stderr__,"IDLE socket error: " + err[1]\
@@ -426,9 +434,10 @@
except (EOFError, IOError, KeyboardInterrupt):
# lost connection or subprocess terminated itself, restart
# [the KBI is from rpc.SocketIO.handle_EOF()]
+ if self.tkconsole.closing:
+ return
response = None
self.restart_subprocess()
- self.tkconsole.endexecuting()
if response:
self.tkconsole.resetoutput()
self.active_seq = None
@@ -673,7 +682,9 @@
def __init__(self, flist=None):
if use_subprocess:
- self.menu_specs.insert(2, ("shell", "_Shell"))
+ ms = self.menu_specs
+ if ms[2][0] != "shell":
+ ms.insert(2, ("shell", "_Shell"))
self.interp = ModifiedInterpreter(self)
if flist is None:
root = Tk()
@@ -793,15 +804,9 @@
parent=self.text)
if response == False:
return "cancel"
- # interrupt the subprocess
- self.canceled = True
- if use_subprocess:
- self.interp.interrupt_subprocess()
- return "cancel"
- else:
- self.closing = True
- # Wait for poll_subprocess() rescheduling to stop
- self.text.after(2 * self.pollinterval, self.close2)
+ self.closing = True
+ # Wait for poll_subprocess() rescheduling to stop
+ self.text.after(2 * self.pollinterval, self.close2)
def close2(self):
return EditorWindow.close(self)
@@ -885,7 +890,10 @@
if self.reading:
self.top.quit()
elif (self.executing and self.interp.rpcclt):
- self.interp.interrupt_subprocess()
+ if self.interp.getdebugger():
+ self.interp.restart_subprocess()
+ else:
+ self.interp.interrupt_subprocess()
return "break"
def eof_callback(self, event):
@@ -1021,16 +1029,7 @@
self.text.see("restart")
def restart_shell(self, event=None):
- if self.executing:
- self.cancel_callback()
- # Wait for subprocess to interrupt and restart
- # This can be a long time if shell is scrolling on a slow system
- # XXX 14 May 03 KBK This delay (and one in ScriptBinding) could be
- # shorter if we didn't print the KeyboardInterrupt on
- # restarting while user code is running....
- self.text.after(2000, self.interp.restart_subprocess)
- else:
- self.interp.restart_subprocess()
+ self.interp.restart_subprocess()
def showprompt(self):
self.resetoutput()
diff --git a/Lib/idlelib/ScriptBinding.py b/Lib/idlelib/ScriptBinding.py
index 252526d..b6d9da3 100644
--- a/Lib/idlelib/ScriptBinding.py
+++ b/Lib/idlelib/ScriptBinding.py
@@ -125,17 +125,6 @@
interp = shell.interp
if PyShell.use_subprocess:
shell.restart_shell()
- if shell.executing:
- delay = 2700
- else:
- delay = 500
- # Wait for the interrupt and reset to finish
- shell.text.after(delay, self.run_module_event2, interp,
- filename, code)
- else:
- self.run_module_event2(interp, filename, code)
-
- def run_module_event2(self, interp, filename, code):
# XXX Too often this discards arguments the user just set...
interp.runcommand("""if 1:
_filename = %s
diff --git a/Lib/idlelib/rpc.py b/Lib/idlelib/rpc.py
index 8bb1aba..658aaf3 100644
--- a/Lib/idlelib/rpc.py
+++ b/Lib/idlelib/rpc.py
@@ -41,7 +41,6 @@
import types
import marshal
-import interrupt
def unpickle_code(ms):
co = marshal.loads(ms)
@@ -327,12 +326,9 @@
while len(s) > 0:
try:
n = self.sock.send(s)
- except AttributeError:
+ except (AttributeError, socket.error):
# socket was closed
raise IOError
- except socket.error:
- self.debug("putmessage:socketerror:pid:%s" % os.getpid())
- os._exit(0)
else:
s = s[n:]
@@ -471,7 +467,6 @@
self.responses[key] = ('EOF', None)
cv.notify()
cv.release()
- interrupt.interrupt_main()
# call our (possibly overridden) exit function
self.exithook()
diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py
index 98255c7..abc9969 100644
--- a/Lib/idlelib/run.py
+++ b/Lib/idlelib/run.py
@@ -21,7 +21,8 @@
# the socket) and the main thread (which runs user code), plus global
# completion and exit flags:
-exit_requested = False
+exit_now = False
+quitting = False
def main():
"""Start the Python execution server in a subprocess
@@ -41,6 +42,8 @@
register and unregister themselves.
"""
+ global exit_now
+ global quitting
port = 8833
if sys.argv[1:]:
port = int(sys.argv[1])
@@ -52,8 +55,12 @@
sockthread.start()
while 1:
try:
- if exit_requested:
- sys.exit(0)
+ if exit_now:
+ try:
+ sys.exit(0)
+ except KeyboardInterrupt:
+ # exiting but got an extra KBI? Try again!
+ continue
try:
seq, request = rpc.request_queue.get(0)
except Queue.Empty:
@@ -63,17 +70,22 @@
ret = method(*args, **kwargs)
rpc.response_queue.put((seq, ret))
except KeyboardInterrupt:
+ if quitting:
+ exit_now = True
continue
except SystemExit:
raise
except:
+ type, value, tb = sys.exc_info()
try:
print_exception()
rpc.response_queue.put((seq, None))
except:
- traceback.print_exc(file=sys.__stderr__)
- sys.exit(1.1)
- continue
+ # Link didn't work, print same exception to __stderr__
+ traceback.print_exception(type, value, tb, file=sys.__stderr__)
+ sys.exit(0)
+ else:
+ continue
def manage_socket(address):
for i in range(6):
@@ -89,17 +101,17 @@
+ err[1] + ", retrying...."
else:
print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
- global exit_requested
- exit_requested = True
+ global exit_now
+ exit_now = True
return
server.handle_request() # A single request only
def print_exception():
flush_stdout()
efile = sys.stderr
- typ, val, tb = info = sys.exc_info()
+ typ, val, tb = sys.exc_info()
tbe = traceback.extract_tb(tb)
- print >>efile, 'Traceback (most recent call last):'
+ print >>efile, '\nTraceback (most recent call last):'
exclude = ("run.py", "rpc.py", "threading.py", "Queue.py",
"RemoteDebugger.py", "bdb.py")
cleanup_traceback(tbe, exclude)
@@ -161,8 +173,8 @@
except SystemExit:
raise
except EOFError:
- global exit_requested
- exit_requested = True
+ global exit_now
+ exit_now = True
interrupt.interrupt_main()
except:
erf = sys.__stderr__
@@ -174,7 +186,7 @@
traceback.print_exc(file=erf)
print>>erf, '\n*** Unrecoverable, server exiting!'
print>>erf, '-'*40
- os._exit(0)
+ sys.exit(0)
class MyHandler(rpc.RPCHandler):
@@ -190,15 +202,18 @@
def exithook(self):
"override SocketIO method - wait for MainThread to shut us down"
- while 1: pass
+ time.sleep(10)
def EOFhook(self):
"Override SocketIO method - terminate wait on callback and exit thread"
- global exit_requested
- exit_requested = True
+ global quitting
+ quitting = True
+ interrupt.interrupt_main()
def decode_interrupthook(self):
"interrupt awakened thread"
+ global quitting
+ quitting = True
interrupt.interrupt_main()
@@ -213,15 +228,10 @@
try:
exec code in self.locals
except:
- if exit_requested:
+ if quitting:
sys.exit(0)
- try:
- # even print a user code SystemExit exception, continue
- print_exception()
- except:
- # link not working?
- traceback.print_exc(file=sys.__stderr__)
- sys.exit(1.2)
+ # even print a user code SystemExit exception, continue
+ print_exception()
else:
flush_stdout()