mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
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
This commit is contained in:
parent
ebc198faa9
commit
67fd0ea46d
4 changed files with 57 additions and 64 deletions
|
@ -296,6 +296,14 @@ class ModifiedUndoDelegator(UndoDelegator):
|
||||||
pass
|
pass
|
||||||
UndoDelegator.delete(self, index1, index2)
|
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):
|
class ModifiedInterpreter(InteractiveInterpreter):
|
||||||
|
|
||||||
def __init__(self, tkconsole):
|
def __init__(self, tkconsole):
|
||||||
|
@ -329,7 +337,7 @@ class ModifiedInterpreter(InteractiveInterpreter):
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
time.sleep(i)
|
time.sleep(i)
|
||||||
try:
|
try:
|
||||||
self.rpcclt = rpc.RPCClient(addr)
|
self.rpcclt = MyRPCClient(addr)
|
||||||
break
|
break
|
||||||
except socket.error, err:
|
except socket.error, err:
|
||||||
print>>sys.__stderr__,"IDLE socket error: " + err[1]\
|
print>>sys.__stderr__,"IDLE socket error: " + err[1]\
|
||||||
|
@ -426,9 +434,10 @@ class ModifiedInterpreter(InteractiveInterpreter):
|
||||||
except (EOFError, IOError, KeyboardInterrupt):
|
except (EOFError, IOError, KeyboardInterrupt):
|
||||||
# lost connection or subprocess terminated itself, restart
|
# lost connection or subprocess terminated itself, restart
|
||||||
# [the KBI is from rpc.SocketIO.handle_EOF()]
|
# [the KBI is from rpc.SocketIO.handle_EOF()]
|
||||||
|
if self.tkconsole.closing:
|
||||||
|
return
|
||||||
response = None
|
response = None
|
||||||
self.restart_subprocess()
|
self.restart_subprocess()
|
||||||
self.tkconsole.endexecuting()
|
|
||||||
if response:
|
if response:
|
||||||
self.tkconsole.resetoutput()
|
self.tkconsole.resetoutput()
|
||||||
self.active_seq = None
|
self.active_seq = None
|
||||||
|
@ -673,7 +682,9 @@ class PyShell(OutputWindow):
|
||||||
|
|
||||||
def __init__(self, flist=None):
|
def __init__(self, flist=None):
|
||||||
if use_subprocess:
|
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)
|
self.interp = ModifiedInterpreter(self)
|
||||||
if flist is None:
|
if flist is None:
|
||||||
root = Tk()
|
root = Tk()
|
||||||
|
@ -793,15 +804,9 @@ class PyShell(OutputWindow):
|
||||||
parent=self.text)
|
parent=self.text)
|
||||||
if response == False:
|
if response == False:
|
||||||
return "cancel"
|
return "cancel"
|
||||||
# interrupt the subprocess
|
self.closing = True
|
||||||
self.canceled = True
|
# Wait for poll_subprocess() rescheduling to stop
|
||||||
if use_subprocess:
|
self.text.after(2 * self.pollinterval, self.close2)
|
||||||
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)
|
|
||||||
|
|
||||||
def close2(self):
|
def close2(self):
|
||||||
return EditorWindow.close(self)
|
return EditorWindow.close(self)
|
||||||
|
@ -885,7 +890,10 @@ class PyShell(OutputWindow):
|
||||||
if self.reading:
|
if self.reading:
|
||||||
self.top.quit()
|
self.top.quit()
|
||||||
elif (self.executing and self.interp.rpcclt):
|
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"
|
return "break"
|
||||||
|
|
||||||
def eof_callback(self, event):
|
def eof_callback(self, event):
|
||||||
|
@ -1021,16 +1029,7 @@ class PyShell(OutputWindow):
|
||||||
self.text.see("restart")
|
self.text.see("restart")
|
||||||
|
|
||||||
def restart_shell(self, event=None):
|
def restart_shell(self, event=None):
|
||||||
if self.executing:
|
self.interp.restart_subprocess()
|
||||||
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()
|
|
||||||
|
|
||||||
def showprompt(self):
|
def showprompt(self):
|
||||||
self.resetoutput()
|
self.resetoutput()
|
||||||
|
|
|
@ -125,17 +125,6 @@ class ScriptBinding:
|
||||||
interp = shell.interp
|
interp = shell.interp
|
||||||
if PyShell.use_subprocess:
|
if PyShell.use_subprocess:
|
||||||
shell.restart_shell()
|
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...
|
# XXX Too often this discards arguments the user just set...
|
||||||
interp.runcommand("""if 1:
|
interp.runcommand("""if 1:
|
||||||
_filename = %s
|
_filename = %s
|
||||||
|
|
|
@ -41,7 +41,6 @@ import copy_reg
|
||||||
import types
|
import types
|
||||||
import marshal
|
import marshal
|
||||||
|
|
||||||
import interrupt
|
|
||||||
|
|
||||||
def unpickle_code(ms):
|
def unpickle_code(ms):
|
||||||
co = marshal.loads(ms)
|
co = marshal.loads(ms)
|
||||||
|
@ -327,12 +326,9 @@ class SocketIO:
|
||||||
while len(s) > 0:
|
while len(s) > 0:
|
||||||
try:
|
try:
|
||||||
n = self.sock.send(s)
|
n = self.sock.send(s)
|
||||||
except AttributeError:
|
except (AttributeError, socket.error):
|
||||||
# socket was closed
|
# socket was closed
|
||||||
raise IOError
|
raise IOError
|
||||||
except socket.error:
|
|
||||||
self.debug("putmessage:socketerror:pid:%s" % os.getpid())
|
|
||||||
os._exit(0)
|
|
||||||
else:
|
else:
|
||||||
s = s[n:]
|
s = s[n:]
|
||||||
|
|
||||||
|
@ -471,7 +467,6 @@ class SocketIO:
|
||||||
self.responses[key] = ('EOF', None)
|
self.responses[key] = ('EOF', None)
|
||||||
cv.notify()
|
cv.notify()
|
||||||
cv.release()
|
cv.release()
|
||||||
interrupt.interrupt_main()
|
|
||||||
# call our (possibly overridden) exit function
|
# call our (possibly overridden) exit function
|
||||||
self.exithook()
|
self.exithook()
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,8 @@ import __main__
|
||||||
# the socket) and the main thread (which runs user code), plus global
|
# the socket) and the main thread (which runs user code), plus global
|
||||||
# completion and exit flags:
|
# completion and exit flags:
|
||||||
|
|
||||||
exit_requested = False
|
exit_now = False
|
||||||
|
quitting = False
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Start the Python execution server in a subprocess
|
"""Start the Python execution server in a subprocess
|
||||||
|
@ -41,6 +42,8 @@ def main():
|
||||||
register and unregister themselves.
|
register and unregister themselves.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
global exit_now
|
||||||
|
global quitting
|
||||||
port = 8833
|
port = 8833
|
||||||
if sys.argv[1:]:
|
if sys.argv[1:]:
|
||||||
port = int(sys.argv[1])
|
port = int(sys.argv[1])
|
||||||
|
@ -52,8 +55,12 @@ def main():
|
||||||
sockthread.start()
|
sockthread.start()
|
||||||
while 1:
|
while 1:
|
||||||
try:
|
try:
|
||||||
if exit_requested:
|
if exit_now:
|
||||||
sys.exit(0)
|
try:
|
||||||
|
sys.exit(0)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
# exiting but got an extra KBI? Try again!
|
||||||
|
continue
|
||||||
try:
|
try:
|
||||||
seq, request = rpc.request_queue.get(0)
|
seq, request = rpc.request_queue.get(0)
|
||||||
except Queue.Empty:
|
except Queue.Empty:
|
||||||
|
@ -63,17 +70,22 @@ def main():
|
||||||
ret = method(*args, **kwargs)
|
ret = method(*args, **kwargs)
|
||||||
rpc.response_queue.put((seq, ret))
|
rpc.response_queue.put((seq, ret))
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
if quitting:
|
||||||
|
exit_now = True
|
||||||
continue
|
continue
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
raise
|
raise
|
||||||
except:
|
except:
|
||||||
|
type, value, tb = sys.exc_info()
|
||||||
try:
|
try:
|
||||||
print_exception()
|
print_exception()
|
||||||
rpc.response_queue.put((seq, None))
|
rpc.response_queue.put((seq, None))
|
||||||
except:
|
except:
|
||||||
traceback.print_exc(file=sys.__stderr__)
|
# Link didn't work, print same exception to __stderr__
|
||||||
sys.exit(1.1)
|
traceback.print_exception(type, value, tb, file=sys.__stderr__)
|
||||||
continue
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
def manage_socket(address):
|
def manage_socket(address):
|
||||||
for i in range(6):
|
for i in range(6):
|
||||||
|
@ -89,17 +101,17 @@ def manage_socket(address):
|
||||||
+ err[1] + ", retrying...."
|
+ err[1] + ", retrying...."
|
||||||
else:
|
else:
|
||||||
print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
|
print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
|
||||||
global exit_requested
|
global exit_now
|
||||||
exit_requested = True
|
exit_now = True
|
||||||
return
|
return
|
||||||
server.handle_request() # A single request only
|
server.handle_request() # A single request only
|
||||||
|
|
||||||
def print_exception():
|
def print_exception():
|
||||||
flush_stdout()
|
flush_stdout()
|
||||||
efile = sys.stderr
|
efile = sys.stderr
|
||||||
typ, val, tb = info = sys.exc_info()
|
typ, val, tb = sys.exc_info()
|
||||||
tbe = traceback.extract_tb(tb)
|
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",
|
exclude = ("run.py", "rpc.py", "threading.py", "Queue.py",
|
||||||
"RemoteDebugger.py", "bdb.py")
|
"RemoteDebugger.py", "bdb.py")
|
||||||
cleanup_traceback(tbe, exclude)
|
cleanup_traceback(tbe, exclude)
|
||||||
|
@ -161,8 +173,8 @@ class MyRPCServer(rpc.RPCServer):
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
raise
|
raise
|
||||||
except EOFError:
|
except EOFError:
|
||||||
global exit_requested
|
global exit_now
|
||||||
exit_requested = True
|
exit_now = True
|
||||||
interrupt.interrupt_main()
|
interrupt.interrupt_main()
|
||||||
except:
|
except:
|
||||||
erf = sys.__stderr__
|
erf = sys.__stderr__
|
||||||
|
@ -174,7 +186,7 @@ class MyRPCServer(rpc.RPCServer):
|
||||||
traceback.print_exc(file=erf)
|
traceback.print_exc(file=erf)
|
||||||
print>>erf, '\n*** Unrecoverable, server exiting!'
|
print>>erf, '\n*** Unrecoverable, server exiting!'
|
||||||
print>>erf, '-'*40
|
print>>erf, '-'*40
|
||||||
os._exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
class MyHandler(rpc.RPCHandler):
|
class MyHandler(rpc.RPCHandler):
|
||||||
|
@ -190,15 +202,18 @@ class MyHandler(rpc.RPCHandler):
|
||||||
|
|
||||||
def exithook(self):
|
def exithook(self):
|
||||||
"override SocketIO method - wait for MainThread to shut us down"
|
"override SocketIO method - wait for MainThread to shut us down"
|
||||||
while 1: pass
|
time.sleep(10)
|
||||||
|
|
||||||
def EOFhook(self):
|
def EOFhook(self):
|
||||||
"Override SocketIO method - terminate wait on callback and exit thread"
|
"Override SocketIO method - terminate wait on callback and exit thread"
|
||||||
global exit_requested
|
global quitting
|
||||||
exit_requested = True
|
quitting = True
|
||||||
|
interrupt.interrupt_main()
|
||||||
|
|
||||||
def decode_interrupthook(self):
|
def decode_interrupthook(self):
|
||||||
"interrupt awakened thread"
|
"interrupt awakened thread"
|
||||||
|
global quitting
|
||||||
|
quitting = True
|
||||||
interrupt.interrupt_main()
|
interrupt.interrupt_main()
|
||||||
|
|
||||||
|
|
||||||
|
@ -213,15 +228,10 @@ class Executive:
|
||||||
try:
|
try:
|
||||||
exec code in self.locals
|
exec code in self.locals
|
||||||
except:
|
except:
|
||||||
if exit_requested:
|
if quitting:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
try:
|
# even print a user code SystemExit exception, continue
|
||||||
# even print a user code SystemExit exception, continue
|
print_exception()
|
||||||
print_exception()
|
|
||||||
except:
|
|
||||||
# link not working?
|
|
||||||
traceback.print_exc(file=sys.__stderr__)
|
|
||||||
sys.exit(1.2)
|
|
||||||
else:
|
else:
|
||||||
flush_stdout()
|
flush_stdout()
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue