diff options
Diffstat (limited to 'Tools/idle/run.py')
-rw-r--r-- | Tools/idle/run.py | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/Tools/idle/run.py b/Tools/idle/run.py new file mode 100644 index 0000000..497cbbd --- /dev/null +++ b/Tools/idle/run.py @@ -0,0 +1,216 @@ +import sys +import time +import socket +import traceback +import threading +import Queue + +import boolcheck + +import CallTips +import RemoteDebugger +import RemoteObjectBrowser +import StackViewer +import rpc +import interrupt + +import __main__ + +# Thread shared globals: Establish a queue between a subthread (which handles +# 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 + + In the Python subprocess, RPCServer is instantiated with handlerclass + MyHandler, which inherits register/unregister methods from RPCHandler via + the mix-in class SocketIO. + + When the RPCServer 'server' is instantiated, the TCPServer initialization + creates an instance of run.MyHandler and calls its handle() method. + handle() instantiates a run.Executive object, passing it a reference to the + MyHandler object. That reference is saved as attribute rpchandler of the + Executive instance. The Executive methods have access to the reference and + can pass it on to entities that they command + (e.g. RemoteDebugger.Debugger.start_debugger()). The latter, in turn, can + call MyHandler(SocketIO) register/unregister methods via the reference to + register and unregister themselves. + + """ + global queue, execution_finished, exit_requested + + port = 8833 + if sys.argv[1:]: + port = int(sys.argv[1]) + sys.argv[:] = [""] + sockthread = threading.Thread(target=manage_socket, + name='SockThread', + args=(('localhost', port),)) + sockthread.setDaemon(True) + sockthread.start() + while 1: + 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: + if i < 3: + print>>sys.__stderr__, ".. ", + else: + print>>sys.__stderr__,"\nPython subprocess socket error: "\ + + err[1] + ", retrying...." + else: + print>>sys.__stderr__, "\nConnection to Idle failed, exiting." + exit_requested = True + server.handle_request() # A single request only + + +class MyHandler(rpc.RPCHandler): + + def handle(self): + """Override base method""" + executive = Executive(self) + self.register("exec", executive) + 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) + + +class Executive: + + def __init__(self, rpchandler): + self.rpchandler = rpchandler + self.locals = __main__.__dict__ + 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 + else: + self.flush_stdout() + execution_finished = True + + def flush_stdout(self): + try: + if sys.stdout.softspace: + sys.stdout.softspace = 0 + sys.stdout.write("\n") + except (AttributeError, EOFError): + pass + + def cleanup_traceback(self, tb, exclude): + "Remove excluded traces from beginning/end of tb; get cached lines" + orig_tb = tb[:] + while tb: + for rpcfile in exclude: + if tb[0][0].count(rpcfile): + break # found an exclude, break for: and delete tb[0] + else: + break # no excludes, have left RPC code, break while: + del tb[0] + while tb: + for rpcfile in exclude: + if tb[-1][0].count(rpcfile): + break + else: + break + del tb[-1] + if len(tb) == 0: + # exception was in IDLE internals, don't prune! + tb[:] = orig_tb[:] + print>>sys.stderr, "** IDLE Internal Exception: " + for i in range(len(tb)): + fn, ln, nm, line = tb[i] + if nm == '?': + nm = "-toplevel-" + if not line and fn.startswith("<pyshell#"): + line = self.rpchandler.remotecall('linecache', 'getline', + (fn, ln), {}) + 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) + + def stop_the_debugger(self, idb_adap_oid): + "Unregister the Idb Adapter. Link objects and Idb then subject to GC" + self.rpchandler.unregister(idb_adap_oid) + + def get_the_calltip(self, name): + return self.calltip.fetch_tip(name) + + def stackviewer(self, flist_oid=None): + if not hasattr(sys, "last_traceback"): + return None + flist = None + if flist_oid is not None: + flist = self.rpchandler.get_remote_proxy(flist_oid) + tb = sys.last_traceback + while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]: + tb = tb.tb_next + item = StackViewer.StackTreeItem(flist, tb) + return RemoteObjectBrowser.remote_object_tree_item(item) |