blob: 8cfa808b14a34362a119069dc69c865c1bf1ce4b [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():
Kurt B. Kaisere9802a32004-01-02 04:04:04 +0000112 import linecache
113 linecache.checkcache()
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000114 flush_stdout()
115 efile = sys.stderr
Kurt B. Kaiser924f6162003-11-19 04:52:32 +0000116 typ, val, tb = excinfo = sys.exc_info()
117 sys.last_type, sys.last_value, sys.last_traceback = excinfo
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000118 tbe = traceback.extract_tb(tb)
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000119 print >>efile, '\nTraceback (most recent call last):'
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000120 exclude = ("run.py", "rpc.py", "threading.py", "Queue.py",
121 "RemoteDebugger.py", "bdb.py")
122 cleanup_traceback(tbe, exclude)
123 traceback.print_list(tbe, file=efile)
124 lines = traceback.format_exception_only(typ, val)
125 for line in lines:
126 print>>efile, line,
127
128def cleanup_traceback(tb, exclude):
129 "Remove excluded traces from beginning/end of tb; get cached lines"
130 orig_tb = tb[:]
131 while tb:
132 for rpcfile in exclude:
133 if tb[0][0].count(rpcfile):
134 break # found an exclude, break for: and delete tb[0]
135 else:
136 break # no excludes, have left RPC code, break while:
137 del tb[0]
138 while tb:
139 for rpcfile in exclude:
140 if tb[-1][0].count(rpcfile):
141 break
142 else:
143 break
144 del tb[-1]
145 if len(tb) == 0:
146 # exception was in IDLE internals, don't prune!
147 tb[:] = orig_tb[:]
148 print>>sys.stderr, "** IDLE Internal Exception: "
149 rpchandler = rpc.objecttable['exec'].rpchandler
150 for i in range(len(tb)):
151 fn, ln, nm, line = tb[i]
152 if nm == '?':
153 nm = "-toplevel-"
154 if not line and fn.startswith("<pyshell#"):
155 line = rpchandler.remotecall('linecache', 'getline',
156 (fn, ln), {})
157 tb[i] = fn, ln, nm, line
158
159def flush_stdout():
160 try:
161 if sys.stdout.softspace:
162 sys.stdout.softspace = 0
163 sys.stdout.write("\n")
164 except (AttributeError, EOFError):
165 pass
166
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000167def exit():
168 """Exit subprocess, possibly after first deleting sys.exitfunc
169
170 If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any
171 sys.exitfunc will be removed before exiting. (VPython support)
172
173 """
174 if no_exitfunc:
175 del sys.exitfunc
176 sys.exit(0)
Chui Tey5d2af632002-05-26 13:36:41 +0000177
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000178class MyRPCServer(rpc.RPCServer):
179
180 def handle_error(self, request, client_address):
181 """Override RPCServer method for IDLE
182
183 Interrupt the MainThread and exit server if link is dropped.
184
185 """
186 try:
187 raise
188 except SystemExit:
189 raise
190 except EOFError:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000191 global exit_now
192 exit_now = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000193 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000194 except:
195 erf = sys.__stderr__
196 print>>erf, '\n' + '-'*40
197 print>>erf, 'Unhandled server exception!'
198 print>>erf, 'Thread: %s' % threading.currentThread().getName()
199 print>>erf, 'Client Address: ', client_address
200 print>>erf, 'Request: ', repr(request)
201 traceback.print_exc(file=erf)
202 print>>erf, '\n*** Unrecoverable, server exiting!'
203 print>>erf, '-'*40
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000204 exit()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000205
206
Chui Tey5d2af632002-05-26 13:36:41 +0000207class MyHandler(rpc.RPCHandler):
208
209 def handle(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000210 """Override base method"""
Chui Tey5d2af632002-05-26 13:36:41 +0000211 executive = Executive(self)
212 self.register("exec", executive)
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000213 sys.stdin = self.console = self.get_remote_proxy("stdin")
Chui Tey5d2af632002-05-26 13:36:41 +0000214 sys.stdout = self.get_remote_proxy("stdout")
215 sys.stderr = self.get_remote_proxy("stderr")
Martin v. Löwisbcc651a2003-06-22 07:52:56 +0000216 import IOBinding
217 sys.stdin.encoding = sys.stdout.encoding = \
218 sys.stderr.encoding = IOBinding.encoding
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000219 self.interp = self.get_remote_proxy("interp")
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000220 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
221
222 def exithook(self):
223 "override SocketIO method - wait for MainThread to shut us down"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000224 time.sleep(10)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000225
226 def EOFhook(self):
227 "Override SocketIO method - terminate wait on callback and exit thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000228 global quitting
229 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000230 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000231
232 def decode_interrupthook(self):
233 "interrupt awakened thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000234 global quitting
235 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000236 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000237
Chui Tey5d2af632002-05-26 13:36:41 +0000238
239class Executive:
240
241 def __init__(self, rpchandler):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000242 self.rpchandler = rpchandler
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000243 self.locals = __main__.__dict__
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000244 self.calltip = CallTips.CallTips()
Chui Tey5d2af632002-05-26 13:36:41 +0000245
246 def runcode(self, code):
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000247 try:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000248 self.usr_exc_info = None
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000249 exec code in self.locals
250 except:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000251 self.usr_exc_info = sys.exc_info()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000252 if quitting:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000253 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000254 # even print a user code SystemExit exception, continue
255 print_exception()
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000256 jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
257 if jit:
258 self.rpchandler.interp.open_remote_stack_viewer()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000259 else:
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000260 flush_stdout()
Chui Tey5d2af632002-05-26 13:36:41 +0000261
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000262 def interrupt_the_server(self):
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000263 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000264
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000265 def start_the_debugger(self, gui_adap_oid):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000266 return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
267
268 def stop_the_debugger(self, idb_adap_oid):
269 "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
270 self.rpchandler.unregister(idb_adap_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000271
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000272 def get_the_calltip(self, name):
273 return self.calltip.fetch_tip(name)
274
Chui Tey5d2af632002-05-26 13:36:41 +0000275 def stackviewer(self, flist_oid=None):
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000276 if self.usr_exc_info:
277 typ, val, tb = self.usr_exc_info
278 else:
Chui Tey5d2af632002-05-26 13:36:41 +0000279 return None
280 flist = None
281 if flist_oid is not None:
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000282 flist = self.rpchandler.get_remote_proxy(flist_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000283 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
284 tb = tb.tb_next
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000285 sys.last_type = typ
286 sys.last_value = val
Chui Tey5d2af632002-05-26 13:36:41 +0000287 item = StackViewer.StackTreeItem(flist, tb)
288 return RemoteObjectBrowser.remote_object_tree_item(item)