blob: e05be8f28ec25d055750250b3b4ef319fc133028 [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. Kaiser9ec454e2003-05-12 02:33:47 +000065 except:
66 print_exception()
67 rpc.response_queue.put((seq, None))
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000068 continue
69
70def manage_socket(address):
Kurt B. Kaiser8dcdb772002-08-05 03:52:10 +000071 for i in range(6):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000072 time.sleep(i)
73 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000074 server = MyRPCServer(address, MyHandler)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000075 break
76 except socket.error, err:
Kurt B. Kaiser8dcdb772002-08-05 03:52:10 +000077 if i < 3:
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000078 print>>sys.__stderr__, ".. ",
79 else:
80 print>>sys.__stderr__,"\nPython subprocess socket error: "\
81 + err[1] + ", retrying...."
82 else:
83 print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000084 global exit_requested
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000085 exit_requested = True
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000086 return
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000087 server.handle_request() # A single request only
88
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +000089def print_exception():
90 flush_stdout()
91 efile = sys.stderr
92 typ, val, tb = info = sys.exc_info()
93 tbe = traceback.extract_tb(tb)
94 print >>efile, 'Traceback (most recent call last):'
95 exclude = ("run.py", "rpc.py", "threading.py", "Queue.py",
96 "RemoteDebugger.py", "bdb.py")
97 cleanup_traceback(tbe, exclude)
98 traceback.print_list(tbe, file=efile)
99 lines = traceback.format_exception_only(typ, val)
100 for line in lines:
101 print>>efile, line,
102
103def cleanup_traceback(tb, exclude):
104 "Remove excluded traces from beginning/end of tb; get cached lines"
105 orig_tb = tb[:]
106 while tb:
107 for rpcfile in exclude:
108 if tb[0][0].count(rpcfile):
109 break # found an exclude, break for: and delete tb[0]
110 else:
111 break # no excludes, have left RPC code, break while:
112 del tb[0]
113 while tb:
114 for rpcfile in exclude:
115 if tb[-1][0].count(rpcfile):
116 break
117 else:
118 break
119 del tb[-1]
120 if len(tb) == 0:
121 # exception was in IDLE internals, don't prune!
122 tb[:] = orig_tb[:]
123 print>>sys.stderr, "** IDLE Internal Exception: "
124 rpchandler = rpc.objecttable['exec'].rpchandler
125 for i in range(len(tb)):
126 fn, ln, nm, line = tb[i]
127 if nm == '?':
128 nm = "-toplevel-"
129 if not line and fn.startswith("<pyshell#"):
130 line = rpchandler.remotecall('linecache', 'getline',
131 (fn, ln), {})
132 tb[i] = fn, ln, nm, line
133
134def flush_stdout():
135 try:
136 if sys.stdout.softspace:
137 sys.stdout.softspace = 0
138 sys.stdout.write("\n")
139 except (AttributeError, EOFError):
140 pass
141
Chui Tey5d2af632002-05-26 13:36:41 +0000142
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000143class MyRPCServer(rpc.RPCServer):
144
145 def handle_error(self, request, client_address):
146 """Override RPCServer method for IDLE
147
148 Interrupt the MainThread and exit server if link is dropped.
149
150 """
151 try:
152 raise
153 except SystemExit:
154 raise
155 except EOFError:
156 global exit_requested
157 exit_requested = True
158 interrupt.interrupt_main()
159 except:
160 erf = sys.__stderr__
161 print>>erf, '\n' + '-'*40
162 print>>erf, 'Unhandled server exception!'
163 print>>erf, 'Thread: %s' % threading.currentThread().getName()
164 print>>erf, 'Client Address: ', client_address
165 print>>erf, 'Request: ', repr(request)
166 traceback.print_exc(file=erf)
167 print>>erf, '\n*** Unrecoverable, server exiting!'
168 print>>erf, '-'*40
169 os._exit(0)
170
171
Chui Tey5d2af632002-05-26 13:36:41 +0000172class MyHandler(rpc.RPCHandler):
173
174 def handle(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000175 """Override base method"""
Chui Tey5d2af632002-05-26 13:36:41 +0000176 executive = Executive(self)
177 self.register("exec", executive)
178 sys.stdin = self.get_remote_proxy("stdin")
179 sys.stdout = self.get_remote_proxy("stdout")
180 sys.stderr = self.get_remote_proxy("stderr")
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000181 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
182
183 def exithook(self):
184 "override SocketIO method - wait for MainThread to shut us down"
185 while 1: pass
186
187 def EOFhook(self):
188 "Override SocketIO method - terminate wait on callback and exit thread"
189 global exit_requested
190 exit_requested = True
191
192 def decode_interrupthook(self):
193 "interrupt awakened thread"
194 interrupt.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000195
Chui Tey5d2af632002-05-26 13:36:41 +0000196
197class Executive:
198
199 def __init__(self, rpchandler):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000200 self.rpchandler = rpchandler
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000201 self.locals = __main__.__dict__
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000202 self.calltip = CallTips.CallTips()
Chui Tey5d2af632002-05-26 13:36:41 +0000203
204 def runcode(self, code):
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000205 try:
206 exec code in self.locals
207 except:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000208 try:
209 if exit_requested:
210 os._exit(0)
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000211 print_exception()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000212 except:
213 sys.stderr = sys.__stderr__
214 raise
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000215 else:
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000216 flush_stdout()
Chui Tey5d2af632002-05-26 13:36:41 +0000217
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000218 def interrupt_the_server(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000219 interrupt.interrupt_main()
220
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000221 def start_the_debugger(self, gui_adap_oid):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000222 return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
223
224 def stop_the_debugger(self, idb_adap_oid):
225 "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
226 self.rpchandler.unregister(idb_adap_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000227
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000228 def get_the_calltip(self, name):
229 return self.calltip.fetch_tip(name)
230
Chui Tey5d2af632002-05-26 13:36:41 +0000231 def stackviewer(self, flist_oid=None):
232 if not hasattr(sys, "last_traceback"):
233 return None
234 flist = None
235 if flist_oid is not None:
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000236 flist = self.rpchandler.get_remote_proxy(flist_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000237 tb = sys.last_traceback
238 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
239 tb = tb.tb_next
240 item = StackViewer.StackTreeItem(flist, tb)
241 return RemoteObjectBrowser.remote_object_tree_item(item)