blob: 5e86e47f9753fb34d4912d5fdb50a9e3898a1d0f [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. Kaiser11c53e22003-03-22 19:40:19 +00006import threading
7import Queue
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +00008
Tony Lowndsf53dec22002-12-20 04:24:43 +00009import boolcheck
10
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000011import CallTips
12import RemoteDebugger
13import RemoteObjectBrowser
14import StackViewer
Chui Tey5d2af632002-05-26 13:36:41 +000015import rpc
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000016import interrupt
Chui Tey5d2af632002-05-26 13:36:41 +000017
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000018import __main__
19
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +000020LOCALHOST = '127.0.0.1'
21
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000022# Thread shared globals: Establish a queue between a subthread (which handles
23# the socket) and the main thread (which runs user code), plus global
24# completion and exit flags:
25
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000026exit_now = False
27quitting = False
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000028
Kurt B. Kaiser62df0442003-05-28 01:47:46 +000029def main(del_exitfunc=False):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000030 """Start the Python execution server in a subprocess
31
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000032 In the Python subprocess, RPCServer is instantiated with handlerclass
33 MyHandler, which inherits register/unregister methods from RPCHandler via
34 the mix-in class SocketIO.
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000035
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000036 When the RPCServer 'server' is instantiated, the TCPServer initialization
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000037 creates an instance of run.MyHandler and calls its handle() method.
38 handle() instantiates a run.Executive object, passing it a reference to the
39 MyHandler object. That reference is saved as attribute rpchandler of the
40 Executive instance. The Executive methods have access to the reference and
41 can pass it on to entities that they command
42 (e.g. RemoteDebugger.Debugger.start_debugger()). The latter, in turn, can
43 call MyHandler(SocketIO) register/unregister methods via the reference to
44 register and unregister themselves.
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000045
46 """
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000047 global exit_now
48 global quitting
Kurt B. Kaiser62df0442003-05-28 01:47:46 +000049 global no_exitfunc
50 no_exitfunc = del_exitfunc
Chui Tey5d2af632002-05-26 13:36:41 +000051 port = 8833
52 if sys.argv[1:]:
53 port = int(sys.argv[1])
54 sys.argv[:] = [""]
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000055 sockthread = threading.Thread(target=manage_socket,
56 name='SockThread',
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +000057 args=((LOCALHOST, port),))
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000058 sockthread.setDaemon(True)
59 sockthread.start()
60 while 1:
61 try:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000062 if exit_now:
63 try:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +000064 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000065 except KeyboardInterrupt:
66 # exiting but got an extra KBI? Try again!
67 continue
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000068 try:
69 seq, request = rpc.request_queue.get(0)
70 except Queue.Empty:
71 time.sleep(0.05)
72 continue
73 method, args, kwargs = request
74 ret = method(*args, **kwargs)
75 rpc.response_queue.put((seq, ret))
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +000076 except KeyboardInterrupt:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000077 if quitting:
78 exit_now = True
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +000079 continue
Kurt B. Kaisera2792be2003-05-17 21:04:10 +000080 except SystemExit:
81 raise
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +000082 except:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000083 type, value, tb = sys.exc_info()
Kurt B. Kaisera2792be2003-05-17 21:04:10 +000084 try:
85 print_exception()
86 rpc.response_queue.put((seq, None))
87 except:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000088 # Link didn't work, print same exception to __stderr__
89 traceback.print_exception(type, value, tb, file=sys.__stderr__)
Kurt B. Kaiser62df0442003-05-28 01:47:46 +000090 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000091 else:
92 continue
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000093
94def manage_socket(address):
Kurt B. Kaiser8dcdb772002-08-05 03:52:10 +000095 for i in range(6):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000096 time.sleep(i)
97 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000098 server = MyRPCServer(address, MyHandler)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000099 break
100 except socket.error, err:
Kurt B. Kaiser8dcdb772002-08-05 03:52:10 +0000101 if i < 3:
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000102 print>>sys.__stderr__, ".. ",
103 else:
104 print>>sys.__stderr__,"\nPython subprocess socket error: "\
105 + err[1] + ", retrying...."
106 else:
107 print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000108 global exit_now
109 exit_now = True
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000110 return
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000111 server.handle_request() # A single request only
112
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000113def print_exception():
114 flush_stdout()
115 efile = sys.stderr
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000116 typ, val, tb = sys.exc_info()
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000117 tbe = traceback.extract_tb(tb)
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000118 print >>efile, '\nTraceback (most recent call last):'
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000119 exclude = ("run.py", "rpc.py", "threading.py", "Queue.py",
120 "RemoteDebugger.py", "bdb.py")
121 cleanup_traceback(tbe, exclude)
122 traceback.print_list(tbe, file=efile)
123 lines = traceback.format_exception_only(typ, val)
124 for line in lines:
125 print>>efile, line,
126
127def cleanup_traceback(tb, exclude):
128 "Remove excluded traces from beginning/end of tb; get cached lines"
129 orig_tb = tb[:]
130 while tb:
131 for rpcfile in exclude:
132 if tb[0][0].count(rpcfile):
133 break # found an exclude, break for: and delete tb[0]
134 else:
135 break # no excludes, have left RPC code, break while:
136 del tb[0]
137 while tb:
138 for rpcfile in exclude:
139 if tb[-1][0].count(rpcfile):
140 break
141 else:
142 break
143 del tb[-1]
144 if len(tb) == 0:
145 # exception was in IDLE internals, don't prune!
146 tb[:] = orig_tb[:]
147 print>>sys.stderr, "** IDLE Internal Exception: "
148 rpchandler = rpc.objecttable['exec'].rpchandler
149 for i in range(len(tb)):
150 fn, ln, nm, line = tb[i]
151 if nm == '?':
152 nm = "-toplevel-"
153 if not line and fn.startswith("<pyshell#"):
154 line = rpchandler.remotecall('linecache', 'getline',
155 (fn, ln), {})
156 tb[i] = fn, ln, nm, line
157
158def flush_stdout():
159 try:
160 if sys.stdout.softspace:
161 sys.stdout.softspace = 0
162 sys.stdout.write("\n")
163 except (AttributeError, EOFError):
164 pass
165
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000166def exit():
167 """Exit subprocess, possibly after first deleting sys.exitfunc
168
169 If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any
170 sys.exitfunc will be removed before exiting. (VPython support)
171
172 """
173 if no_exitfunc:
174 del sys.exitfunc
175 sys.exit(0)
Chui Tey5d2af632002-05-26 13:36:41 +0000176
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000177class MyRPCServer(rpc.RPCServer):
178
179 def handle_error(self, request, client_address):
180 """Override RPCServer method for IDLE
181
182 Interrupt the MainThread and exit server if link is dropped.
183
184 """
185 try:
186 raise
187 except SystemExit:
188 raise
189 except EOFError:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000190 global exit_now
191 exit_now = True
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000192 interrupt.interrupt_main()
193 except:
194 erf = sys.__stderr__
195 print>>erf, '\n' + '-'*40
196 print>>erf, 'Unhandled server exception!'
197 print>>erf, 'Thread: %s' % threading.currentThread().getName()
198 print>>erf, 'Client Address: ', client_address
199 print>>erf, 'Request: ', repr(request)
200 traceback.print_exc(file=erf)
201 print>>erf, '\n*** Unrecoverable, server exiting!'
202 print>>erf, '-'*40
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000203 exit()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000204
205
Chui Tey5d2af632002-05-26 13:36:41 +0000206class MyHandler(rpc.RPCHandler):
207
208 def handle(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000209 """Override base method"""
Chui Tey5d2af632002-05-26 13:36:41 +0000210 executive = Executive(self)
211 self.register("exec", executive)
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000212 sys.stdin = self.console = self.get_remote_proxy("stdin")
Chui Tey5d2af632002-05-26 13:36:41 +0000213 sys.stdout = self.get_remote_proxy("stdout")
214 sys.stderr = self.get_remote_proxy("stderr")
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000215 self.interp = self.get_remote_proxy("interp")
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000216 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
217
218 def exithook(self):
219 "override SocketIO method - wait for MainThread to shut us down"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000220 time.sleep(10)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000221
222 def EOFhook(self):
223 "Override SocketIO method - terminate wait on callback and exit thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000224 global quitting
225 quitting = True
226 interrupt.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000227
228 def decode_interrupthook(self):
229 "interrupt awakened thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000230 global quitting
231 quitting = True
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000232 interrupt.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000233
Chui Tey5d2af632002-05-26 13:36:41 +0000234
235class Executive:
236
237 def __init__(self, rpchandler):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000238 self.rpchandler = rpchandler
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000239 self.locals = __main__.__dict__
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000240 self.calltip = CallTips.CallTips()
Chui Tey5d2af632002-05-26 13:36:41 +0000241
242 def runcode(self, code):
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000243 try:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000244 self.usr_exc_info = None
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000245 exec code in self.locals
246 except:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000247 self.usr_exc_info = sys.exc_info()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000248 if quitting:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000249 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000250 # even print a user code SystemExit exception, continue
251 print_exception()
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000252 jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
253 if jit:
254 self.rpchandler.interp.open_remote_stack_viewer()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000255 else:
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000256 flush_stdout()
Chui Tey5d2af632002-05-26 13:36:41 +0000257
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000258 def interrupt_the_server(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000259 interrupt.interrupt_main()
260
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000261 def start_the_debugger(self, gui_adap_oid):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000262 return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
263
264 def stop_the_debugger(self, idb_adap_oid):
265 "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
266 self.rpchandler.unregister(idb_adap_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000267
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000268 def get_the_calltip(self, name):
269 return self.calltip.fetch_tip(name)
270
Chui Tey5d2af632002-05-26 13:36:41 +0000271 def stackviewer(self, flist_oid=None):
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000272 if self.usr_exc_info:
273 typ, val, tb = self.usr_exc_info
274 else:
Chui Tey5d2af632002-05-26 13:36:41 +0000275 return None
276 flist = None
277 if flist_oid is not None:
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000278 flist = self.rpchandler.get_remote_proxy(flist_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000279 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
280 tb = tb.tb_next
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000281 sys.last_type = typ
282 sys.last_value = val
Chui Tey5d2af632002-05-26 13:36:41 +0000283 item = StackViewer.StackTreeItem(flist, tb)
284 return RemoteObjectBrowser.remote_object_tree_item(item)