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