blob: 98255c79ce8241f4c7002ae142fad7f3e00363b4 [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. Kaiser11c53e22003-03-22 19:40:19 +000024exit_requested = False
25
Chui Tey5d2af632002-05-26 13:36:41 +000026def main():
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000027 """Start the Python execution server in a subprocess
28
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000029 In the Python subprocess, RPCServer is instantiated with handlerclass
30 MyHandler, which inherits register/unregister methods from RPCHandler via
31 the mix-in class SocketIO.
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000032
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000033 When the RPCServer 'server' is instantiated, the TCPServer initialization
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000034 creates an instance of run.MyHandler and calls its handle() method.
35 handle() instantiates a run.Executive object, passing it a reference to the
36 MyHandler object. That reference is saved as attribute rpchandler of the
37 Executive instance. The Executive methods have access to the reference and
38 can pass it on to entities that they command
39 (e.g. RemoteDebugger.Debugger.start_debugger()). The latter, in turn, can
40 call MyHandler(SocketIO) register/unregister methods via the reference to
41 register and unregister themselves.
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000042
43 """
Chui Tey5d2af632002-05-26 13:36:41 +000044 port = 8833
45 if sys.argv[1:]:
46 port = int(sys.argv[1])
47 sys.argv[:] = [""]
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000048 sockthread = threading.Thread(target=manage_socket,
49 name='SockThread',
50 args=(('localhost', port),))
51 sockthread.setDaemon(True)
52 sockthread.start()
53 while 1:
54 try:
55 if exit_requested:
Kurt B. Kaisera2792be2003-05-17 21:04:10 +000056 sys.exit(0)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000057 try:
58 seq, request = rpc.request_queue.get(0)
59 except Queue.Empty:
60 time.sleep(0.05)
61 continue
62 method, args, kwargs = request
63 ret = method(*args, **kwargs)
64 rpc.response_queue.put((seq, ret))
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +000065 except KeyboardInterrupt:
66 continue
Kurt B. Kaisera2792be2003-05-17 21:04:10 +000067 except SystemExit:
68 raise
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +000069 except:
Kurt B. Kaisera2792be2003-05-17 21:04:10 +000070 try:
71 print_exception()
72 rpc.response_queue.put((seq, None))
73 except:
74 traceback.print_exc(file=sys.__stderr__)
75 sys.exit(1.1)
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000076 continue
77
78def manage_socket(address):
Kurt B. Kaiser8dcdb772002-08-05 03:52:10 +000079 for i in range(6):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000080 time.sleep(i)
81 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000082 server = MyRPCServer(address, MyHandler)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000083 break
84 except socket.error, err:
Kurt B. Kaiser8dcdb772002-08-05 03:52:10 +000085 if i < 3:
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000086 print>>sys.__stderr__, ".. ",
87 else:
88 print>>sys.__stderr__,"\nPython subprocess socket error: "\
89 + err[1] + ", retrying...."
90 else:
91 print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000092 global exit_requested
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000093 exit_requested = True
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000094 return
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000095 server.handle_request() # A single request only
96
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +000097def print_exception():
98 flush_stdout()
99 efile = sys.stderr
100 typ, val, tb = info = sys.exc_info()
101 tbe = traceback.extract_tb(tb)
102 print >>efile, 'Traceback (most recent call last):'
103 exclude = ("run.py", "rpc.py", "threading.py", "Queue.py",
104 "RemoteDebugger.py", "bdb.py")
105 cleanup_traceback(tbe, exclude)
106 traceback.print_list(tbe, file=efile)
107 lines = traceback.format_exception_only(typ, val)
108 for line in lines:
109 print>>efile, line,
110
111def cleanup_traceback(tb, exclude):
112 "Remove excluded traces from beginning/end of tb; get cached lines"
113 orig_tb = tb[:]
114 while tb:
115 for rpcfile in exclude:
116 if tb[0][0].count(rpcfile):
117 break # found an exclude, break for: and delete tb[0]
118 else:
119 break # no excludes, have left RPC code, break while:
120 del tb[0]
121 while tb:
122 for rpcfile in exclude:
123 if tb[-1][0].count(rpcfile):
124 break
125 else:
126 break
127 del tb[-1]
128 if len(tb) == 0:
129 # exception was in IDLE internals, don't prune!
130 tb[:] = orig_tb[:]
131 print>>sys.stderr, "** IDLE Internal Exception: "
132 rpchandler = rpc.objecttable['exec'].rpchandler
133 for i in range(len(tb)):
134 fn, ln, nm, line = tb[i]
135 if nm == '?':
136 nm = "-toplevel-"
137 if not line and fn.startswith("<pyshell#"):
138 line = rpchandler.remotecall('linecache', 'getline',
139 (fn, ln), {})
140 tb[i] = fn, ln, nm, line
141
142def flush_stdout():
143 try:
144 if sys.stdout.softspace:
145 sys.stdout.softspace = 0
146 sys.stdout.write("\n")
147 except (AttributeError, EOFError):
148 pass
149
Chui Tey5d2af632002-05-26 13:36:41 +0000150
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000151class MyRPCServer(rpc.RPCServer):
152
153 def handle_error(self, request, client_address):
154 """Override RPCServer method for IDLE
155
156 Interrupt the MainThread and exit server if link is dropped.
157
158 """
159 try:
160 raise
161 except SystemExit:
162 raise
163 except EOFError:
164 global exit_requested
165 exit_requested = True
166 interrupt.interrupt_main()
167 except:
168 erf = sys.__stderr__
169 print>>erf, '\n' + '-'*40
170 print>>erf, 'Unhandled server exception!'
171 print>>erf, 'Thread: %s' % threading.currentThread().getName()
172 print>>erf, 'Client Address: ', client_address
173 print>>erf, 'Request: ', repr(request)
174 traceback.print_exc(file=erf)
175 print>>erf, '\n*** Unrecoverable, server exiting!'
176 print>>erf, '-'*40
177 os._exit(0)
178
179
Chui Tey5d2af632002-05-26 13:36:41 +0000180class MyHandler(rpc.RPCHandler):
181
182 def handle(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000183 """Override base method"""
Chui Tey5d2af632002-05-26 13:36:41 +0000184 executive = Executive(self)
185 self.register("exec", executive)
186 sys.stdin = self.get_remote_proxy("stdin")
187 sys.stdout = self.get_remote_proxy("stdout")
188 sys.stderr = self.get_remote_proxy("stderr")
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000189 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
190
191 def exithook(self):
192 "override SocketIO method - wait for MainThread to shut us down"
193 while 1: pass
194
195 def EOFhook(self):
196 "Override SocketIO method - terminate wait on callback and exit thread"
197 global exit_requested
198 exit_requested = True
199
200 def decode_interrupthook(self):
201 "interrupt awakened thread"
202 interrupt.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000203
Chui Tey5d2af632002-05-26 13:36:41 +0000204
205class Executive:
206
207 def __init__(self, rpchandler):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000208 self.rpchandler = rpchandler
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000209 self.locals = __main__.__dict__
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000210 self.calltip = CallTips.CallTips()
Chui Tey5d2af632002-05-26 13:36:41 +0000211
212 def runcode(self, code):
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000213 try:
214 exec code in self.locals
215 except:
Kurt B. Kaisera2792be2003-05-17 21:04:10 +0000216 if exit_requested:
217 sys.exit(0)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000218 try:
Kurt B. Kaisera2792be2003-05-17 21:04:10 +0000219 # even print a user code SystemExit exception, continue
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000220 print_exception()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000221 except:
Kurt B. Kaisera2792be2003-05-17 21:04:10 +0000222 # link not working?
223 traceback.print_exc(file=sys.__stderr__)
224 sys.exit(1.2)
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000225 else:
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000226 flush_stdout()
Chui Tey5d2af632002-05-26 13:36:41 +0000227
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000228 def interrupt_the_server(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000229 interrupt.interrupt_main()
230
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000231 def start_the_debugger(self, gui_adap_oid):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000232 return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
233
234 def stop_the_debugger(self, idb_adap_oid):
235 "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
236 self.rpchandler.unregister(idb_adap_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000237
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000238 def get_the_calltip(self, name):
239 return self.calltip.fetch_tip(name)
240
Chui Tey5d2af632002-05-26 13:36:41 +0000241 def stackviewer(self, flist_oid=None):
242 if not hasattr(sys, "last_traceback"):
243 return None
244 flist = None
245 if flist_oid is not None:
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000246 flist = self.rpchandler.get_remote_proxy(flist_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000247 tb = sys.last_traceback
248 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
249 tb = tb.tb_next
250 item = StackViewer.StackTreeItem(flist, tb)
251 return RemoteObjectBrowser.remote_object_tree_item(item)