blob: 96da4593b47b3e803de9a28010abe6f58cc2344a [file] [log] [blame]
Chui Tey5d2af632002-05-26 13:36:41 +00001import sys
Kurt B. Kaisera00050f2003-05-08 20:26:55 +00002import os
Kurt B. Kaiserb4179362002-07-26 00:06:42 +00003import time
4import socket
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +00005import traceback
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +00006import thread
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +00007import threading
8import Queue
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +00009
10import CallTips
11import RemoteDebugger
12import RemoteObjectBrowser
13import StackViewer
Chui Tey5d2af632002-05-26 13:36:41 +000014import rpc
15
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000016import __main__
17
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +000018LOCALHOST = '127.0.0.1'
19
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000020# Thread shared globals: Establish a queue between a subthread (which handles
21# the socket) and the main thread (which runs user code), plus global
22# completion and exit flags:
23
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000024exit_now = False
25quitting = False
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000026
Kurt B. Kaiser62df0442003-05-28 01:47:46 +000027def main(del_exitfunc=False):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000028 """Start the Python execution server in a subprocess
29
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000030 In the Python subprocess, RPCServer is instantiated with handlerclass
31 MyHandler, which inherits register/unregister methods from RPCHandler via
32 the mix-in class SocketIO.
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000033
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000034 When the RPCServer 'server' is instantiated, the TCPServer initialization
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000035 creates an instance of run.MyHandler and calls its handle() method.
36 handle() instantiates a run.Executive object, passing it a reference to the
37 MyHandler object. That reference is saved as attribute rpchandler of the
38 Executive instance. The Executive methods have access to the reference and
39 can pass it on to entities that they command
40 (e.g. RemoteDebugger.Debugger.start_debugger()). The latter, in turn, can
41 call MyHandler(SocketIO) register/unregister methods via the reference to
42 register and unregister themselves.
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000043
44 """
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000045 global exit_now
46 global quitting
Kurt B. Kaiser62df0442003-05-28 01:47:46 +000047 global no_exitfunc
48 no_exitfunc = del_exitfunc
Chui Tey5d2af632002-05-26 13:36:41 +000049 port = 8833
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +000050 #time.sleep(15) # test subprocess not responding
Chui Tey5d2af632002-05-26 13:36:41 +000051 if sys.argv[1:]:
52 port = int(sys.argv[1])
53 sys.argv[:] = [""]
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000054 sockthread = threading.Thread(target=manage_socket,
55 name='SockThread',
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +000056 args=((LOCALHOST, port),))
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000057 sockthread.setDaemon(True)
58 sockthread.start()
59 while 1:
60 try:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000061 if exit_now:
62 try:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +000063 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000064 except KeyboardInterrupt:
65 # exiting but got an extra KBI? Try again!
66 continue
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000067 try:
68 seq, request = rpc.request_queue.get(0)
69 except Queue.Empty:
70 time.sleep(0.05)
71 continue
72 method, args, kwargs = request
73 ret = method(*args, **kwargs)
74 rpc.response_queue.put((seq, ret))
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +000075 except KeyboardInterrupt:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000076 if quitting:
77 exit_now = True
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +000078 continue
Kurt B. Kaisera2792be2003-05-17 21:04:10 +000079 except SystemExit:
80 raise
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +000081 except:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000082 type, value, tb = sys.exc_info()
Kurt B. Kaisera2792be2003-05-17 21:04:10 +000083 try:
84 print_exception()
85 rpc.response_queue.put((seq, None))
86 except:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000087 # Link didn't work, print same exception to __stderr__
88 traceback.print_exception(type, value, tb, file=sys.__stderr__)
Kurt B. Kaiser62df0442003-05-28 01:47:46 +000089 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000090 else:
91 continue
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000092
93def manage_socket(address):
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +000094 for i in range(3):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000095 time.sleep(i)
96 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000097 server = MyRPCServer(address, MyHandler)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000098 break
99 except socket.error, err:
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000100 print>>sys.__stderr__,"IDLE Subprocess: socket error: "\
101 + err[1] + ", retrying...."
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000102 else:
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000103 print>>sys.__stderr__, "IDLE Subprocess: Connection to "\
104 "IDLE GUI failed, exiting."
105 show_socket_error(err, address)
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000106 global exit_now
107 exit_now = True
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000108 return
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000109 server.handle_request() # A single request only
110
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000111def show_socket_error(err, address):
112 import Tkinter
113 import tkMessageBox
114 root = Tkinter.Tk()
115 root.withdraw()
116 if err[0] == 61: # connection refused
117 msg = "IDLE's subprocess can't connect to %s:%d. This may be due "\
118 "to your personal firewall configuration. It is safe to "\
119 "allow this internal connection because no data is visible on "\
120 "external ports." % address
121 tkMessageBox.showerror("IDLE Subprocess Error", msg, parent=root)
122 else:
123 tkMessageBox.showerror("IDLE Subprocess Error", "Socket Error: %s" % err[1])
124 root.destroy()
125
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000126def print_exception():
Kurt B. Kaisere9802a32004-01-02 04:04:04 +0000127 import linecache
128 linecache.checkcache()
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000129 flush_stdout()
130 efile = sys.stderr
Kurt B. Kaiser924f6162003-11-19 04:52:32 +0000131 typ, val, tb = excinfo = sys.exc_info()
132 sys.last_type, sys.last_value, sys.last_traceback = excinfo
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000133 tbe = traceback.extract_tb(tb)
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000134 print>>efile, '\nTraceback (most recent call last):'
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000135 exclude = ("run.py", "rpc.py", "threading.py", "Queue.py",
136 "RemoteDebugger.py", "bdb.py")
137 cleanup_traceback(tbe, exclude)
138 traceback.print_list(tbe, file=efile)
139 lines = traceback.format_exception_only(typ, val)
140 for line in lines:
141 print>>efile, line,
142
143def cleanup_traceback(tb, exclude):
144 "Remove excluded traces from beginning/end of tb; get cached lines"
145 orig_tb = tb[:]
146 while tb:
147 for rpcfile in exclude:
148 if tb[0][0].count(rpcfile):
149 break # found an exclude, break for: and delete tb[0]
150 else:
151 break # no excludes, have left RPC code, break while:
152 del tb[0]
153 while tb:
154 for rpcfile in exclude:
155 if tb[-1][0].count(rpcfile):
156 break
157 else:
158 break
159 del tb[-1]
160 if len(tb) == 0:
161 # exception was in IDLE internals, don't prune!
162 tb[:] = orig_tb[:]
163 print>>sys.stderr, "** IDLE Internal Exception: "
164 rpchandler = rpc.objecttable['exec'].rpchandler
165 for i in range(len(tb)):
166 fn, ln, nm, line = tb[i]
167 if nm == '?':
168 nm = "-toplevel-"
169 if not line and fn.startswith("<pyshell#"):
170 line = rpchandler.remotecall('linecache', 'getline',
171 (fn, ln), {})
172 tb[i] = fn, ln, nm, line
173
174def flush_stdout():
175 try:
176 if sys.stdout.softspace:
177 sys.stdout.softspace = 0
178 sys.stdout.write("\n")
179 except (AttributeError, EOFError):
180 pass
181
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000182def exit():
183 """Exit subprocess, possibly after first deleting sys.exitfunc
184
185 If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any
186 sys.exitfunc will be removed before exiting. (VPython support)
187
188 """
189 if no_exitfunc:
190 del sys.exitfunc
191 sys.exit(0)
Chui Tey5d2af632002-05-26 13:36:41 +0000192
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000193class MyRPCServer(rpc.RPCServer):
194
195 def handle_error(self, request, client_address):
196 """Override RPCServer method for IDLE
197
198 Interrupt the MainThread and exit server if link is dropped.
199
200 """
201 try:
202 raise
203 except SystemExit:
204 raise
205 except EOFError:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000206 global exit_now
207 exit_now = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000208 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000209 except:
210 erf = sys.__stderr__
211 print>>erf, '\n' + '-'*40
212 print>>erf, 'Unhandled server exception!'
213 print>>erf, 'Thread: %s' % threading.currentThread().getName()
214 print>>erf, 'Client Address: ', client_address
215 print>>erf, 'Request: ', repr(request)
216 traceback.print_exc(file=erf)
217 print>>erf, '\n*** Unrecoverable, server exiting!'
218 print>>erf, '-'*40
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000219 exit()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000220
221
Chui Tey5d2af632002-05-26 13:36:41 +0000222class MyHandler(rpc.RPCHandler):
223
224 def handle(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000225 """Override base method"""
Chui Tey5d2af632002-05-26 13:36:41 +0000226 executive = Executive(self)
227 self.register("exec", executive)
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000228 sys.stdin = self.console = self.get_remote_proxy("stdin")
Chui Tey5d2af632002-05-26 13:36:41 +0000229 sys.stdout = self.get_remote_proxy("stdout")
230 sys.stderr = self.get_remote_proxy("stderr")
Martin v. Löwisbcc651a2003-06-22 07:52:56 +0000231 import IOBinding
232 sys.stdin.encoding = sys.stdout.encoding = \
233 sys.stderr.encoding = IOBinding.encoding
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000234 self.interp = self.get_remote_proxy("interp")
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000235 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
236
237 def exithook(self):
238 "override SocketIO method - wait for MainThread to shut us down"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000239 time.sleep(10)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000240
241 def EOFhook(self):
242 "Override SocketIO method - terminate wait on callback and exit thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000243 global quitting
244 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000245 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000246
247 def decode_interrupthook(self):
248 "interrupt awakened thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000249 global quitting
250 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000251 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000252
Chui Tey5d2af632002-05-26 13:36:41 +0000253
254class Executive:
255
256 def __init__(self, rpchandler):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000257 self.rpchandler = rpchandler
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000258 self.locals = __main__.__dict__
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000259 self.calltip = CallTips.CallTips()
Chui Tey5d2af632002-05-26 13:36:41 +0000260
261 def runcode(self, code):
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000262 try:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000263 self.usr_exc_info = None
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000264 exec code in self.locals
265 except:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000266 self.usr_exc_info = sys.exc_info()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000267 if quitting:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000268 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000269 # even print a user code SystemExit exception, continue
270 print_exception()
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000271 jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
272 if jit:
273 self.rpchandler.interp.open_remote_stack_viewer()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000274 else:
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000275 flush_stdout()
Chui Tey5d2af632002-05-26 13:36:41 +0000276
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000277 def interrupt_the_server(self):
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000278 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000279
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000280 def start_the_debugger(self, gui_adap_oid):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000281 return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
282
283 def stop_the_debugger(self, idb_adap_oid):
284 "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
285 self.rpchandler.unregister(idb_adap_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000286
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000287 def get_the_calltip(self, name):
288 return self.calltip.fetch_tip(name)
289
Chui Tey5d2af632002-05-26 13:36:41 +0000290 def stackviewer(self, flist_oid=None):
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000291 if self.usr_exc_info:
292 typ, val, tb = self.usr_exc_info
293 else:
Chui Tey5d2af632002-05-26 13:36:41 +0000294 return None
295 flist = None
296 if flist_oid is not None:
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000297 flist = self.rpchandler.get_remote_proxy(flist_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000298 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
299 tb = tb.tb_next
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000300 sys.last_type = typ
301 sys.last_value = val
Chui Tey5d2af632002-05-26 13:36:41 +0000302 item = StackViewer.StackTreeItem(flist, tb)
303 return RemoteObjectBrowser.remote_object_tree_item(item)