blob: 497cbbd622be457311293f05c27483f423b6b2dc [file] [log] [blame]
Guido van Rossum57cd21f2003-04-29 10:23:27 +00001import sys
2import time
3import socket
4import traceback
5import threading
6import Queue
7
8import boolcheck
9
10import CallTips
11import RemoteDebugger
12import RemoteObjectBrowser
13import StackViewer
14import rpc
15import interrupt
16
17import __main__
18
19# Thread shared globals: Establish a queue between a subthread (which handles
20# the socket) and the main thread (which runs user code), plus global
21# completion and exit flags:
22
23server = None # RPCServer instance
24queue = Queue.Queue(0)
25execution_finished = False
26exit_requested = False
27
28
29def main():
30 """Start the Python execution server in a subprocess
31
32 In the Python subprocess, RPCServer is instantiated with handlerclass
33 MyHandler, which inherits register/unregister methods from RPCHandler via
34 the mix-in class SocketIO.
35
36 When the RPCServer 'server' is instantiated, the TCPServer initialization
37 creates an instance of run.MyHandler and calls its handle() method.
38 handle() instantiates a run.Executive object, passing it a reference to the
39 MyHandler object. That reference is saved as attribute rpchandler of the
40 Executive instance. The Executive methods have access to the reference and
41 can pass it on to entities that they command
42 (e.g. RemoteDebugger.Debugger.start_debugger()). The latter, in turn, can
43 call MyHandler(SocketIO) register/unregister methods via the reference to
44 register and unregister themselves.
45
46 """
47 global queue, execution_finished, exit_requested
48
49 port = 8833
50 if sys.argv[1:]:
51 port = int(sys.argv[1])
52 sys.argv[:] = [""]
53 sockthread = threading.Thread(target=manage_socket,
54 name='SockThread',
55 args=(('localhost', port),))
56 sockthread.setDaemon(True)
57 sockthread.start()
58 while 1:
59 try:
60 if exit_requested:
61 sys.exit()
62 # XXX KBK 22Mar03 eventually check queue here!
63 pass
64 time.sleep(0.05)
65 except KeyboardInterrupt:
66 ##execution_finished = True
67 continue
68
69def manage_socket(address):
70 global server, exit_requested
71
72 for i in range(6):
73 time.sleep(i)
74 try:
75 server = rpc.RPCServer(address, MyHandler)
76 break
77 except socket.error, err:
78 if i < 3:
79 print>>sys.__stderr__, ".. ",
80 else:
81 print>>sys.__stderr__,"\nPython subprocess socket error: "\
82 + err[1] + ", retrying...."
83 else:
84 print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
85 exit_requested = True
86 server.handle_request() # A single request only
87
88
89class MyHandler(rpc.RPCHandler):
90
91 def handle(self):
92 """Override base method"""
93 executive = Executive(self)
94 self.register("exec", executive)
95 sys.stdin = self.get_remote_proxy("stdin")
96 sys.stdout = self.get_remote_proxy("stdout")
97 sys.stderr = self.get_remote_proxy("stderr")
98 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.5)
99
100
101class Executive:
102
103 def __init__(self, rpchandler):
104 self.rpchandler = rpchandler
105 self.locals = __main__.__dict__
106 self.calltip = CallTips.CallTips()
107
108 def runcode(self, code):
109 global queue, execution_finished
110
111 execution_finished = False
112 queue.put(code)
113 # dequeue and run in subthread
114 self.runcode_from_queue()
115 while not execution_finished:
116 time.sleep(0.05)
117
118 def runcode_from_queue(self):
119 global queue, execution_finished
120
121 # poll until queue has code object, using threads, just block?
122 while True:
123 try:
124 code = queue.get(0)
125 break
126 except Queue.Empty:
127 time.sleep(0.05)
128 try:
129 exec code in self.locals
130 except:
131 self.flush_stdout()
132 efile = sys.stderr
133 typ, val, tb = info = sys.exc_info()
134 sys.last_type, sys.last_value, sys.last_traceback = info
135 tbe = traceback.extract_tb(tb)
136 print >>efile, 'Traceback (most recent call last):'
137 exclude = ("run.py", "rpc.py", "RemoteDebugger.py", "bdb.py")
138 self.cleanup_traceback(tbe, exclude)
139 traceback.print_list(tbe, file=efile)
140 lines = traceback.format_exception_only(typ, val)
141 for line in lines:
142 print>>efile, line,
143 execution_finished = True
144 else:
145 self.flush_stdout()
146 execution_finished = True
147
148 def flush_stdout(self):
149 try:
150 if sys.stdout.softspace:
151 sys.stdout.softspace = 0
152 sys.stdout.write("\n")
153 except (AttributeError, EOFError):
154 pass
155
156 def cleanup_traceback(self, tb, exclude):
157 "Remove excluded traces from beginning/end of tb; get cached lines"
158 orig_tb = tb[:]
159 while tb:
160 for rpcfile in exclude:
161 if tb[0][0].count(rpcfile):
162 break # found an exclude, break for: and delete tb[0]
163 else:
164 break # no excludes, have left RPC code, break while:
165 del tb[0]
166 while tb:
167 for rpcfile in exclude:
168 if tb[-1][0].count(rpcfile):
169 break
170 else:
171 break
172 del tb[-1]
173 if len(tb) == 0:
174 # exception was in IDLE internals, don't prune!
175 tb[:] = orig_tb[:]
176 print>>sys.stderr, "** IDLE Internal Exception: "
177 for i in range(len(tb)):
178 fn, ln, nm, line = tb[i]
179 if nm == '?':
180 nm = "-toplevel-"
181 if not line and fn.startswith("<pyshell#"):
182 line = self.rpchandler.remotecall('linecache', 'getline',
183 (fn, ln), {})
184 tb[i] = fn, ln, nm, line
185
186 def interrupt_the_server(self):
187 self.rpchandler.interrupted = True
188 ##print>>sys.__stderr__, "** Interrupt main!"
189 interrupt.interrupt_main()
190
191 def shutdown_the_server(self):
192 global exit_requested
193
194 exit_requested = True
195
196 def start_the_debugger(self, gui_adap_oid):
197 return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
198
199 def stop_the_debugger(self, idb_adap_oid):
200 "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
201 self.rpchandler.unregister(idb_adap_oid)
202
203 def get_the_calltip(self, name):
204 return self.calltip.fetch_tip(name)
205
206 def stackviewer(self, flist_oid=None):
207 if not hasattr(sys, "last_traceback"):
208 return None
209 flist = None
210 if flist_oid is not None:
211 flist = self.rpchandler.get_remote_proxy(flist_oid)
212 tb = sys.last_traceback
213 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
214 tb = tb.tb_next
215 item = StackViewer.StackTreeItem(flist, tb)
216 return RemoteObjectBrowser.remote_object_tree_item(item)