blob: 8b1555d05afef2084e4e71a8f62d275048d9160a [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. 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
50 if sys.argv[1:]:
51 port = int(sys.argv[1])
52 sys.argv[:] = [""]
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000053 sockthread = threading.Thread(target=manage_socket,
54 name='SockThread',
55 args=(('localhost', port),))
56 sockthread.setDaemon(True)
57 sockthread.start()
58 while 1:
59 try:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000060 if exit_now:
61 try:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +000062 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000063 except KeyboardInterrupt:
64 # exiting but got an extra KBI? Try again!
65 continue
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000066 try:
67 seq, request = rpc.request_queue.get(0)
68 except Queue.Empty:
69 time.sleep(0.05)
70 continue
71 method, args, kwargs = request
72 ret = method(*args, **kwargs)
73 rpc.response_queue.put((seq, ret))
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +000074 except KeyboardInterrupt:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000075 if quitting:
76 exit_now = True
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +000077 continue
Kurt B. Kaisera2792be2003-05-17 21:04:10 +000078 except SystemExit:
79 raise
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +000080 except:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000081 type, value, tb = sys.exc_info()
Kurt B. Kaisera2792be2003-05-17 21:04:10 +000082 try:
83 print_exception()
84 rpc.response_queue.put((seq, None))
85 except:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000086 # Link didn't work, print same exception to __stderr__
87 traceback.print_exception(type, value, tb, file=sys.__stderr__)
Kurt B. Kaiser62df0442003-05-28 01:47:46 +000088 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000089 else:
90 continue
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000091
92def manage_socket(address):
Kurt B. Kaiser8dcdb772002-08-05 03:52:10 +000093 for i in range(6):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000094 time.sleep(i)
95 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000096 server = MyRPCServer(address, MyHandler)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000097 break
98 except socket.error, err:
Kurt B. Kaiser8dcdb772002-08-05 03:52:10 +000099 if i < 3:
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000100 print>>sys.__stderr__, ".. ",
101 else:
102 print>>sys.__stderr__,"\nPython subprocess socket error: "\
103 + err[1] + ", retrying...."
104 else:
105 print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
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. Kaiser9ec454e2003-05-12 02:33:47 +0000111def print_exception():
112 flush_stdout()
113 efile = sys.stderr
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000114 typ, val, tb = sys.exc_info()
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000115 tbe = traceback.extract_tb(tb)
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000116 print >>efile, '\nTraceback (most recent call last):'
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000117 exclude = ("run.py", "rpc.py", "threading.py", "Queue.py",
118 "RemoteDebugger.py", "bdb.py")
119 cleanup_traceback(tbe, exclude)
120 traceback.print_list(tbe, file=efile)
121 lines = traceback.format_exception_only(typ, val)
122 for line in lines:
123 print>>efile, line,
124
125def cleanup_traceback(tb, exclude):
126 "Remove excluded traces from beginning/end of tb; get cached lines"
127 orig_tb = tb[:]
128 while tb:
129 for rpcfile in exclude:
130 if tb[0][0].count(rpcfile):
131 break # found an exclude, break for: and delete tb[0]
132 else:
133 break # no excludes, have left RPC code, break while:
134 del tb[0]
135 while tb:
136 for rpcfile in exclude:
137 if tb[-1][0].count(rpcfile):
138 break
139 else:
140 break
141 del tb[-1]
142 if len(tb) == 0:
143 # exception was in IDLE internals, don't prune!
144 tb[:] = orig_tb[:]
145 print>>sys.stderr, "** IDLE Internal Exception: "
146 rpchandler = rpc.objecttable['exec'].rpchandler
147 for i in range(len(tb)):
148 fn, ln, nm, line = tb[i]
149 if nm == '?':
150 nm = "-toplevel-"
151 if not line and fn.startswith("<pyshell#"):
152 line = rpchandler.remotecall('linecache', 'getline',
153 (fn, ln), {})
154 tb[i] = fn, ln, nm, line
155
156def flush_stdout():
157 try:
158 if sys.stdout.softspace:
159 sys.stdout.softspace = 0
160 sys.stdout.write("\n")
161 except (AttributeError, EOFError):
162 pass
163
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000164def exit():
165 """Exit subprocess, possibly after first deleting sys.exitfunc
166
167 If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any
168 sys.exitfunc will be removed before exiting. (VPython support)
169
170 """
171 if no_exitfunc:
172 del sys.exitfunc
173 sys.exit(0)
Chui Tey5d2af632002-05-26 13:36:41 +0000174
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000175class MyRPCServer(rpc.RPCServer):
176
177 def handle_error(self, request, client_address):
178 """Override RPCServer method for IDLE
179
180 Interrupt the MainThread and exit server if link is dropped.
181
182 """
183 try:
184 raise
185 except SystemExit:
186 raise
187 except EOFError:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000188 global exit_now
189 exit_now = True
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000190 interrupt.interrupt_main()
191 except:
192 erf = sys.__stderr__
193 print>>erf, '\n' + '-'*40
194 print>>erf, 'Unhandled server exception!'
195 print>>erf, 'Thread: %s' % threading.currentThread().getName()
196 print>>erf, 'Client Address: ', client_address
197 print>>erf, 'Request: ', repr(request)
198 traceback.print_exc(file=erf)
199 print>>erf, '\n*** Unrecoverable, server exiting!'
200 print>>erf, '-'*40
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000201 exit()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000202
203
Chui Tey5d2af632002-05-26 13:36:41 +0000204class MyHandler(rpc.RPCHandler):
205
206 def handle(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000207 """Override base method"""
Chui Tey5d2af632002-05-26 13:36:41 +0000208 executive = Executive(self)
209 self.register("exec", executive)
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000210 sys.stdin = self.console = self.get_remote_proxy("stdin")
Chui Tey5d2af632002-05-26 13:36:41 +0000211 sys.stdout = self.get_remote_proxy("stdout")
212 sys.stderr = self.get_remote_proxy("stderr")
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000213 self.interp = self.get_remote_proxy("interp")
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000214 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
215
216 def exithook(self):
217 "override SocketIO method - wait for MainThread to shut us down"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000218 time.sleep(10)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000219
220 def EOFhook(self):
221 "Override SocketIO method - terminate wait on callback and exit thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000222 global quitting
223 quitting = True
224 interrupt.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000225
226 def decode_interrupthook(self):
227 "interrupt awakened thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000228 global quitting
229 quitting = True
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000230 interrupt.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000231
Chui Tey5d2af632002-05-26 13:36:41 +0000232
233class Executive:
234
235 def __init__(self, rpchandler):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000236 self.rpchandler = rpchandler
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000237 self.locals = __main__.__dict__
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000238 self.calltip = CallTips.CallTips()
Chui Tey5d2af632002-05-26 13:36:41 +0000239
240 def runcode(self, code):
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000241 try:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000242 self.usr_exc_info = None
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000243 exec code in self.locals
244 except:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000245 self.usr_exc_info = sys.exc_info()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000246 if quitting:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000247 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000248 # even print a user code SystemExit exception, continue
249 print_exception()
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000250 jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
251 if jit:
252 self.rpchandler.interp.open_remote_stack_viewer()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000253 else:
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000254 flush_stdout()
Chui Tey5d2af632002-05-26 13:36:41 +0000255
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000256 def interrupt_the_server(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000257 interrupt.interrupt_main()
258
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000259 def start_the_debugger(self, gui_adap_oid):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000260 return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
261
262 def stop_the_debugger(self, idb_adap_oid):
263 "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
264 self.rpchandler.unregister(idb_adap_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000265
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000266 def get_the_calltip(self, name):
267 return self.calltip.fetch_tip(name)
268
Chui Tey5d2af632002-05-26 13:36:41 +0000269 def stackviewer(self, flist_oid=None):
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000270 if self.usr_exc_info:
271 typ, val, tb = self.usr_exc_info
272 else:
Chui Tey5d2af632002-05-26 13:36:41 +0000273 return None
274 flist = None
275 if flist_oid is not None:
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000276 flist = self.rpchandler.get_remote_proxy(flist_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000277 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
278 tb = tb.tb_next
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000279 sys.last_type = typ
280 sys.last_value = val
Chui Tey5d2af632002-05-26 13:36:41 +0000281 item = StackViewer.StackTreeItem(flist, tb)
282 return RemoteObjectBrowser.remote_object_tree_item(item)