[Idle-dev] CVS: idle NEWS.txt,1.14,1.15 PyShell.py,1.59,1.60 rpc.py,1.22,1.23 run.py,1.14,1.15
Kurt B. Kaiser
kbk@users.sourceforge.net
Thu, 08 May 2003 13:26:58 -0700
Update of /cvsroot/idlefork/idle
In directory sc8-pr-cvs1:/tmp/cvs-serv5992
Modified Files:
NEWS.txt PyShell.py rpc.py run.py
Log Message:
1. Implement processing of user code in subprocess MainThread. Pass loop
is now interruptable on Windows.
2. Tweak signal.signal() wait parameters as called by various methods
to improve I/O response, especially on Windows.
3. Debugger is disabled at this check-in pending further development.
M NEWS.txt
M PyShell.py
M rpc.py
M run.py
Index: NEWS.txt
===================================================================
RCS file: /cvsroot/idlefork/idle/NEWS.txt,v
retrieving revision 1.14
retrieving revision 1.15
diff -C2 -r1.14 -r1.15
*** NEWS.txt 25 Apr 2003 17:48:08 -0000 1.14
--- NEWS.txt 8 May 2003 20:26:54 -0000 1.15
***************
*** 6,10 ****
===================================
! *Release date: 25-Apr-2003*
- Implemented the 'interrupt' extension module, which allows a subthread
--- 6,16 ----
===================================
! *Release date: XX-XXX-2003*
!
! - Improved I/O response by tweaking the wait parameter in various
! calls to signal.signal().
!
! - Implemented a threaded subprocess which allows interrupting a pass
! loop in user code using the 'interrupt' extension.
- Implemented the 'interrupt' extension module, which allows a subthread
***************
*** 37,45 ****
- Known issues:
- + Can't kill/restart a tight loop in the Windows version: add
- I/O to the loop or use the Task Manager to kill the subprocess.
+ Typing two Control-C in close succession when the subprocess is busy can
cause IDLE to lose communication with the subprocess. Please type one
! only and wait for the exception to complete.
+ Printing under some versions of Linux may be problematic.
--- 43,50 ----
- Known issues:
+ Typing two Control-C in close succession when the subprocess is busy can
cause IDLE to lose communication with the subprocess. Please type one
! only and wait for the exception to complete. If you do manage to
! interrupt the interrupt, simply restart the shell.
+ Printing under some versions of Linux may be problematic.
Index: PyShell.py
===================================================================
RCS file: /cvsroot/idlefork/idle/PyShell.py,v
retrieving revision 1.59
retrieving revision 1.60
diff -C2 -r1.59 -r1.60
*** PyShell.py 22 Mar 2003 19:40:18 -0000 1.59
--- PyShell.py 8 May 2003 20:26:54 -0000 1.60
***************
*** 36,39 ****
--- 36,44 ----
IDENTCHARS = string.ascii_letters + string.digits + "_"
+ try:
+ from signal import SIGTERM
+ except ImportError:
+ SIGTERM = 15
+
# Change warnings module to write to sys.__stderr__
try:
***************
*** 368,378 ****
pass
# Kill subprocess, spawn a new one, accept connection.
! try:
! self.interrupt_subprocess()
! self.shutdown_subprocess()
! self.rpcclt.close()
! os.wait()
! except:
! pass
self.tkconsole.executing = False
self.spawn_subprocess()
--- 373,378 ----
pass
# Kill subprocess, spawn a new one, accept connection.
! self.rpcclt.close()
! self.unix_terminate()
self.tkconsole.executing = False
self.spawn_subprocess()
***************
*** 392,431 ****
debug.load_breakpoints()
- def __signal_interrupt(self):
- try:
- from signal import SIGINT
- except ImportError:
- SIGINT = 2
- try:
- os.kill(self.rpcpid, SIGINT)
- except OSError: # subprocess may have already exited
- pass
-
def __request_interrupt(self):
! try:
! self.rpcclt.asynccall("exec", "interrupt_the_server", (), {})
! except:
! pass
def interrupt_subprocess(self):
! # XXX KBK 22Mar03 Use interrupt message on all platforms for now.
! # XXX if hasattr(os, "kill"):
! if False:
! 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 __request_shutdown(self):
! try:
! self.rpcclt.asynccall("exec", "shutdown_the_server", (), {})
! except:
! pass
! def shutdown_subprocess(self):
! t = threading.Thread(target=self.__request_shutdown)
! t.start()
! t.join()
def transfer_path(self):
--- 392,420 ----
debug.load_breakpoints()
def __request_interrupt(self):
! self.rpcclt.remotecall("exec", "interrupt_the_server", (), {})
def interrupt_subprocess(self):
! threading.Thread(target=self.__request_interrupt).start()
! def kill_subprocess(self):
! self.rpcclt.close()
! self.unix_terminate()
! self.tkconsole.executing = False
! self.rpcclt = None
! def unix_terminate(self):
! "UNIX: make sure subprocess is terminated and collect status"
! if hasattr(os, 'kill'):
! try:
! os.kill(self.rpcpid, SIGTERM)
! except OSError:
! # process already terminated:
! return
! else:
! try:
! os.waitpid(self.rpcpid, 0)
! except OSError:
! return
def transfer_path(self):
***************
*** 446,464 ****
return
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:
self.tkconsole.resetoutput()
--- 435,447 ----
return
try:
! response = clt.pollresponse(self.active_seq, wait=0.05)
! 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()
***************
*** 478,488 ****
# we received a response to the currently active seq number:
self.tkconsole.endexecuting()
!
! def kill_subprocess(self):
! clt = self.rpcclt
! if clt is not None:
! self.shutdown_subprocess()
! clt.close()
! self.rpcclt = None
debugger = None
--- 461,466 ----
# we received a response to the currently active seq number:
self.tkconsole.endexecuting()
! # Reschedule myself in 50 ms
! self.tkconsole.text.after(50, self.poll_subprocess)
debugger = None
***************
*** 496,500 ****
def remote_stack_viewer(self):
import RemoteObjectBrowser
! oid = self.rpcclt.remotecall("exec", "stackviewer", ("flist",), {})
if oid is None:
self.tkconsole.root.bell()
--- 474,478 ----
def remote_stack_viewer(self):
import RemoteObjectBrowser
! oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {})
if oid is None:
self.tkconsole.root.bell()
***************
*** 629,633 ****
return 0
if self.rpcclt:
! self.rpcclt.remotecall("exec", "runcode", (code,), {})
else:
exec code in self.locals
--- 607,611 ----
return 0
if self.rpcclt:
! self.rpcclt.remotequeue("exec", "runcode", (code,), {})
else:
exec code in self.locals
***************
*** 646,650 ****
try:
if not debugger and self.rpcclt is not None:
! self.active_seq = self.rpcclt.asynccall("exec", "runcode",
(code,), {})
elif debugger:
--- 624,628 ----
try:
if not debugger and self.rpcclt is not None:
! self.active_seq = self.rpcclt.asyncqueue("exec", "runcode",
(code,), {})
elif debugger:
***************
*** 713,717 ****
text.bind("<<end-of-file>>", self.eof_callback)
text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
! text.bind("<<toggle-debugger>>", self.toggle_debugger)
text.bind("<<open-python-shell>>", self.flist.open_shell)
text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
--- 691,695 ----
text.bind("<<end-of-file>>", self.eof_callback)
text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
! ##text.bind("<<toggle-debugger>>", self.toggle_debugger)
text.bind("<<open-python-shell>>", self.flist.open_shell)
text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
***************
*** 800,810 ****
self.resetoutput()
self.executing = 1
- ##self._cancel_check = self.cancel_check
- ##sys.settrace(self._cancel_check)
def endexecuting(self):
"Helper for ModifiedInterpreter"
- ##sys.settrace(None)
- ##self._cancel_check = None
self.executing = 0
self.canceled = 0
--- 778,784 ----
***************
*** 823,827 ****
# interrupt the subprocess
self.closing = True
- self.cancel_callback()
self.endexecuting()
return EditorWindow.close(self)
--- 797,800 ----
***************
*** 1017,1037 ****
line = line[:i]
more = self.interp.runsource(line)
-
- def cancel_check(self, frame, what, args,
- dooneevent=tkinter.dooneevent,
- dontwait=tkinter.DONT_WAIT):
- # Hack -- use the debugger hooks to be able to handle events
- # and interrupt execution at any time.
- # This slows execution down quite a bit, so you may want to
- # disable this (by not calling settrace() in beginexecuting() and
- # endexecuting() for full-bore (uninterruptable) speed.)
- # XXX This should become a user option.
- if self.canceled:
- return
- dooneevent(dontwait)
- if self.canceled:
- self.canceled = 0
- raise KeyboardInterrupt
- return self._cancel_check
def open_stack_viewer(self, event=None):
--- 990,993 ----
Index: rpc.py
===================================================================
RCS file: /cvsroot/idlefork/idle/rpc.py,v
retrieving revision 1.22
retrieving revision 1.23
diff -C2 -r1.22 -r1.23
*** rpc.py 22 Mar 2003 20:11:14 -0000 1.22
--- rpc.py 8 May 2003 20:26:54 -0000 1.23
***************
*** 29,32 ****
--- 29,33 ----
import sys
+ import os
import socket
import select
***************
*** 35,38 ****
--- 36,40 ----
import cPickle as pickle
import threading
+ import Queue
import traceback
import copy_reg
***************
*** 40,43 ****
--- 42,47 ----
import marshal
+ import interrupt
+
def unpickle_code(ms):
co = marshal.loads(ms)
***************
*** 99,104 ****
except SystemExit:
raise
- except EOFError:
- pass
except:
erf = sys.__stderr__
--- 103,106 ----
***************
*** 111,119 ****
print>>erf, '\n*** Unrecoverable, server exiting!'
print>>erf, '-'*40
- import os
os._exit(0)
objecttable = {}
class SocketIO:
--- 113,124 ----
print>>erf, '\n*** Unrecoverable, server exiting!'
print>>erf, '-'*40
os._exit(0)
+ #----------------- end class RPCServer --------------------
objecttable = {}
+ request_queue = Queue.Queue(0)
+ response_queue = Queue.Queue(0)
+
class SocketIO:
***************
*** 122,126 ****
def __init__(self, sock, objtable=None, debugging=None):
! self.mainthread = threading.currentThread()
if debugging is not None:
self.debugging = debugging
--- 127,131 ----
def __init__(self, sock, objtable=None, debugging=None):
! self.sockthread = threading.currentThread()
if debugging is not None:
self.debugging = debugging
***************
*** 129,136 ****
objtable = objecttable
self.objtable = objtable
- self.cvar = threading.Condition()
self.responses = {}
self.cvars = {}
- self.interrupted = False
def close(self):
--- 134,139 ----
***************
*** 140,143 ****
--- 143,150 ----
sock.close()
+ def exithook(self):
+ "override for specific exit action"
+ os._exit()
+
def debug(self, *args):
if not self.debugging:
***************
*** 157,161 ****
pass
! def localcall(self, request):
self.debug("localcall:", request)
try:
--- 164,168 ----
pass
! def localcall(self, seq, request):
self.debug("localcall:", request)
try:
***************
*** 163,167 ****
except TypeError:
return ("ERROR", "Bad request format")
- assert how == "call"
if not self.objtable.has_key(oid):
return ("ERROR", "Unknown object id: %s" % `oid`)
--- 170,173 ----
***************
*** 179,190 ****
method = getattr(obj, methodname)
try:
! ret = method(*args, **kwargs)
! if isinstance(ret, RemoteObject):
! ret = remoteref(ret)
! return ("OK", ret)
except SystemExit:
raise
except socket.error:
! pass
except:
self.debug("localcall:EXCEPTION")
--- 185,202 ----
method = getattr(obj, methodname)
try:
! if how == 'CALL':
! ret = method(*args, **kwargs)
! if isinstance(ret, RemoteObject):
! ret = remoteref(ret)
! return ("OK", ret)
! elif how == 'QUEUE':
! request_queue.put((seq, (method, args, kwargs)))
! return("QUEUED", None)
! else:
! return ("ERROR", "Unsupported message type: %s" % how)
except SystemExit:
raise
except socket.error:
! raise
except:
self.debug("localcall:EXCEPTION")
***************
*** 194,215 ****
def remotecall(self, oid, methodname, args, kwargs):
self.debug("remotecall:asynccall: ", oid, methodname)
- # XXX KBK 06Feb03 self.interrupted logic may not be necessary if
- # subprocess is threaded.
- if self.interrupted:
- self.interrupted = False
- raise KeyboardInterrupt
seq = self.asynccall(oid, methodname, args, kwargs)
return self.asyncreturn(seq)
def asynccall(self, oid, methodname, args, kwargs):
! request = ("call", (oid, methodname, args, kwargs))
seq = self.newseq()
self.debug(("asynccall:%d:" % seq), oid, methodname, args, kwargs)
self.putmessage((seq, request))
return seq
def asyncreturn(self, seq):
self.debug("asyncreturn:%d:call getresponse(): " % seq)
! response = self.getresponse(seq, wait=None)
self.debug(("asyncreturn:%d:response: " % seq), response)
return self.decoderesponse(response)
--- 206,240 ----
def remotecall(self, oid, methodname, args, kwargs):
self.debug("remotecall:asynccall: ", oid, methodname)
seq = self.asynccall(oid, methodname, args, kwargs)
return self.asyncreturn(seq)
+ def remotequeue(self, oid, methodname, args, kwargs):
+ self.debug("remotequeue:asyncqueue: ", oid, methodname)
+ seq = self.asyncqueue(oid, methodname, args, kwargs)
+ return self.asyncreturn(seq)
+
def asynccall(self, oid, methodname, args, kwargs):
! request = ("CALL", (oid, methodname, args, kwargs))
seq = self.newseq()
+ if threading.currentThread() != self.sockthread:
+ cvar = threading.Condition()
+ self.cvars[seq] = cvar
self.debug(("asynccall:%d:" % seq), oid, methodname, args, kwargs)
self.putmessage((seq, request))
return seq
+ def asyncqueue(self, oid, methodname, args, kwargs):
+ request = ("QUEUE", (oid, methodname, args, kwargs))
+ seq = self.newseq()
+ if threading.currentThread() != self.sockthread:
+ cvar = threading.Condition()
+ self.cvars[seq] = cvar
+ self.debug(("asyncqueue:%d:" % seq), oid, methodname, args, kwargs)
+ self.putmessage((seq, request))
+ return seq
+
def asyncreturn(self, seq):
self.debug("asyncreturn:%d:call getresponse(): " % seq)
! response = self.getresponse(seq, wait=0.05)
self.debug(("asyncreturn:%d:response: " % seq), response)
return self.decoderesponse(response)
***************
*** 219,225 ****
--- 244,256 ----
if how == "OK":
return what
+ if how == "QUEUED":
+ return None
if how == "EXCEPTION":
self.debug("decoderesponse: EXCEPTION")
return None
+ if how == "EOF":
+ self.debug("decoderesponse: EOF")
+ self.decode_interrupthook()
+ return None
if how == "ERROR":
self.debug("decoderesponse: Internal ERROR:", what)
***************
*** 227,241 ****
raise SystemError, (how, what)
def mainloop(self):
"""Listen on socket until I/O not ready or EOF
! Main thread pollresponse() will loop looking for seq number None, which
never comes, and exit on EOFError.
"""
try:
! self.getresponse(myseq=None, wait=None)
except EOFError:
! pass
def getresponse(self, myseq, wait):
--- 258,277 ----
raise SystemError, (how, what)
+ def decode_interrupthook(self):
+ ""
+ raise EOFError
+
def mainloop(self):
"""Listen on socket until I/O not ready or EOF
! pollresponse() will loop looking for seq number None, which
never comes, and exit on EOFError.
"""
try:
! self.getresponse(myseq=None, wait=0.05)
except EOFError:
! self.debug("mainloop:return")
! return
def getresponse(self, myseq, wait):
***************
*** 257,263 ****
def _getresponse(self, myseq, wait):
self.debug("_getresponse:myseq:", myseq)
! if threading.currentThread() is self.mainthread:
! # Main thread: does all reading of requests or responses
! # Loop here, blocking each time until socket is ready.
while 1:
response = self.pollresponse(myseq, wait)
--- 293,298 ----
def _getresponse(self, myseq, wait):
self.debug("_getresponse:myseq:", myseq)
! if threading.currentThread() is self.sockthread:
! # this thread does all reading of requests or responses
while 1:
response = self.pollresponse(myseq, wait)
***************
*** 265,277 ****
return response
else:
! # Auxiliary thread: wait for notification from main thread
! self.cvar.acquire()
! self.cvars[myseq] = self.cvar
while not self.responses.has_key(myseq):
! self.cvar.wait()
response = self.responses[myseq]
del self.responses[myseq]
del self.cvars[myseq]
! self.cvar.release()
return response
--- 300,314 ----
return response
else:
! # wait for notification from socket handling thread
! cvar = self.cvars[myseq]
! cvar.acquire()
while not self.responses.has_key(myseq):
! cvar.wait()
response = self.responses[myseq]
+ self.debug("_getresponse:%s: thread woke up: response: %s" %
+ (myseq, response))
del self.responses[myseq]
del self.cvars[myseq]
! cvar.release()
return response
***************
*** 284,288 ****
try:
s = pickle.dumps(message)
! except:
print >>sys.__stderr__, "Cannot pickle:", `message`
raise
--- 321,325 ----
try:
s = pickle.dumps(message)
! except pickle.UnpicklingError:
print >>sys.__stderr__, "Cannot pickle:", `message`
raise
***************
*** 294,301 ****
# socket was closed
raise IOError
else:
s = s[n:]
! def ioready(self, wait=0.0):
r, w, x = select.select([self.sock.fileno()], [], [], wait)
return len(r)
--- 331,341 ----
# socket was closed
raise IOError
+ except socket.error:
+ self.debug("putmessage:socketerror:pid:%s" % os.getpid())
+ os._exit(0)
else:
s = s[n:]
! def ioready(self, wait):
r, w, x = select.select([self.sock.fileno()], [], [], wait)
return len(r)
***************
*** 305,309 ****
bufstate = 0 # meaning: 0 => reading count; 1 => reading data
! def pollpacket(self, wait=0.0):
self._stage0()
if len(self.buffer) < self.bufneed:
--- 345,349 ----
bufstate = 0 # meaning: 0 => reading count; 1 => reading data
! def pollpacket(self, wait):
self._stage0()
if len(self.buffer) < self.bufneed:
***************
*** 335,339 ****
return packet
! def pollmessage(self, wait=0.0):
packet = self.pollpacket(wait)
if packet is None:
--- 375,379 ----
return packet
! def pollmessage(self, wait):
packet = self.pollpacket(wait)
if packet is None:
***************
*** 349,391 ****
return message
! def pollresponse(self, myseq, wait=0.0):
"""Handle messages received on the socket.
! Some messages received may be asynchronous 'call' commands, and
! some may be responses intended for other threads.
! Loop until message with myseq sequence number is received. Save others
! in self.responses and notify the owning thread, except that 'call'
! commands are handed off to localcall() and the response sent back
! across the link with the appropriate sequence number.
"""
while 1:
! message = self.pollmessage(wait)
! if message is None: # socket not ready
return None
- #wait = 0.0 # poll on subsequent passes instead of blocking
seq, resq = message
self.debug("pollresponse:%d:myseq:%s" % (seq, myseq))
! if resq[0] == "call":
self.debug("pollresponse:%d:localcall:call:" % seq)
! response = self.localcall(resq)
self.debug("pollresponse:%d:localcall:response:%s"
% (seq, response))
! self.putmessage((seq, response))
continue
elif seq == myseq:
return resq
else:
! self.cvar.acquire()
! cv = self.cvars.get(seq)
# response involving unknown sequence number is discarded,
! # probably intended for prior incarnation
if cv is not None:
self.responses[seq] = resq
cv.notify()
! self.cvar.release()
continue
#----------------- end class SocketIO --------------------
--- 389,483 ----
return message
! def pollresponse(self, myseq, wait):
"""Handle messages received on the socket.
! Some messages received may be asynchronous 'call' or 'queue' requests,
! and some may be responses for other threads.
!
! 'call' requests are passed to self.localcall() with the expectation of
! immediate execution, during which time the socket is not serviced.
! 'queue' requests are used for tasks (which may block or hang) to be
! processed in a different thread. These requests are fed into
! request_queue by self.localcall(). Responses to queued requests are
! taken from response_queue and sent across the link with the associated
! sequence numbers. Messages in the queues are (sequence_number,
! request/response) tuples and code using this module removing messages
! from the request_queue is responsible for returning the correct
! sequence number in the response_queue.
!
! pollresponse() will loop until a response message with the myseq
! sequence number is received, and will save other responses in
! self.responses and notify the owning thread.
"""
while 1:
! # send queued response if there is one available
! try:
! qmsg = response_queue.get(0)
! except Queue.Empty:
! pass
! else:
! seq, response = qmsg
! message = (seq, ('OK', response))
! self.putmessage(message)
! # poll for message on link
! try:
! message = self.pollmessage(wait)
! if message is None: # socket not ready
! return None
! except EOFError:
! self.handle_EOF()
! return None
! except AttributeError:
return None
seq, resq = message
+ how = resq[0]
self.debug("pollresponse:%d:myseq:%s" % (seq, myseq))
! # process or queue a request
! if how in ("CALL", "QUEUE"):
self.debug("pollresponse:%d:localcall:call:" % seq)
! response = self.localcall(seq, resq)
self.debug("pollresponse:%d:localcall:response:%s"
% (seq, response))
! if how == "CALL":
! self.putmessage((seq, response))
! elif how == "QUEUE":
! # don't acknowledge the 'queue' request!
! pass
continue
+ # return if completed message transaction
elif seq == myseq:
return resq
+ # must be a response for a different thread:
else:
! cv = self.cvars.get(seq, None)
# response involving unknown sequence number is discarded,
! # probably intended for prior incarnation of server
if cv is not None:
+ cv.acquire()
self.responses[seq] = resq
cv.notify()
! cv.release()
continue
+ def handle_EOF(self):
+ "action taken upon link being closed by peer"
+ self.EOFhook()
+ self.debug("handle_EOF")
+ for key in self.cvars:
+ cv = self.cvars[key]
+ cv.acquire()
+ self.responses[key] = ('EOF', None)
+ cv.notify()
+ cv.release()
+ interrupt.interrupt_main()
+ # call our (possibly overridden) exit function
+ self.exithook()
+
+ def EOFhook(self):
+ "Classes using rpc client/server can override to augment EOF action"
+ pass
+
#----------------- end class SocketIO --------------------
***************
*** 466,470 ****
if not self.__attributes.has_key(name):
raise AttributeError, name
! __getattr__.DebuggerStepThrough=1
def __getattributes(self):
--- 558,563 ----
if not self.__attributes.has_key(name):
raise AttributeError, name
!
! __getattr__.DebuggerStepThrough = 1
def __getattributes(self):
Index: run.py
===================================================================
RCS file: /cvsroot/idlefork/idle/run.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -C2 -r1.14 -r1.15
*** run.py 22 Mar 2003 19:40:19 -0000 1.14
--- run.py 8 May 2003 20:26:55 -0000 1.15
***************
*** 1,3 ****
--- 1,4 ----
import sys
+ import os
import time
import socket
***************
*** 21,30 ****
# completion and exit flags:
- server = None # RPCServer instance
- queue = Queue.Queue(0)
- execution_finished = False
exit_requested = False
-
def main():
"""Start the Python execution server in a subprocess
--- 22,27 ----
***************
*** 45,50 ****
"""
- global queue, execution_finished, exit_requested
-
port = 8833
if sys.argv[1:]:
--- 42,45 ----
***************
*** 59,77 ****
try:
if exit_requested:
! sys.exit()
! # XXX KBK 22Mar03 eventually check queue here!
! pass
! time.sleep(0.05)
except KeyboardInterrupt:
- ##execution_finished = True
continue
def manage_socket(address):
- global server, exit_requested
-
for i in range(6):
time.sleep(i)
try:
! server = rpc.RPCServer(address, MyHandler)
break
except socket.error, err:
--- 54,74 ----
try:
if exit_requested:
! os._exit(0)
! try:
! seq, request = rpc.request_queue.get(0)
! except Queue.Empty:
! time.sleep(0.05)
! continue
! method, args, kwargs = request
! ret = method(*args, **kwargs)
! rpc.response_queue.put((seq, ret))
except KeyboardInterrupt:
continue
def manage_socket(address):
for i in range(6):
time.sleep(i)
try:
! server = MyRPCServer(address, MyHandler)
break
except socket.error, err:
***************
*** 83,90 ****
--- 80,118 ----
else:
print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
+ global exit_requested
exit_requested = True
+ return
server.handle_request() # A single request only
+ class MyRPCServer(rpc.RPCServer):
+
+ def handle_error(self, request, client_address):
+ """Override RPCServer method for IDLE
+
+ Interrupt the MainThread and exit server if link is dropped.
+
+ """
+ try:
+ raise
+ except SystemExit:
+ raise
+ except EOFError:
+ global exit_requested
+ exit_requested = True
+ interrupt.interrupt_main()
+ except:
+ erf = sys.__stderr__
+ print>>erf, '\n' + '-'*40
+ print>>erf, 'Unhandled server exception!'
+ print>>erf, 'Thread: %s' % threading.currentThread().getName()
+ print>>erf, 'Client Address: ', client_address
+ print>>erf, 'Request: ', repr(request)
+ traceback.print_exc(file=erf)
+ print>>erf, '\n*** Unrecoverable, server exiting!'
+ print>>erf, '-'*40
+ os._exit(0)
+
+
class MyHandler(rpc.RPCHandler):
***************
*** 96,100 ****
sys.stdout = self.get_remote_proxy("stdout")
sys.stderr = self.get_remote_proxy("stderr")
! rpc.RPCHandler.getresponse(self, myseq=None, wait=0.5)
--- 124,141 ----
sys.stdout = self.get_remote_proxy("stdout")
sys.stderr = self.get_remote_proxy("stderr")
! rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
!
! def exithook(self):
! "override SocketIO method - wait for MainThread to shut us down"
! while 1: pass
!
! def EOFhook(self):
! "Override SocketIO method - terminate wait on callback and exit thread"
! global exit_requested
! exit_requested = True
!
! def decode_interrupthook(self):
! "interrupt awakened thread"
! interrupt.interrupt_main()
***************
*** 107,148 ****
def runcode(self, code):
- global queue, execution_finished
-
- execution_finished = False
- queue.put(code)
- # dequeue and run in subthread
- self.runcode_from_queue()
- while not execution_finished:
- time.sleep(0.05)
-
- def runcode_from_queue(self):
- global queue, execution_finished
-
- # poll until queue has code object, using threads, just block?
- while True:
- try:
- code = queue.get(0)
- break
- except Queue.Empty:
- time.sleep(0.05)
try:
exec code in self.locals
except:
! self.flush_stdout()
! efile = sys.stderr
! typ, val, tb = info = sys.exc_info()
! sys.last_type, sys.last_value, sys.last_traceback = info
! tbe = traceback.extract_tb(tb)
! print >>efile, 'Traceback (most recent call last):'
! exclude = ("run.py", "rpc.py", "RemoteDebugger.py", "bdb.py")
! self.cleanup_traceback(tbe, exclude)
! traceback.print_list(tbe, file=efile)
! lines = traceback.format_exception_only(typ, val)
! for line in lines:
! print>>efile, line,
! execution_finished = True
else:
self.flush_stdout()
- execution_finished = True
def flush_stdout(self):
--- 148,175 ----
def runcode(self, code):
try:
exec code in self.locals
except:
! try:
! if exit_requested:
! os._exit(0)
! self.flush_stdout()
! efile = sys.stderr
! typ, val, tb = info = sys.exc_info()
! sys.last_type, sys.last_value, sys.last_traceback = info
! tbe = traceback.extract_tb(tb)
! print >>efile, 'Traceback (most recent call last):'
! exclude = ("run.py", "rpc.py", "threading.py",
! "RemoteDebugger.py", "bdb.py")
! self.cleanup_traceback(tbe, exclude)
! traceback.print_list(tbe, file=efile)
! lines = traceback.format_exception_only(typ, val)
! for line in lines:
! print>>efile, line,
! except:
! sys.stderr = sys.__stderr__
! raise
else:
self.flush_stdout()
def flush_stdout(self):
***************
*** 185,196 ****
def interrupt_the_server(self):
- self.rpchandler.interrupted = True
- ##print>>sys.__stderr__, "** Interrupt main!"
interrupt.interrupt_main()
-
- def shutdown_the_server(self):
- global exit_requested
-
- exit_requested = True
def start_the_debugger(self, gui_adap_oid):
--- 212,216 ----