blob: 2d5c5edaaa7de51d32d86a94253d2ef4f02459f8 [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
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +000070 #time.sleep(15) # test subprocess not responding
Kurt B. Kaiser4724f402009-04-02 02:44:54 +000071 try:
72 assert(len(sys.argv) > 1)
73 port = int(sys.argv[-1])
74 except:
75 print>>sys.stderr, "IDLE Subprocess: no IP port passed in sys.argv."
76 return
Chui Tey5d2af632002-05-26 13:36:41 +000077 sys.argv[:] = [""]
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000078 sockthread = threading.Thread(target=manage_socket,
79 name='SockThread',
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +000080 args=((LOCALHOST, port),))
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000081 sockthread.setDaemon(True)
82 sockthread.start()
83 while 1:
84 try:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000085 if exit_now:
86 try:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +000087 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000088 except KeyboardInterrupt:
89 # exiting but got an extra KBI? Try again!
90 continue
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000091 try:
Kurt B. Kaiser20345fb2005-05-05 23:29:54 +000092 seq, request = rpc.request_queue.get(block=True, timeout=0.05)
Georg Brandla6168f92008-05-25 07:20:14 +000093 except Queue.Empty:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000094 continue
95 method, args, kwargs = request
96 ret = method(*args, **kwargs)
97 rpc.response_queue.put((seq, ret))
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +000098 except KeyboardInterrupt:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000099 if quitting:
100 exit_now = True
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +0000101 continue
Kurt B. Kaisera2792be2003-05-17 21:04:10 +0000102 except SystemExit:
103 raise
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000104 except:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000105 type, value, tb = sys.exc_info()
Kurt B. Kaisera2792be2003-05-17 21:04:10 +0000106 try:
107 print_exception()
108 rpc.response_queue.put((seq, None))
109 except:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000110 # Link didn't work, print same exception to __stderr__
111 traceback.print_exception(type, value, tb, file=sys.__stderr__)
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000112 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000113 else:
114 continue
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000115
116def manage_socket(address):
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000117 for i in range(3):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000118 time.sleep(i)
119 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000120 server = MyRPCServer(address, MyHandler)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000121 break
122 except socket.error, err:
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000123 print>>sys.__stderr__,"IDLE Subprocess: socket error: "\
124 + err[1] + ", retrying...."
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000125 else:
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000126 print>>sys.__stderr__, "IDLE Subprocess: Connection to "\
127 "IDLE GUI failed, exiting."
128 show_socket_error(err, address)
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000129 global exit_now
130 exit_now = True
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000131 return
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000132 server.handle_request() # A single request only
133
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000134def show_socket_error(err, address):
Georg Brandl6634bf22008-05-20 07:13:37 +0000135 import Tkinter
136 import tkMessageBox
137 root = Tkinter.Tk()
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000138 root.withdraw()
139 if err[0] == 61: # connection refused
140 msg = "IDLE's subprocess can't connect to %s:%d. This may be due "\
141 "to your personal firewall configuration. It is safe to "\
142 "allow this internal connection because no data is visible on "\
143 "external ports." % address
144 tkMessageBox.showerror("IDLE Subprocess Error", msg, parent=root)
145 else:
146 tkMessageBox.showerror("IDLE Subprocess Error", "Socket Error: %s" % err[1])
147 root.destroy()
148
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000149def print_exception():
Kurt B. Kaisere9802a32004-01-02 04:04:04 +0000150 import linecache
151 linecache.checkcache()
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000152 flush_stdout()
153 efile = sys.stderr
Kurt B. Kaiser924f6162003-11-19 04:52:32 +0000154 typ, val, tb = excinfo = sys.exc_info()
155 sys.last_type, sys.last_value, sys.last_traceback = excinfo
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000156 tbe = traceback.extract_tb(tb)
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000157 print>>efile, '\nTraceback (most recent call last):'
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000158 exclude = ("run.py", "rpc.py", "threading.py", "Queue.py",
159 "RemoteDebugger.py", "bdb.py")
160 cleanup_traceback(tbe, exclude)
161 traceback.print_list(tbe, file=efile)
162 lines = traceback.format_exception_only(typ, val)
163 for line in lines:
164 print>>efile, line,
165
166def cleanup_traceback(tb, exclude):
167 "Remove excluded traces from beginning/end of tb; get cached lines"
168 orig_tb = tb[:]
169 while tb:
170 for rpcfile in exclude:
171 if tb[0][0].count(rpcfile):
172 break # found an exclude, break for: and delete tb[0]
173 else:
174 break # no excludes, have left RPC code, break while:
175 del tb[0]
176 while tb:
177 for rpcfile in exclude:
178 if tb[-1][0].count(rpcfile):
179 break
180 else:
181 break
182 del tb[-1]
183 if len(tb) == 0:
184 # exception was in IDLE internals, don't prune!
185 tb[:] = orig_tb[:]
186 print>>sys.stderr, "** IDLE Internal Exception: "
187 rpchandler = rpc.objecttable['exec'].rpchandler
188 for i in range(len(tb)):
189 fn, ln, nm, line = tb[i]
190 if nm == '?':
191 nm = "-toplevel-"
192 if not line and fn.startswith("<pyshell#"):
193 line = rpchandler.remotecall('linecache', 'getline',
194 (fn, ln), {})
195 tb[i] = fn, ln, nm, line
196
197def flush_stdout():
198 try:
199 if sys.stdout.softspace:
200 sys.stdout.softspace = 0
201 sys.stdout.write("\n")
202 except (AttributeError, EOFError):
203 pass
204
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000205def exit():
206 """Exit subprocess, possibly after first deleting sys.exitfunc
207
208 If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any
209 sys.exitfunc will be removed before exiting. (VPython support)
210
211 """
212 if no_exitfunc:
Kurt B. Kaiserf30ba3d2008-01-23 22:55:26 +0000213 try:
214 del sys.exitfunc
215 except AttributeError:
216 pass
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000217 sys.exit(0)
Chui Tey5d2af632002-05-26 13:36:41 +0000218
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000219class MyRPCServer(rpc.RPCServer):
220
221 def handle_error(self, request, client_address):
222 """Override RPCServer method for IDLE
223
224 Interrupt the MainThread and exit server if link is dropped.
225
226 """
Kurt B. Kaisere9535112004-11-19 15:46:49 +0000227 global quitting
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000228 try:
229 raise
230 except SystemExit:
231 raise
232 except EOFError:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000233 global exit_now
234 exit_now = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000235 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000236 except:
237 erf = sys.__stderr__
238 print>>erf, '\n' + '-'*40
239 print>>erf, 'Unhandled server exception!'
240 print>>erf, 'Thread: %s' % threading.currentThread().getName()
241 print>>erf, 'Client Address: ', client_address
242 print>>erf, 'Request: ', repr(request)
243 traceback.print_exc(file=erf)
244 print>>erf, '\n*** Unrecoverable, server exiting!'
245 print>>erf, '-'*40
Kurt B. Kaisere9535112004-11-19 15:46:49 +0000246 quitting = True
247 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000248
249
Chui Tey5d2af632002-05-26 13:36:41 +0000250class MyHandler(rpc.RPCHandler):
251
252 def handle(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000253 """Override base method"""
Chui Tey5d2af632002-05-26 13:36:41 +0000254 executive = Executive(self)
255 self.register("exec", executive)
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000256 sys.stdin = self.console = self.get_remote_proxy("stdin")
Chui Tey5d2af632002-05-26 13:36:41 +0000257 sys.stdout = self.get_remote_proxy("stdout")
258 sys.stderr = self.get_remote_proxy("stderr")
Martin v. Löwisbcc651a2003-06-22 07:52:56 +0000259 import IOBinding
260 sys.stdin.encoding = sys.stdout.encoding = \
261 sys.stderr.encoding = IOBinding.encoding
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000262 self.interp = self.get_remote_proxy("interp")
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000263 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
264
265 def exithook(self):
266 "override SocketIO method - wait for MainThread to shut us down"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000267 time.sleep(10)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000268
269 def EOFhook(self):
270 "Override SocketIO method - terminate wait on callback and exit thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000271 global quitting
272 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000273 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000274
275 def decode_interrupthook(self):
276 "interrupt awakened thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000277 global quitting
278 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000279 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000280
Chui Tey5d2af632002-05-26 13:36:41 +0000281
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000282class Executive(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000283
284 def __init__(self, rpchandler):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000285 self.rpchandler = rpchandler
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000286 self.locals = __main__.__dict__
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000287 self.calltip = CallTips.CallTips()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000288 self.autocomplete = AutoComplete.AutoComplete()
Chui Tey5d2af632002-05-26 13:36:41 +0000289
290 def runcode(self, code):
Kurt B. Kaiserc8f65e62007-10-09 19:31:30 +0000291 global interruptable
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000292 try:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000293 self.usr_exc_info = None
Kurt B. Kaiserc8f65e62007-10-09 19:31:30 +0000294 interruptable = True
295 try:
296 exec code in self.locals
297 finally:
298 interruptable = False
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000299 except:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000300 self.usr_exc_info = sys.exc_info()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000301 if quitting:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000302 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000303 # even print a user code SystemExit exception, continue
304 print_exception()
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000305 jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
306 if jit:
307 self.rpchandler.interp.open_remote_stack_viewer()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000308 else:
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000309 flush_stdout()
Chui Tey5d2af632002-05-26 13:36:41 +0000310
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000311 def interrupt_the_server(self):
Kurt B. Kaiserc8f65e62007-10-09 19:31:30 +0000312 if interruptable:
313 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000314
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000315 def start_the_debugger(self, gui_adap_oid):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000316 return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
317
318 def stop_the_debugger(self, idb_adap_oid):
319 "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
320 self.rpchandler.unregister(idb_adap_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000321
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000322 def get_the_calltip(self, name):
323 return self.calltip.fetch_tip(name)
324
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000325 def get_the_completion_list(self, what, mode):
326 return self.autocomplete.fetch_completions(what, mode)
327
Chui Tey5d2af632002-05-26 13:36:41 +0000328 def stackviewer(self, flist_oid=None):
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000329 if self.usr_exc_info:
330 typ, val, tb = self.usr_exc_info
331 else:
Chui Tey5d2af632002-05-26 13:36:41 +0000332 return None
333 flist = None
334 if flist_oid is not None:
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000335 flist = self.rpchandler.get_remote_proxy(flist_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000336 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
337 tb = tb.tb_next
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000338 sys.last_type = typ
339 sys.last_value = val
Chui Tey5d2af632002-05-26 13:36:41 +0000340 item = StackViewer.StackTreeItem(flist, tb)
341 return RemoteObjectBrowser.remote_object_tree_item(item)