blob: 320525ce8019a7338f8552be7845f5b520abe389 [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. Kaisera00050f2003-05-08 20:26:55 +000056 os._exit(0)
57 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. Kaiser11c53e22003-03-22 19:40:19 +000065 except KeyboardInterrupt:
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000066 continue
67
68def manage_socket(address):
Kurt B. Kaiser8dcdb772002-08-05 03:52:10 +000069 for i in range(6):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000070 time.sleep(i)
71 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000072 server = MyRPCServer(address, MyHandler)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000073 break
74 except socket.error, err:
Kurt B. Kaiser8dcdb772002-08-05 03:52:10 +000075 if i < 3:
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000076 print>>sys.__stderr__, ".. ",
77 else:
78 print>>sys.__stderr__,"\nPython subprocess socket error: "\
79 + err[1] + ", retrying...."
80 else:
81 print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000082 global exit_requested
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000083 exit_requested = True
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000084 return
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000085 server.handle_request() # A single request only
86
Chui Tey5d2af632002-05-26 13:36:41 +000087
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000088class MyRPCServer(rpc.RPCServer):
89
90 def handle_error(self, request, client_address):
91 """Override RPCServer method for IDLE
92
93 Interrupt the MainThread and exit server if link is dropped.
94
95 """
96 try:
97 raise
98 except SystemExit:
99 raise
100 except EOFError:
101 global exit_requested
102 exit_requested = True
103 interrupt.interrupt_main()
104 except:
105 erf = sys.__stderr__
106 print>>erf, '\n' + '-'*40
107 print>>erf, 'Unhandled server exception!'
108 print>>erf, 'Thread: %s' % threading.currentThread().getName()
109 print>>erf, 'Client Address: ', client_address
110 print>>erf, 'Request: ', repr(request)
111 traceback.print_exc(file=erf)
112 print>>erf, '\n*** Unrecoverable, server exiting!'
113 print>>erf, '-'*40
114 os._exit(0)
115
116
Chui Tey5d2af632002-05-26 13:36:41 +0000117class MyHandler(rpc.RPCHandler):
118
119 def handle(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000120 """Override base method"""
Chui Tey5d2af632002-05-26 13:36:41 +0000121 executive = Executive(self)
122 self.register("exec", executive)
123 sys.stdin = self.get_remote_proxy("stdin")
124 sys.stdout = self.get_remote_proxy("stdout")
125 sys.stderr = self.get_remote_proxy("stderr")
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000126 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
127
128 def exithook(self):
129 "override SocketIO method - wait for MainThread to shut us down"
130 while 1: pass
131
132 def EOFhook(self):
133 "Override SocketIO method - terminate wait on callback and exit thread"
134 global exit_requested
135 exit_requested = True
136
137 def decode_interrupthook(self):
138 "interrupt awakened thread"
139 interrupt.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000140
Chui Tey5d2af632002-05-26 13:36:41 +0000141
142class Executive:
143
144 def __init__(self, rpchandler):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000145 self.rpchandler = rpchandler
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000146 self.locals = __main__.__dict__
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000147 self.calltip = CallTips.CallTips()
Chui Tey5d2af632002-05-26 13:36:41 +0000148
149 def runcode(self, code):
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000150 try:
151 exec code in self.locals
152 except:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000153 try:
154 if exit_requested:
155 os._exit(0)
156 self.flush_stdout()
157 efile = sys.stderr
158 typ, val, tb = info = sys.exc_info()
159 sys.last_type, sys.last_value, sys.last_traceback = info
160 tbe = traceback.extract_tb(tb)
161 print >>efile, 'Traceback (most recent call last):'
162 exclude = ("run.py", "rpc.py", "threading.py",
163 "RemoteDebugger.py", "bdb.py")
164 self.cleanup_traceback(tbe, exclude)
165 traceback.print_list(tbe, file=efile)
166 lines = traceback.format_exception_only(typ, val)
167 for line in lines:
168 print>>efile, line,
169 except:
170 sys.stderr = sys.__stderr__
171 raise
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000172 else:
173 self.flush_stdout()
Kurt B. Kaiserb6aff152003-03-03 20:06:48 +0000174
175 def flush_stdout(self):
176 try:
177 if sys.stdout.softspace:
178 sys.stdout.softspace = 0
179 sys.stdout.write("\n")
Kurt B. Kaiser7c221322003-03-11 22:55:56 +0000180 except (AttributeError, EOFError):
Kurt B. Kaiserb6aff152003-03-03 20:06:48 +0000181 pass
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000182
183 def cleanup_traceback(self, tb, exclude):
184 "Remove excluded traces from beginning/end of tb; get cached lines"
185 orig_tb = tb[:]
186 while tb:
187 for rpcfile in exclude:
188 if tb[0][0].count(rpcfile):
189 break # found an exclude, break for: and delete tb[0]
190 else:
191 break # no excludes, have left RPC code, break while:
192 del tb[0]
193 while tb:
194 for rpcfile in exclude:
195 if tb[-1][0].count(rpcfile):
196 break
197 else:
198 break
199 del tb[-1]
200 if len(tb) == 0:
201 # exception was in IDLE internals, don't prune!
202 tb[:] = orig_tb[:]
203 print>>sys.stderr, "** IDLE Internal Exception: "
204 for i in range(len(tb)):
205 fn, ln, nm, line = tb[i]
206 if nm == '?':
207 nm = "-toplevel-"
208 if not line and fn.startswith("<pyshell#"):
209 line = self.rpchandler.remotecall('linecache', 'getline',
210 (fn, ln), {})
211 tb[i] = fn, ln, nm, line
Chui Tey5d2af632002-05-26 13:36:41 +0000212
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000213 def interrupt_the_server(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000214 interrupt.interrupt_main()
215
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000216 def start_the_debugger(self, gui_adap_oid):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000217 return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
218
219 def stop_the_debugger(self, idb_adap_oid):
220 "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
221 self.rpchandler.unregister(idb_adap_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000222
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000223 def get_the_calltip(self, name):
224 return self.calltip.fetch_tip(name)
225
Chui Tey5d2af632002-05-26 13:36:41 +0000226 def stackviewer(self, flist_oid=None):
227 if not hasattr(sys, "last_traceback"):
228 return None
229 flist = None
230 if flist_oid is not None:
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000231 flist = self.rpchandler.get_remote_proxy(flist_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000232 tb = sys.last_traceback
233 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
234 tb = tb.tb_next
235 item = StackViewer.StackTreeItem(flist, tb)
236 return RemoteObjectBrowser.remote_object_tree_item(item)