blob: 4e6345c8d69c263999d59ae9006d0bc3d2d5995b [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
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',
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +000055 args=((LOCALHOST, port),))
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000056 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. Kaiser93e8e542003-06-13 22:03:43 +0000190 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000191 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")
Martin v. Löwisbcc651a2003-06-22 07:52:56 +0000213 import IOBinding
214 sys.stdin.encoding = sys.stdout.encoding = \
215 sys.stderr.encoding = IOBinding.encoding
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000216 self.interp = self.get_remote_proxy("interp")
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000217 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
218
219 def exithook(self):
220 "override SocketIO method - wait for MainThread to shut us down"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000221 time.sleep(10)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000222
223 def EOFhook(self):
224 "Override SocketIO method - terminate wait on callback and exit thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000225 global quitting
226 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000227 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000228
229 def decode_interrupthook(self):
230 "interrupt awakened thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000231 global quitting
232 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000233 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000234
Chui Tey5d2af632002-05-26 13:36:41 +0000235
236class Executive:
237
238 def __init__(self, rpchandler):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000239 self.rpchandler = rpchandler
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000240 self.locals = __main__.__dict__
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000241 self.calltip = CallTips.CallTips()
Chui Tey5d2af632002-05-26 13:36:41 +0000242
243 def runcode(self, code):
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000244 try:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000245 self.usr_exc_info = None
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000246 exec code in self.locals
247 except:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000248 self.usr_exc_info = sys.exc_info()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000249 if quitting:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000250 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000251 # even print a user code SystemExit exception, continue
252 print_exception()
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000253 jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
254 if jit:
255 self.rpchandler.interp.open_remote_stack_viewer()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000256 else:
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000257 flush_stdout()
Chui Tey5d2af632002-05-26 13:36:41 +0000258
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000259 def interrupt_the_server(self):
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000260 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000261
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000262 def start_the_debugger(self, gui_adap_oid):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000263 return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
264
265 def stop_the_debugger(self, idb_adap_oid):
266 "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
267 self.rpchandler.unregister(idb_adap_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000268
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000269 def get_the_calltip(self, name):
270 return self.calltip.fetch_tip(name)
271
Chui Tey5d2af632002-05-26 13:36:41 +0000272 def stackviewer(self, flist_oid=None):
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000273 if self.usr_exc_info:
274 typ, val, tb = self.usr_exc_info
275 else:
Chui Tey5d2af632002-05-26 13:36:41 +0000276 return None
277 flist = None
278 if flist_oid is not None:
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000279 flist = self.rpchandler.get_remote_proxy(flist_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000280 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
281 tb = tb.tb_next
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000282 sys.last_type = typ
283 sys.last_value = val
Chui Tey5d2af632002-05-26 13:36:41 +0000284 item = StackViewer.StackTreeItem(flist, tb)
285 return RemoteObjectBrowser.remote_object_tree_item(item)