diff options
Diffstat (limited to 'lib/python2.7/idlelib/run.py')
-rw-r--r-- | lib/python2.7/idlelib/run.py | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/lib/python2.7/idlelib/run.py b/lib/python2.7/idlelib/run.py new file mode 100644 index 0000000..466c61e --- /dev/null +++ b/lib/python2.7/idlelib/run.py @@ -0,0 +1,375 @@ +import sys +import linecache +import time +import socket +import traceback +import thread +import threading +import Queue + +from idlelib import CallTips +from idlelib import AutoComplete + +from idlelib import RemoteDebugger +from idlelib import RemoteObjectBrowser +from idlelib import StackViewer +from idlelib import rpc +from idlelib import PyShell +from idlelib import IOBinding + +import __main__ + +LOCALHOST = '127.0.0.1' + +import warnings + +def idle_showwarning_subproc( + message, category, filename, lineno, file=None, line=None): + """Show Idle-format warning after replacing warnings.showwarning. + + The only difference is the formatter called. + """ + if file is None: + file = sys.stderr + try: + file.write(PyShell.idle_formatwarning( + message, category, filename, lineno, line)) + except IOError: + pass # the file (probably stderr) is invalid - this warning gets lost. + +_warnings_showwarning = None + +def capture_warnings(capture): + "Replace warning.showwarning with idle_showwarning_subproc, or reverse." + + global _warnings_showwarning + if capture: + if _warnings_showwarning is None: + _warnings_showwarning = warnings.showwarning + warnings.showwarning = idle_showwarning_subproc + else: + if _warnings_showwarning is not None: + warnings.showwarning = _warnings_showwarning + _warnings_showwarning = None + +capture_warnings(True) + +# Thread shared globals: Establish a queue between a subthread (which handles +# the socket) and the main thread (which runs user code), plus global +# completion, exit and interruptable (the main thread) flags: + +exit_now = False +quitting = False +interruptable = False + +def main(del_exitfunc=False): + """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 exit_now + global quitting + global no_exitfunc + no_exitfunc = del_exitfunc + #time.sleep(15) # test subprocess not responding + try: + assert(len(sys.argv) > 1) + port = int(sys.argv[-1]) + except: + print>>sys.stderr, "IDLE Subprocess: no IP port passed in sys.argv." + return + + capture_warnings(True) + sys.argv[:] = [""] + sockthread = threading.Thread(target=manage_socket, + name='SockThread', + args=((LOCALHOST, port),)) + sockthread.setDaemon(True) + sockthread.start() + while 1: + try: + if exit_now: + try: + exit() + except KeyboardInterrupt: + # exiting but got an extra KBI? Try again! + continue + try: + seq, request = rpc.request_queue.get(block=True, timeout=0.05) + except Queue.Empty: + continue + method, args, kwargs = request + ret = method(*args, **kwargs) + rpc.response_queue.put((seq, ret)) + except KeyboardInterrupt: + if quitting: + exit_now = True + continue + except SystemExit: + capture_warnings(False) + raise + except: + type, value, tb = sys.exc_info() + try: + print_exception() + rpc.response_queue.put((seq, None)) + except: + # Link didn't work, print same exception to __stderr__ + traceback.print_exception(type, value, tb, file=sys.__stderr__) + exit() + else: + continue + +def manage_socket(address): + for i in range(3): + time.sleep(i) + try: + server = MyRPCServer(address, MyHandler) + break + except socket.error as err: + print>>sys.__stderr__,"IDLE Subprocess: socket error: "\ + + err.args[1] + ", retrying...." + else: + print>>sys.__stderr__, "IDLE Subprocess: Connection to "\ + "IDLE GUI failed, exiting." + show_socket_error(err, address) + global exit_now + exit_now = True + return + server.handle_request() # A single request only + +def show_socket_error(err, address): + import Tkinter + import tkMessageBox + root = Tkinter.Tk() + root.withdraw() + if err.args[0] == 61: # connection refused + msg = "IDLE's subprocess can't connect to %s:%d. This may be due "\ + "to your personal firewall configuration. It is safe to "\ + "allow this internal connection because no data is visible on "\ + "external ports." % address + tkMessageBox.showerror("IDLE Subprocess Error", msg, parent=root) + else: + tkMessageBox.showerror("IDLE Subprocess Error", + "Socket Error: %s" % err.args[1], parent=root) + root.destroy() + +def print_exception(): + import linecache + linecache.checkcache() + flush_stdout() + efile = sys.stderr + typ, val, tb = excinfo = sys.exc_info() + sys.last_type, sys.last_value, sys.last_traceback = excinfo + tbe = traceback.extract_tb(tb) + print>>efile, '\nTraceback (most recent call last):' + exclude = ("run.py", "rpc.py", "threading.py", "Queue.py", + "RemoteDebugger.py", "bdb.py") + cleanup_traceback(tbe, exclude) + traceback.print_list(tbe, file=efile) + lines = traceback.format_exception_only(typ, val) + for line in lines: + print>>efile, line, + +def cleanup_traceback(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: " + rpchandler = rpc.objecttable['exec'].rpchandler + for i in range(len(tb)): + fn, ln, nm, line = tb[i] + if nm == '?': + nm = "-toplevel-" + if fn.startswith("<pyshell#") and IOBinding.encoding != 'utf-8': + ln -= 1 # correction for coding cookie + if not line and fn.startswith("<pyshell#"): + line = rpchandler.remotecall('linecache', 'getline', + (fn, ln), {}) + tb[i] = fn, ln, nm, line + +def flush_stdout(): + try: + if sys.stdout.softspace: + sys.stdout.softspace = 0 + sys.stdout.write("\n") + except (AttributeError, EOFError): + pass + +def exit(): + """Exit subprocess, possibly after first deleting sys.exitfunc + + If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any + sys.exitfunc will be removed before exiting. (VPython support) + + """ + if no_exitfunc: + try: + del sys.exitfunc + except AttributeError: + pass + capture_warnings(False) + sys.exit(0) + +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. + + """ + global quitting + try: + raise + except SystemExit: + raise + except EOFError: + global exit_now + exit_now = True + thread.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 + quitting = True + thread.interrupt_main() + +class MyHandler(rpc.RPCHandler): + + def handle(self): + """Override base method""" + executive = Executive(self) + self.register("exec", executive) + self.console = self.get_remote_proxy("console") + sys.stdin = PyShell.PseudoInputFile(self.console, "stdin", + IOBinding.encoding) + sys.stdout = PyShell.PseudoOutputFile(self.console, "stdout", + IOBinding.encoding) + sys.stderr = PyShell.PseudoOutputFile(self.console, "stderr", + IOBinding.encoding) + + # Keep a reference to stdin so that it won't try to exit IDLE if + # sys.stdin gets changed from within IDLE's shell. See issue17838. + self._keep_stdin = sys.stdin + + self.interp = self.get_remote_proxy("interp") + rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05) + + def exithook(self): + "override SocketIO method - wait for MainThread to shut us down" + time.sleep(10) + + def EOFhook(self): + "Override SocketIO method - terminate wait on callback and exit thread" + global quitting + quitting = True + thread.interrupt_main() + + def decode_interrupthook(self): + "interrupt awakened thread" + global quitting + quitting = True + thread.interrupt_main() + + +class Executive(object): + + def __init__(self, rpchandler): + self.rpchandler = rpchandler + self.locals = __main__.__dict__ + self.calltip = CallTips.CallTips() + self.autocomplete = AutoComplete.AutoComplete() + + def runcode(self, code): + global interruptable + try: + self.usr_exc_info = None + interruptable = True + try: + exec code in self.locals + finally: + interruptable = False + except SystemExit: + # Scripts that raise SystemExit should just + # return to the interactive prompt + pass + except: + self.usr_exc_info = sys.exc_info() + if quitting: + exit() + print_exception() + jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>") + if jit: + self.rpchandler.interp.open_remote_stack_viewer() + else: + flush_stdout() + + def interrupt_the_server(self): + if interruptable: + thread.interrupt_main() + + 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 get_the_completion_list(self, what, mode): + return self.autocomplete.fetch_completions(what, mode) + + def stackviewer(self, flist_oid=None): + if self.usr_exc_info: + typ, val, tb = self.usr_exc_info + else: + return None + flist = None + if flist_oid is not None: + flist = self.rpchandler.get_remote_proxy(flist_oid) + while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]: + tb = tb.tb_next + sys.last_type = typ + sys.last_value = val + item = StackViewer.StackTreeItem(flist, tb) + return RemoteObjectBrowser.remote_object_tree_item(item) + +capture_warnings(False) # Make sure turned off; see issue 18081 |