123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- 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
|