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