diff options
author | Kurt B. Kaiser <kbk@shore.net> | 2003-05-08 20:26:55 (GMT) |
---|---|---|
committer | Kurt B. Kaiser <kbk@shore.net> | 2003-05-08 20:26:55 (GMT) |
commit | a00050f209acf2201b2382f9d534a2595bacf5c3 (patch) | |
tree | c3b85c94b6edeaca2174e6a96e1fb6af421ecfd1 | |
parent | c4607dadce95073361a091d2c4cef42de5be44a3 (diff) | |
download | cpython-a00050f209acf2201b2382f9d534a2595bacf5c3.zip cpython-a00050f209acf2201b2382f9d534a2595bacf5c3.tar.gz cpython-a00050f209acf2201b2382f9d534a2595bacf5c3.tar.bz2 |
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
-rw-r--r-- | Lib/idlelib/NEWS.txt | 13 | ||||
-rw-r--r-- | Lib/idlelib/PyShell.py | 118 | ||||
-rw-r--r-- | Lib/idlelib/rpc.py | 199 | ||||
-rw-r--r-- | Lib/idlelib/run.py | 130 |
4 files changed, 267 insertions, 193 deletions
diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 2ee9776..94c0d12 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -5,7 +5,13 @@ IDLEfork NEWS What's New in IDLEfork 0.9b1? =================================== -*Release date: 25-Apr-2003* +*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 to raise a KeyboardInterrupt in the main thread. @@ -36,11 +42,10 @@ What's New in IDLEfork 0.9b1? - 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. + 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. diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py index 9f158f5..483a921 100644 --- a/Lib/idlelib/PyShell.py +++ b/Lib/idlelib/PyShell.py @@ -35,6 +35,11 @@ import RemoteDebugger IDENTCHARS = string.ascii_letters + string.digits + "_" +try: + from signal import SIGTERM +except ImportError: + SIGTERM = 15 + # Change warnings module to write to sys.__stderr__ try: import warnings @@ -367,13 +372,8 @@ class ModifiedInterpreter(InteractiveInterpreter): except: pass # Kill subprocess, spawn a new one, accept connection. - try: - self.interrupt_subprocess() - self.shutdown_subprocess() - self.rpcclt.close() - os.wait() - except: - pass + self.rpcclt.close() + self.unix_terminate() self.tkconsole.executing = False self.spawn_subprocess() self.rpcclt.accept() @@ -391,42 +391,31 @@ class ModifiedInterpreter(InteractiveInterpreter): # reload remote debugger breakpoints for all PyShellEditWindows 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 + self.rpcclt.remotecall("exec", "interrupt_the_server", (), {}) 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() + threading.Thread(target=self.__request_interrupt).start() - def __request_shutdown(self): - try: - self.rpcclt.asynccall("exec", "shutdown_the_server", (), {}) - except: - pass + def kill_subprocess(self): + self.rpcclt.close() + self.unix_terminate() + self.tkconsole.executing = False + self.rpcclt = None - def shutdown_subprocess(self): - t = threading.Thread(target=self.__request_shutdown) - t.start() - t.join() + 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): self.runcommand("""if 1: @@ -445,21 +434,15 @@ class ModifiedInterpreter(InteractiveInterpreter): if clt is None: return try: - response = clt.pollresponse(self.active_seq) - except (EOFError, IOError): - # lost connection: subprocess terminated itself, restart + 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 - 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() self.active_seq = None @@ -477,13 +460,8 @@ class ModifiedInterpreter(InteractiveInterpreter): print >>console, errmsg, what # 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 + # Reschedule myself in 50 ms + self.tkconsole.text.after(50, self.poll_subprocess) debugger = None @@ -495,7 +473,7 @@ class ModifiedInterpreter(InteractiveInterpreter): def remote_stack_viewer(self): import RemoteObjectBrowser - oid = self.rpcclt.remotecall("exec", "stackviewer", ("flist",), {}) + oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {}) if oid is None: self.tkconsole.root.bell() return @@ -628,7 +606,7 @@ class ModifiedInterpreter(InteractiveInterpreter): self.display_executing_dialog() return 0 if self.rpcclt: - self.rpcclt.remotecall("exec", "runcode", (code,), {}) + self.rpcclt.remotequeue("exec", "runcode", (code,), {}) else: exec code in self.locals return 1 @@ -645,7 +623,7 @@ class ModifiedInterpreter(InteractiveInterpreter): self.tkconsole.beginexecuting() try: if not debugger and self.rpcclt is not None: - self.active_seq = self.rpcclt.asynccall("exec", "runcode", + self.active_seq = self.rpcclt.asyncqueue("exec", "runcode", (code,), {}) elif debugger: debugger.run(code, self.locals) @@ -712,7 +690,7 @@ class PyShell(OutputWindow): text.bind("<<beginning-of-line>>", self.home_callback) 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("<<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) text.bind("<<view-restart>>", self.view_restart_mark) @@ -799,13 +777,9 @@ class PyShell(OutputWindow): "Helper for ModifiedInterpreter" 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 self.showprompt() @@ -822,7 +796,6 @@ class PyShell(OutputWindow): return "cancel" # interrupt the subprocess self.closing = True - self.cancel_callback() self.endexecuting() return EditorWindow.close(self) @@ -1017,23 +990,6 @@ class PyShell(OutputWindow): 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): if self.interp.rpcclt: return self.interp.remote_stack_viewer() diff --git a/Lib/idlelib/rpc.py b/Lib/idlelib/rpc.py index 15946a6..4c3ef3e 100644 --- a/Lib/idlelib/rpc.py +++ b/Lib/idlelib/rpc.py @@ -28,17 +28,21 @@ accomplished in Idle. """ import sys +import os import socket import select import SocketServer import struct import cPickle as pickle import threading +import Queue import traceback import copy_reg import types import marshal +import interrupt + def unpickle_code(ms): co = marshal.loads(ms) assert isinstance(co, types.CodeType) @@ -98,8 +102,6 @@ class RPCServer(SocketServer.TCPServer): raise except SystemExit: raise - except EOFError: - pass except: erf = sys.__stderr__ print>>erf, '\n' + '-'*40 @@ -110,28 +112,29 @@ class RPCServer(SocketServer.TCPServer): traceback.print_exc(file=erf) print>>erf, '\n*** Unrecoverable, server exiting!' print>>erf, '-'*40 - import os os._exit(0) +#----------------- end class RPCServer -------------------- objecttable = {} +request_queue = Queue.Queue(0) +response_queue = Queue.Queue(0) + class SocketIO: nextseq = 0 def __init__(self, sock, objtable=None, debugging=None): - self.mainthread = threading.currentThread() + self.sockthread = threading.currentThread() if debugging is not None: self.debugging = debugging self.sock = sock if objtable is None: objtable = objecttable self.objtable = objtable - self.cvar = threading.Condition() self.responses = {} self.cvars = {} - self.interrupted = False def close(self): sock = self.sock @@ -139,6 +142,10 @@ class SocketIO: if sock is not None: sock.close() + def exithook(self): + "override for specific exit action" + os._exit() + def debug(self, *args): if not self.debugging: return @@ -156,13 +163,12 @@ class SocketIO: except KeyError: pass - def localcall(self, request): + def localcall(self, seq, request): self.debug("localcall:", request) try: how, (oid, methodname, args, kwargs) = request except TypeError: return ("ERROR", "Bad request format") - assert how == "call" if not self.objtable.has_key(oid): return ("ERROR", "Unknown object id: %s" % `oid`) obj = self.objtable[oid] @@ -178,14 +184,20 @@ class SocketIO: return ("ERROR", "Unsupported method name: %s" % `methodname`) method = getattr(obj, methodname) try: - ret = method(*args, **kwargs) - if isinstance(ret, RemoteObject): - ret = remoteref(ret) - return ("OK", ret) + 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: - pass + raise except: self.debug("localcall:EXCEPTION") traceback.print_exc(file=sys.__stderr__) @@ -193,24 +205,37 @@ class SocketIO: 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 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)) + 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=None) + response = self.getresponse(seq, wait=0.05) self.debug(("asyncreturn:%d:response: " % seq), response) return self.decoderesponse(response) @@ -218,25 +243,36 @@ class SocketIO: how, what = response 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) raise RuntimeError, what raise SystemError, (how, what) + def decode_interrupthook(self): + "" + raise EOFError + def mainloop(self): """Listen on socket until I/O not ready or EOF - Main thread pollresponse() will loop looking for seq number None, which + pollresponse() will loop looking for seq number None, which never comes, and exit on EOFError. """ try: - self.getresponse(myseq=None, wait=None) + self.getresponse(myseq=None, wait=0.05) except EOFError: - pass + self.debug("mainloop:return") + return def getresponse(self, myseq, wait): response = self._getresponse(myseq, wait) @@ -256,23 +292,24 @@ class SocketIO: 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. + if threading.currentThread() is self.sockthread: + # this thread does all reading of requests or responses while 1: response = self.pollresponse(myseq, wait) if response is not None: return response else: - # Auxiliary thread: wait for notification from main thread - self.cvar.acquire() - self.cvars[myseq] = self.cvar + # wait for notification from socket handling thread + cvar = self.cvars[myseq] + cvar.acquire() while not self.responses.has_key(myseq): - self.cvar.wait() + 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] - self.cvar.release() + cvar.release() return response def newseq(self): @@ -283,7 +320,7 @@ class SocketIO: self.debug("putmessage:%d:" % message[0]) try: s = pickle.dumps(message) - except: + except pickle.UnpicklingError: print >>sys.__stderr__, "Cannot pickle:", `message` raise s = struct.pack("<i", len(s)) + s @@ -293,10 +330,13 @@ class SocketIO: except AttributeError: # 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=0.0): + def ioready(self, wait): r, w, x = select.select([self.sock.fileno()], [], [], wait) return len(r) @@ -304,7 +344,7 @@ class SocketIO: bufneed = 4 bufstate = 0 # meaning: 0 => reading count; 1 => reading data - def pollpacket(self, wait=0.0): + def pollpacket(self, wait): self._stage0() if len(self.buffer) < self.bufneed: if not self.ioready(wait): @@ -334,7 +374,7 @@ class SocketIO: self.bufstate = 0 return packet - def pollmessage(self, wait=0.0): + def pollmessage(self, wait): packet = self.pollpacket(wait) if packet is None: return None @@ -348,45 +388,97 @@ class SocketIO: raise return message - def pollresponse(self, myseq, wait=0.0): + def pollresponse(self, myseq, wait): """Handle messages received on the socket. - Some messages received may be asynchronous 'call' commands, and - some may be responses intended for other threads. + 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. - 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. + '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: - message = self.pollmessage(wait) - if message is None: # socket not ready + # 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 - #wait = 0.0 # poll on subsequent passes instead of blocking seq, resq = message + how = resq[0] self.debug("pollresponse:%d:myseq:%s" % (seq, myseq)) - if resq[0] == "call": + # process or queue a request + if how in ("CALL", "QUEUE"): self.debug("pollresponse:%d:localcall:call:" % seq) - response = self.localcall(resq) + response = self.localcall(seq, resq) self.debug("pollresponse:%d:localcall:response:%s" % (seq, response)) - self.putmessage((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: - self.cvar.acquire() - cv = self.cvars.get(seq) + cv = self.cvars.get(seq, None) # response involving unknown sequence number is discarded, - # probably intended for prior incarnation + # probably intended for prior incarnation of server if cv is not None: + cv.acquire() self.responses[seq] = resq cv.notify() - self.cvar.release() + 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 -------------------- class RemoteObject: @@ -465,7 +557,8 @@ class RPCProxy: self.__getattributes() if not self.__attributes.has_key(name): raise AttributeError, name - __getattr__.DebuggerStepThrough=1 + + __getattr__.DebuggerStepThrough = 1 def __getattributes(self): self.__attributes = self.sockio.remotecall(self.oid, diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index 497cbbd..320525c 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -1,4 +1,5 @@ import sys +import os import time import socket import traceback @@ -20,12 +21,8 @@ import __main__ # the socket) and the main thread (which runs user code), plus global # 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 @@ -44,8 +41,6 @@ def main(): register and unregister themselves. """ - global queue, execution_finished, exit_requested - port = 8833 if sys.argv[1:]: port = int(sys.argv[1]) @@ -58,21 +53,23 @@ def main(): while 1: try: if exit_requested: - sys.exit() - # XXX KBK 22Mar03 eventually check queue here! - pass - time.sleep(0.05) + 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: - ##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) + server = MyRPCServer(address, MyHandler) break except socket.error, err: if i < 3: @@ -82,10 +79,41 @@ def manage_socket(address): + err[1] + ", retrying...." 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): def handle(self): @@ -95,7 +123,20 @@ class MyHandler(rpc.RPCHandler): sys.stdin = self.get_remote_proxy("stdin") sys.stdout = self.get_remote_proxy("stdout") sys.stderr = self.get_remote_proxy("stderr") - rpc.RPCHandler.getresponse(self, myseq=None, wait=0.5) + 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() class Executive: @@ -106,44 +147,30 @@ class Executive: self.calltip = CallTips.CallTips() 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 + 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() - execution_finished = True def flush_stdout(self): try: @@ -184,15 +211,8 @@ class Executive: tb[i] = fn, ln, nm, line 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): return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid) |