blob: d1fe08f3e880e6cf1ade426a43a9f2c1535447ce [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. Kaiser924f6162003-11-19 04:52:32 +0000114 typ, val, tb = excinfo = sys.exc_info()
115 sys.last_type, sys.last_value, sys.last_traceback = excinfo
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000116 tbe = traceback.extract_tb(tb)
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000117 print >>efile, '\nTraceback (most recent call last):'
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000118 exclude = ("run.py", "rpc.py", "threading.py", "Queue.py",
119 "RemoteDebugger.py", "bdb.py")
120 cleanup_traceback(tbe, exclude)
121 traceback.print_list(tbe, file=efile)
122 lines = traceback.format_exception_only(typ, val)
123 for line in lines:
124 print>>efile, line,
125
126def cleanup_traceback(tb, exclude):
127 "Remove excluded traces from beginning/end of tb; get cached lines"
128 orig_tb = tb[:]
129 while tb:
130 for rpcfile in exclude:
131 if tb[0][0].count(rpcfile):
132 break # found an exclude, break for: and delete tb[0]
133 else:
134 break # no excludes, have left RPC code, break while:
135 del tb[0]
136 while tb:
137 for rpcfile in exclude:
138 if tb[-1][0].count(rpcfile):
139 break
140 else:
141 break
142 del tb[-1]
143 if len(tb) == 0:
144 # exception was in IDLE internals, don't prune!
145 tb[:] = orig_tb[:]
146 print>>sys.stderr, "** IDLE Internal Exception: "
147 rpchandler = rpc.objecttable['exec'].rpchandler
148 for i in range(len(tb)):
149 fn, ln, nm, line = tb[i]
150 if nm == '?':
151 nm = "-toplevel-"
152 if not line and fn.startswith("<pyshell#"):
153 line = rpchandler.remotecall('linecache', 'getline',
154 (fn, ln), {})
155 tb[i] = fn, ln, nm, line
156
157def flush_stdout():
158 try:
159 if sys.stdout.softspace:
160 sys.stdout.softspace = 0
161 sys.stdout.write("\n")
162 except (AttributeError, EOFError):
163 pass
164
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000165def exit():
166 """Exit subprocess, possibly after first deleting sys.exitfunc
167
168 If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any
169 sys.exitfunc will be removed before exiting. (VPython support)
170
171 """
172 if no_exitfunc:
173 del sys.exitfunc
174 sys.exit(0)
Chui Tey5d2af632002-05-26 13:36:41 +0000175
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000176class MyRPCServer(rpc.RPCServer):
177
178 def handle_error(self, request, client_address):
179 """Override RPCServer method for IDLE
180
181 Interrupt the MainThread and exit server if link is dropped.
182
183 """
184 try:
185 raise
186 except SystemExit:
187 raise
188 except EOFError:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000189 global exit_now
190 exit_now = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000191 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000192 except:
193 erf = sys.__stderr__
194 print>>erf, '\n' + '-'*40
195 print>>erf, 'Unhandled server exception!'
196 print>>erf, 'Thread: %s' % threading.currentThread().getName()
197 print>>erf, 'Client Address: ', client_address
198 print>>erf, 'Request: ', repr(request)
199 traceback.print_exc(file=erf)
200 print>>erf, '\n*** Unrecoverable, server exiting!'
201 print>>erf, '-'*40
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000202 exit()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000203
204
Chui Tey5d2af632002-05-26 13:36:41 +0000205class MyHandler(rpc.RPCHandler):
206
207 def handle(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000208 """Override base method"""
Chui Tey5d2af632002-05-26 13:36:41 +0000209 executive = Executive(self)
210 self.register("exec", executive)
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000211 sys.stdin = self.console = self.get_remote_proxy("stdin")
Chui Tey5d2af632002-05-26 13:36:41 +0000212 sys.stdout = self.get_remote_proxy("stdout")
213 sys.stderr = self.get_remote_proxy("stderr")
Martin v. Löwisbcc651a2003-06-22 07:52:56 +0000214 import IOBinding
215 sys.stdin.encoding = sys.stdout.encoding = \
216 sys.stderr.encoding = IOBinding.encoding
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000217 self.interp = self.get_remote_proxy("interp")
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000218 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
219
220 def exithook(self):
221 "override SocketIO method - wait for MainThread to shut us down"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000222 time.sleep(10)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000223
224 def EOFhook(self):
225 "Override SocketIO method - terminate wait on callback and exit thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000226 global quitting
227 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000228 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000229
230 def decode_interrupthook(self):
231 "interrupt awakened thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000232 global quitting
233 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000234 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000235
Chui Tey5d2af632002-05-26 13:36:41 +0000236
237class Executive:
238
239 def __init__(self, rpchandler):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000240 self.rpchandler = rpchandler
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000241 self.locals = __main__.__dict__
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000242 self.calltip = CallTips.CallTips()
Chui Tey5d2af632002-05-26 13:36:41 +0000243
244 def runcode(self, code):
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000245 try:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000246 self.usr_exc_info = None
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000247 exec code in self.locals
248 except:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000249 self.usr_exc_info = sys.exc_info()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000250 if quitting:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000251 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000252 # even print a user code SystemExit exception, continue
253 print_exception()
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000254 jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
255 if jit:
256 self.rpchandler.interp.open_remote_stack_viewer()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000257 else:
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000258 flush_stdout()
Chui Tey5d2af632002-05-26 13:36:41 +0000259
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000260 def interrupt_the_server(self):
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000261 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000262
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000263 def start_the_debugger(self, gui_adap_oid):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000264 return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
265
266 def stop_the_debugger(self, idb_adap_oid):
267 "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
268 self.rpchandler.unregister(idb_adap_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000269
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000270 def get_the_calltip(self, name):
271 return self.calltip.fetch_tip(name)
272
Chui Tey5d2af632002-05-26 13:36:41 +0000273 def stackviewer(self, flist_oid=None):
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000274 if self.usr_exc_info:
275 typ, val, tb = self.usr_exc_info
276 else:
Chui Tey5d2af632002-05-26 13:36:41 +0000277 return None
278 flist = None
279 if flist_oid is not None:
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000280 flist = self.rpchandler.get_remote_proxy(flist_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000281 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
282 tb = tb.tb_next
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000283 sys.last_type = typ
284 sys.last_value = val
Chui Tey5d2af632002-05-26 13:36:41 +0000285 item = StackViewer.StackTreeItem(flist, tb)
286 return RemoteObjectBrowser.remote_object_tree_item(item)