1import sys 2import io 3import linecache 4import time 5import socket 6import traceback 7import thread 8import threading 9import Queue 10 11from idlelib import CallTips 12from idlelib import AutoComplete 13 14from idlelib import RemoteDebugger 15from idlelib import RemoteObjectBrowser 16from idlelib import StackViewer 17from idlelib import rpc 18from idlelib import PyShell 19from idlelib import IOBinding 20 21import __main__ 22 23LOCALHOST = '127.0.0.1' 24 25try: 26 import warnings 27except ImportError: 28 pass 29else: 30 def idle_formatwarning_subproc(message, category, filename, lineno, 31 line=None): 32 """Format warnings the IDLE way""" 33 s = "\nWarning (from warnings module):\n" 34 s += ' File \"%s\", line %s\n' % (filename, lineno) 35 if line is None: 36 line = linecache.getline(filename, lineno) 37 line = line.strip() 38 if line: 39 s += " %s\n" % line 40 s += "%s: %s\n" % (category.__name__, message) 41 return s 42 warnings.formatwarning = idle_formatwarning_subproc 43 44# Thread shared globals: Establish a queue between a subthread (which handles 45# the socket) and the main thread (which runs user code), plus global 46# completion, exit and interruptable (the main thread) flags: 47 48exit_now = False 49quitting = False 50interruptable = False 51 52def main(del_exitfunc=False): 53 """Start the Python execution server in a subprocess 54 55 In the Python subprocess, RPCServer is instantiated with handlerclass 56 MyHandler, which inherits register/unregister methods from RPCHandler via 57 the mix-in class SocketIO. 58 59 When the RPCServer 'server' is instantiated, the TCPServer initialization 60 creates an instance of run.MyHandler and calls its handle() method. 61 handle() instantiates a run.Executive object, passing it a reference to the 62 MyHandler object. That reference is saved as attribute rpchandler of the 63 Executive instance. The Executive methods have access to the reference and 64 can pass it on to entities that they command 65 (e.g. RemoteDebugger.Debugger.start_debugger()). The latter, in turn, can 66 call MyHandler(SocketIO) register/unregister methods via the reference to 67 register and unregister themselves. 68 69 """ 70 global exit_now 71 global quitting 72 global no_exitfunc 73 no_exitfunc = del_exitfunc 74 #time.sleep(15) # test subprocess not responding 75 try: 76 assert(len(sys.argv) > 1) 77 port = int(sys.argv[-1]) 78 except: 79 print>>sys.stderr, "IDLE Subprocess: no IP port passed in sys.argv." 80 return 81 sys.argv[:] = [""] 82 sockthread = threading.Thread(target=manage_socket, 83 name='SockThread', 84 args=((LOCALHOST, port),)) 85 sockthread.setDaemon(True) 86 sockthread.start() 87 while 1: 88 try: 89 if exit_now: 90 try: 91 exit() 92 except KeyboardInterrupt: 93 # exiting but got an extra KBI? Try again! 94 continue 95 try: 96 seq, request = rpc.request_queue.get(block=True, timeout=0.05) 97 except Queue.Empty: 98 continue 99 method, args, kwargs = request 100 ret = method(*args, **kwargs) 101 rpc.response_queue.put((seq, ret)) 102 except KeyboardInterrupt: 103 if quitting: 104 exit_now = True 105 continue 106 except SystemExit: 107 raise 108 except: 109 type, value, tb = sys.exc_info() 110 try: 111 print_exception() 112 rpc.response_queue.put((seq, None)) 113 except: 114 # Link didn't work, print same exception to __stderr__ 115 traceback.print_exception(type, value, tb, file=sys.__stderr__) 116 exit() 117 else: 118 continue 119 120def manage_socket(address): 121 for i in range(3): 122 time.sleep(i) 123 try: 124 server = MyRPCServer(address, MyHandler) 125 break 126 except socket.error, err: 127 print>>sys.__stderr__,"IDLE Subprocess: socket error: "\ 128 + err.args[1] + ", retrying...." 129 else: 130 print>>sys.__stderr__, "IDLE Subprocess: Connection to "\ 131 "IDLE GUI failed, exiting." 132 show_socket_error(err, address) 133 global exit_now 134 exit_now = True 135 return 136 server.handle_request() # A single request only 137 138def show_socket_error(err, address): 139 import Tkinter 140 import tkMessageBox 141 root = Tkinter.Tk() 142 root.withdraw() 143 if err.args[0] == 61: # connection refused 144 msg = "IDLE's subprocess can't connect to %s:%d. This may be due "\ 145 "to your personal firewall configuration. It is safe to "\ 146 "allow this internal connection because no data is visible on "\ 147 "external ports." % address 148 tkMessageBox.showerror("IDLE Subprocess Error", msg, parent=root) 149 else: 150 tkMessageBox.showerror("IDLE Subprocess Error", 151 "Socket Error: %s" % err.args[1]) 152 root.destroy() 153 154def print_exception(): 155 import linecache 156 linecache.checkcache() 157 flush_stdout() 158 efile = sys.stderr 159 typ, val, tb = excinfo = sys.exc_info() 160 sys.last_type, sys.last_value, sys.last_traceback = excinfo 161 tbe = traceback.extract_tb(tb) 162 print>>efile, '\nTraceback (most recent call last):' 163 exclude = ("run.py", "rpc.py", "threading.py", "Queue.py", 164 "RemoteDebugger.py", "bdb.py") 165 cleanup_traceback(tbe, exclude) 166 traceback.print_list(tbe, file=efile) 167 lines = traceback.format_exception_only(typ, val) 168 for line in lines: 169 print>>efile, line, 170 171def cleanup_traceback(tb, exclude): 172 "Remove excluded traces from beginning/end of tb; get cached lines" 173 orig_tb = tb[:] 174 while tb: 175 for rpcfile in exclude: 176 if tb[0][0].count(rpcfile): 177 break # found an exclude, break for: and delete tb[0] 178 else: 179 break # no excludes, have left RPC code, break while: 180 del tb[0] 181 while tb: 182 for rpcfile in exclude: 183 if tb[-1][0].count(rpcfile): 184 break 185 else: 186 break 187 del tb[-1] 188 if len(tb) == 0: 189 # exception was in IDLE internals, don't prune! 190 tb[:] = orig_tb[:] 191 print>>sys.stderr, "** IDLE Internal Exception: " 192 rpchandler = rpc.objecttable['exec'].rpchandler 193 for i in range(len(tb)): 194 fn, ln, nm, line = tb[i] 195 if nm == '?': 196 nm = "-toplevel-" 197 if not line and fn.startswith("<pyshell#"): 198 line = rpchandler.remotecall('linecache', 'getline', 199 (fn, ln), {}) 200 tb[i] = fn, ln, nm, line 201 202def flush_stdout(): 203 try: 204 if sys.stdout.softspace: 205 sys.stdout.softspace = 0 206 sys.stdout.write("\n") 207 except (AttributeError, EOFError): 208 pass 209 210def exit(): 211 """Exit subprocess, possibly after first deleting sys.exitfunc 212 213 If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any 214 sys.exitfunc will be removed before exiting. (VPython support) 215 216 """ 217 if no_exitfunc: 218 try: 219 del sys.exitfunc 220 except AttributeError: 221 pass 222 sys.exit(0) 223 224class MyRPCServer(rpc.RPCServer): 225 226 def handle_error(self, request, client_address): 227 """Override RPCServer method for IDLE 228 229 Interrupt the MainThread and exit server if link is dropped. 230 231 """ 232 global quitting 233 try: 234 raise 235 except SystemExit: 236 raise 237 except EOFError: 238 global exit_now 239 exit_now = True 240 thread.interrupt_main() 241 except: 242 erf = sys.__stderr__ 243 print>>erf, '\n' + '-'*40 244 print>>erf, 'Unhandled server exception!' 245 print>>erf, 'Thread: %s' % threading.currentThread().getName() 246 print>>erf, 'Client Address: ', client_address 247 print>>erf, 'Request: ', repr(request) 248 traceback.print_exc(file=erf) 249 print>>erf, '\n*** Unrecoverable, server exiting!' 250 print>>erf, '-'*40 251 quitting = True 252 thread.interrupt_main() 253 254class MyHandler(rpc.RPCHandler): 255 256 def handle(self): 257 """Override base method""" 258 executive = Executive(self) 259 self.register("exec", executive) 260 self.console = self.get_remote_proxy("console") 261 sys.stdin = PyShell.PseudoInputFile(self.console, "stdin", 262 IOBinding.encoding) 263 sys.stdout = PyShell.PseudoOutputFile(self.console, "stdout", 264 IOBinding.encoding) 265 sys.stderr = PyShell.PseudoOutputFile(self.console, "stderr", 266 IOBinding.encoding) 267 268 # Keep a reference to stdin so that it won't try to exit IDLE if 269 # sys.stdin gets changed from within IDLE's shell. See issue17838. 270 self._keep_stdin = sys.stdin 271 272 self.interp = self.get_remote_proxy("interp") 273 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05) 274 275 def exithook(self): 276 "override SocketIO method - wait for MainThread to shut us down" 277 time.sleep(10) 278 279 def EOFhook(self): 280 "Override SocketIO method - terminate wait on callback and exit thread" 281 global quitting 282 quitting = True 283 thread.interrupt_main() 284 285 def decode_interrupthook(self): 286 "interrupt awakened thread" 287 global quitting 288 quitting = True 289 thread.interrupt_main() 290 291 292class Executive(object): 293 294 def __init__(self, rpchandler): 295 self.rpchandler = rpchandler 296 self.locals = __main__.__dict__ 297 self.calltip = CallTips.CallTips() 298 self.autocomplete = AutoComplete.AutoComplete() 299 300 def runcode(self, code): 301 global interruptable 302 try: 303 self.usr_exc_info = None 304 interruptable = True 305 try: 306 exec code in self.locals 307 finally: 308 interruptable = False 309 except SystemExit: 310 # Scripts that raise SystemExit should just 311 # return to the interactive prompt 312 pass 313 except: 314 self.usr_exc_info = sys.exc_info() 315 if quitting: 316 exit() 317 print_exception() 318 jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>") 319 if jit: 320 self.rpchandler.interp.open_remote_stack_viewer() 321 else: 322 flush_stdout() 323 324 def interrupt_the_server(self): 325 if interruptable: 326 thread.interrupt_main() 327 328 def start_the_debugger(self, gui_adap_oid): 329 return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid) 330 331 def stop_the_debugger(self, idb_adap_oid): 332 "Unregister the Idb Adapter. Link objects and Idb then subject to GC" 333 self.rpchandler.unregister(idb_adap_oid) 334 335 def get_the_calltip(self, name): 336 return self.calltip.fetch_tip(name) 337 338 def get_the_completion_list(self, what, mode): 339 return self.autocomplete.fetch_completions(what, mode) 340 341 def stackviewer(self, flist_oid=None): 342 if self.usr_exc_info: 343 typ, val, tb = self.usr_exc_info 344 else: 345 return None 346 flist = None 347 if flist_oid is not None: 348 flist = self.rpchandler.get_remote_proxy(flist_oid) 349 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]: 350 tb = tb.tb_next 351 sys.last_type = typ 352 sys.last_value = val 353 item = StackViewer.StackTreeItem(flist, tb) 354 return RemoteObjectBrowser.remote_object_tree_item(item) 355