blob: abe94abc8b9c44e9ba6ea1a25d484b8e209954bb [file] [log] [blame]
Chui Tey5d2af632002-05-26 13:36:41 +00001import sys
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +00002import linecache
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
Georg Brandla6168f92008-05-25 07:20:14 +00008import Queue
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +00009
10import CallTips
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000011import AutoComplete
12
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000013import RemoteDebugger
14import RemoteObjectBrowser
15import StackViewer
Chui Tey5d2af632002-05-26 13:36:41 +000016import rpc
17
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000018import __main__
19
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +000020LOCALHOST = '127.0.0.1'
21
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +000022try:
23 import warnings
24except ImportError:
25 pass
26else:
Benjamin Peterson9dc0a632008-10-15 21:05:55 +000027 def idle_formatwarning_subproc(message, category, filename, lineno,
Benjamin Petersonb76444b2008-10-16 19:40:14 +000028 file=None, line=None):
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +000029 """Format warnings the IDLE way"""
30 s = "\nWarning (from warnings module):\n"
31 s += ' File \"%s\", line %s\n' % (filename, lineno)
Benjamin Petersonb76444b2008-10-16 19:40:14 +000032 line = linecache.getline(filename, lineno).strip() \
33 if line is None else line
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +000034 if line:
35 s += " %s\n" % line
36 s += "%s: %s\n" % (category.__name__, message)
37 return s
38 warnings.formatwarning = idle_formatwarning_subproc
39
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000040# Thread shared globals: Establish a queue between a subthread (which handles
41# the socket) and the main thread (which runs user code), plus global
Kurt B. Kaiserc8f65e62007-10-09 19:31:30 +000042# completion, exit and interruptable (the main thread) flags:
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000043
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000044exit_now = False
45quitting = False
Kurt B. Kaiserc8f65e62007-10-09 19:31:30 +000046interruptable = False
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000047
Kurt B. Kaiser62df0442003-05-28 01:47:46 +000048def main(del_exitfunc=False):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000049 """Start the Python execution server in a subprocess
50
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000051 In the Python subprocess, RPCServer is instantiated with handlerclass
52 MyHandler, which inherits register/unregister methods from RPCHandler via
53 the mix-in class SocketIO.
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000054
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000055 When the RPCServer 'server' is instantiated, the TCPServer initialization
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000056 creates an instance of run.MyHandler and calls its handle() method.
57 handle() instantiates a run.Executive object, passing it a reference to the
58 MyHandler object. That reference is saved as attribute rpchandler of the
59 Executive instance. The Executive methods have access to the reference and
60 can pass it on to entities that they command
61 (e.g. RemoteDebugger.Debugger.start_debugger()). The latter, in turn, can
62 call MyHandler(SocketIO) register/unregister methods via the reference to
63 register and unregister themselves.
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000064
65 """
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000066 global exit_now
67 global quitting
Kurt B. Kaiser62df0442003-05-28 01:47:46 +000068 global no_exitfunc
69 no_exitfunc = del_exitfunc
Chui Tey5d2af632002-05-26 13:36:41 +000070 port = 8833
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +000071 #time.sleep(15) # test subprocess not responding
Chui Tey5d2af632002-05-26 13:36:41 +000072 if sys.argv[1:]:
73 port = int(sys.argv[1])
74 sys.argv[:] = [""]
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000075 sockthread = threading.Thread(target=manage_socket,
76 name='SockThread',
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +000077 args=((LOCALHOST, port),))
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000078 sockthread.setDaemon(True)
79 sockthread.start()
80 while 1:
81 try:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000082 if exit_now:
83 try:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +000084 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000085 except KeyboardInterrupt:
86 # exiting but got an extra KBI? Try again!
87 continue
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000088 try:
Kurt B. Kaiser20345fb2005-05-05 23:29:54 +000089 seq, request = rpc.request_queue.get(block=True, timeout=0.05)
Georg Brandla6168f92008-05-25 07:20:14 +000090 except Queue.Empty:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000091 continue
92 method, args, kwargs = request
93 ret = method(*args, **kwargs)
94 rpc.response_queue.put((seq, ret))
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +000095 except KeyboardInterrupt:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000096 if quitting:
97 exit_now = True
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +000098 continue
Kurt B. Kaisera2792be2003-05-17 21:04:10 +000099 except SystemExit:
100 raise
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000101 except:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000102 type, value, tb = sys.exc_info()
Kurt B. Kaisera2792be2003-05-17 21:04:10 +0000103 try:
104 print_exception()
105 rpc.response_queue.put((seq, None))
106 except:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000107 # Link didn't work, print same exception to __stderr__
108 traceback.print_exception(type, value, tb, file=sys.__stderr__)
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000109 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000110 else:
111 continue
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000112
113def manage_socket(address):
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000114 for i in range(3):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000115 time.sleep(i)
116 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000117 server = MyRPCServer(address, MyHandler)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000118 break
119 except socket.error, err:
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000120 print>>sys.__stderr__,"IDLE Subprocess: socket error: "\
121 + err[1] + ", retrying...."
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000122 else:
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000123 print>>sys.__stderr__, "IDLE Subprocess: Connection to "\
124 "IDLE GUI failed, exiting."
125 show_socket_error(err, address)
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000126 global exit_now
127 exit_now = True
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000128 return
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000129 server.handle_request() # A single request only
130
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000131def show_socket_error(err, address):
Georg Brandl6634bf22008-05-20 07:13:37 +0000132 import Tkinter
133 import tkMessageBox
134 root = Tkinter.Tk()
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000135 root.withdraw()
136 if err[0] == 61: # connection refused
137 msg = "IDLE's subprocess can't connect to %s:%d. This may be due "\
138 "to your personal firewall configuration. It is safe to "\
139 "allow this internal connection because no data is visible on "\
140 "external ports." % address
141 tkMessageBox.showerror("IDLE Subprocess Error", msg, parent=root)
142 else:
143 tkMessageBox.showerror("IDLE Subprocess Error", "Socket Error: %s" % err[1])
144 root.destroy()
145
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000146def print_exception():
Kurt B. Kaisere9802a32004-01-02 04:04:04 +0000147 import linecache
148 linecache.checkcache()
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000149 flush_stdout()
150 efile = sys.stderr
Kurt B. Kaiser924f6162003-11-19 04:52:32 +0000151 typ, val, tb = excinfo = sys.exc_info()
152 sys.last_type, sys.last_value, sys.last_traceback = excinfo
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000153 tbe = traceback.extract_tb(tb)
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000154 print>>efile, '\nTraceback (most recent call last):'
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000155 exclude = ("run.py", "rpc.py", "threading.py", "Queue.py",
156 "RemoteDebugger.py", "bdb.py")
157 cleanup_traceback(tbe, exclude)
158 traceback.print_list(tbe, file=efile)
159 lines = traceback.format_exception_only(typ, val)
160 for line in lines:
161 print>>efile, line,
162
163def cleanup_traceback(tb, exclude):
164 "Remove excluded traces from beginning/end of tb; get cached lines"
165 orig_tb = tb[:]
166 while tb:
167 for rpcfile in exclude:
168 if tb[0][0].count(rpcfile):
169 break # found an exclude, break for: and delete tb[0]
170 else:
171 break # no excludes, have left RPC code, break while:
172 del tb[0]
173 while tb:
174 for rpcfile in exclude:
175 if tb[-1][0].count(rpcfile):
176 break
177 else:
178 break
179 del tb[-1]
180 if len(tb) == 0:
181 # exception was in IDLE internals, don't prune!
182 tb[:] = orig_tb[:]
183 print>>sys.stderr, "** IDLE Internal Exception: "
184 rpchandler = rpc.objecttable['exec'].rpchandler
185 for i in range(len(tb)):
186 fn, ln, nm, line = tb[i]
187 if nm == '?':
188 nm = "-toplevel-"
189 if not line and fn.startswith("<pyshell#"):
190 line = rpchandler.remotecall('linecache', 'getline',
191 (fn, ln), {})
192 tb[i] = fn, ln, nm, line
193
194def flush_stdout():
195 try:
196 if sys.stdout.softspace:
197 sys.stdout.softspace = 0
198 sys.stdout.write("\n")
199 except (AttributeError, EOFError):
200 pass
201
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000202def exit():
203 """Exit subprocess, possibly after first deleting sys.exitfunc
204
205 If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any
206 sys.exitfunc will be removed before exiting. (VPython support)
207
208 """
209 if no_exitfunc:
Kurt B. Kaiserf30ba3d2008-01-23 22:55:26 +0000210 try:
211 del sys.exitfunc
212 except AttributeError:
213 pass
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000214 sys.exit(0)
Chui Tey5d2af632002-05-26 13:36:41 +0000215
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000216class MyRPCServer(rpc.RPCServer):
217
218 def handle_error(self, request, client_address):
219 """Override RPCServer method for IDLE
220
221 Interrupt the MainThread and exit server if link is dropped.
222
223 """
Kurt B. Kaisere9535112004-11-19 15:46:49 +0000224 global quitting
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000225 try:
226 raise
227 except SystemExit:
228 raise
229 except EOFError:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000230 global exit_now
231 exit_now = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000232 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000233 except:
234 erf = sys.__stderr__
235 print>>erf, '\n' + '-'*40
236 print>>erf, 'Unhandled server exception!'
237 print>>erf, 'Thread: %s' % threading.currentThread().getName()
238 print>>erf, 'Client Address: ', client_address
239 print>>erf, 'Request: ', repr(request)
240 traceback.print_exc(file=erf)
241 print>>erf, '\n*** Unrecoverable, server exiting!'
242 print>>erf, '-'*40
Kurt B. Kaisere9535112004-11-19 15:46:49 +0000243 quitting = True
244 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000245
246
Chui Tey5d2af632002-05-26 13:36:41 +0000247class MyHandler(rpc.RPCHandler):
248
249 def handle(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000250 """Override base method"""
Chui Tey5d2af632002-05-26 13:36:41 +0000251 executive = Executive(self)
252 self.register("exec", executive)
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000253 sys.stdin = self.console = self.get_remote_proxy("stdin")
Chui Tey5d2af632002-05-26 13:36:41 +0000254 sys.stdout = self.get_remote_proxy("stdout")
255 sys.stderr = self.get_remote_proxy("stderr")
Martin v. Löwisbcc651a2003-06-22 07:52:56 +0000256 import IOBinding
257 sys.stdin.encoding = sys.stdout.encoding = \
258 sys.stderr.encoding = IOBinding.encoding
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000259 self.interp = self.get_remote_proxy("interp")
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000260 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
261
262 def exithook(self):
263 "override SocketIO method - wait for MainThread to shut us down"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000264 time.sleep(10)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000265
266 def EOFhook(self):
267 "Override SocketIO method - terminate wait on callback and exit thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000268 global quitting
269 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000270 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000271
272 def decode_interrupthook(self):
273 "interrupt awakened thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000274 global quitting
275 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000276 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000277
Chui Tey5d2af632002-05-26 13:36:41 +0000278
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000279class Executive(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000280
281 def __init__(self, rpchandler):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000282 self.rpchandler = rpchandler
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000283 self.locals = __main__.__dict__
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000284 self.calltip = CallTips.CallTips()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000285 self.autocomplete = AutoComplete.AutoComplete()
Chui Tey5d2af632002-05-26 13:36:41 +0000286
287 def runcode(self, code):
Kurt B. Kaiserc8f65e62007-10-09 19:31:30 +0000288 global interruptable
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000289 try:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000290 self.usr_exc_info = None
Kurt B. Kaiserc8f65e62007-10-09 19:31:30 +0000291 interruptable = True
292 try:
293 exec code in self.locals
294 finally:
295 interruptable = False
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000296 except:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000297 self.usr_exc_info = sys.exc_info()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000298 if quitting:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000299 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000300 # even print a user code SystemExit exception, continue
301 print_exception()
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000302 jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
303 if jit:
304 self.rpchandler.interp.open_remote_stack_viewer()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000305 else:
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000306 flush_stdout()
Chui Tey5d2af632002-05-26 13:36:41 +0000307
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000308 def interrupt_the_server(self):
Kurt B. Kaiserc8f65e62007-10-09 19:31:30 +0000309 if interruptable:
310 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000311
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000312 def start_the_debugger(self, gui_adap_oid):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000313 return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
314
315 def stop_the_debugger(self, idb_adap_oid):
316 "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
317 self.rpchandler.unregister(idb_adap_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000318
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000319 def get_the_calltip(self, name):
320 return self.calltip.fetch_tip(name)
321
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000322 def get_the_completion_list(self, what, mode):
323 return self.autocomplete.fetch_completions(what, mode)
324
Chui Tey5d2af632002-05-26 13:36:41 +0000325 def stackviewer(self, flist_oid=None):
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000326 if self.usr_exc_info:
327 typ, val, tb = self.usr_exc_info
328 else:
Chui Tey5d2af632002-05-26 13:36:41 +0000329 return None
330 flist = None
331 if flist_oid is not None:
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000332 flist = self.rpchandler.get_remote_proxy(flist_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000333 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
334 tb = tb.tb_next
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000335 sys.last_type = typ
336 sys.last_value = val
Chui Tey5d2af632002-05-26 13:36:41 +0000337 item = StackViewer.StackTreeItem(flist, tb)
338 return RemoteObjectBrowser.remote_object_tree_item(item)