blob: c66679cde92bb299e562f2dd5ba7d8cec13e20db [file] [log] [blame]
Chui Tey5d2af632002-05-26 13:36:41 +00001import sys
Martin v. Löwis9ae3f7a2012-07-09 20:46:11 +02002import io
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +00003import linecache
Kurt B. Kaiserb4179362002-07-26 00:06:42 +00004import time
5import socket
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +00006import traceback
Georg Brandl2067bfd2008-05-25 13:05:15 +00007import _thread as thread
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +00008import threading
Alexandre Vassalottif260e442008-05-11 19:59:59 +00009import queue
Andrew Svetlov753445a2012-03-26 21:56:44 +030010import tkinter
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000011
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000012from idlelib import CallTips
13from idlelib import AutoComplete
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000014
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000015from idlelib import RemoteDebugger
16from idlelib import RemoteObjectBrowser
17from idlelib import StackViewer
18from idlelib import rpc
Serhiy Storchaka39e70a42013-01-25 15:30:58 +020019from idlelib import PyShell
20from idlelib import IOBinding
Chui Tey5d2af632002-05-26 13:36:41 +000021
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000022import __main__
23
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +000024LOCALHOST = '127.0.0.1'
25
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +000026try:
27 import warnings
28except ImportError:
29 pass
30else:
Benjamin Peterson206e3072008-10-19 14:07:49 +000031 def idle_formatwarning_subproc(message, category, filename, lineno,
Guilherme Polo1fff0082009-08-14 15:05:30 +000032 line=None):
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +000033 """Format warnings the IDLE way"""
34 s = "\nWarning (from warnings module):\n"
35 s += ' File \"%s\", line %s\n' % (filename, lineno)
Guilherme Polo1fff0082009-08-14 15:05:30 +000036 if line is None:
37 line = linecache.getline(filename, lineno)
38 line = line.strip()
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +000039 if line:
40 s += " %s\n" % line
41 s += "%s: %s\n" % (category.__name__, message)
42 return s
43 warnings.formatwarning = idle_formatwarning_subproc
44
Andrew Svetlova2251aa2012-03-13 18:36:13 -070045
Andrew Svetlov753445a2012-03-26 21:56:44 +030046tcl = tkinter.Tcl()
47
48
49def handle_tk_events(tcl=tcl):
Andrew Svetlova2251aa2012-03-13 18:36:13 -070050 """Process any tk events that are ready to be dispatched if tkinter
51 has been imported, a tcl interpreter has been created and tk has been
52 loaded."""
Andrew Svetlov753445a2012-03-26 21:56:44 +030053 tcl.eval("update")
Andrew Svetlova2251aa2012-03-13 18:36:13 -070054
55
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000056# Thread shared globals: Establish a queue between a subthread (which handles
57# the socket) and the main thread (which runs user code), plus global
Guido van Rossum8ce8a782007-11-01 19:42:39 +000058# completion, exit and interruptable (the main thread) flags:
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000059
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000060exit_now = False
61quitting = False
Guido van Rossum8ce8a782007-11-01 19:42:39 +000062interruptable = False
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000063
Kurt B. Kaiser62df0442003-05-28 01:47:46 +000064def main(del_exitfunc=False):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000065 """Start the Python execution server in a subprocess
66
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000067 In the Python subprocess, RPCServer is instantiated with handlerclass
68 MyHandler, which inherits register/unregister methods from RPCHandler via
69 the mix-in class SocketIO.
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000070
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000071 When the RPCServer 'server' is instantiated, the TCPServer initialization
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000072 creates an instance of run.MyHandler and calls its handle() method.
73 handle() instantiates a run.Executive object, passing it a reference to the
74 MyHandler object. That reference is saved as attribute rpchandler of the
75 Executive instance. The Executive methods have access to the reference and
76 can pass it on to entities that they command
77 (e.g. RemoteDebugger.Debugger.start_debugger()). The latter, in turn, can
78 call MyHandler(SocketIO) register/unregister methods via the reference to
79 register and unregister themselves.
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000080
81 """
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000082 global exit_now
83 global quitting
Kurt B. Kaiser62df0442003-05-28 01:47:46 +000084 global no_exitfunc
85 no_exitfunc = del_exitfunc
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +000086 #time.sleep(15) # test subprocess not responding
Kurt B. Kaisere67842a2009-04-04 20:13:23 +000087 try:
88 assert(len(sys.argv) > 1)
89 port = int(sys.argv[-1])
90 except:
Kurt B. Kaiser8fc98c32009-04-04 20:20:29 +000091 print("IDLE Subprocess: no IP port passed in sys.argv.",
92 file=sys.__stderr__)
Kurt B. Kaisere67842a2009-04-04 20:13:23 +000093 return
Chui Tey5d2af632002-05-26 13:36:41 +000094 sys.argv[:] = [""]
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000095 sockthread = threading.Thread(target=manage_socket,
96 name='SockThread',
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +000097 args=((LOCALHOST, port),))
Benjamin Peterson71088cc2008-09-19 21:49:37 +000098 sockthread.daemon = True
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000099 sockthread.start()
100 while 1:
101 try:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000102 if exit_now:
103 try:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000104 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000105 except KeyboardInterrupt:
106 # exiting but got an extra KBI? Try again!
107 continue
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000108 try:
Kurt B. Kaiser20345fb2005-05-05 23:29:54 +0000109 seq, request = rpc.request_queue.get(block=True, timeout=0.05)
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000110 except queue.Empty:
Andrew Svetlova2251aa2012-03-13 18:36:13 -0700111 handle_tk_events()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000112 continue
113 method, args, kwargs = request
114 ret = method(*args, **kwargs)
115 rpc.response_queue.put((seq, ret))
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +0000116 except KeyboardInterrupt:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000117 if quitting:
118 exit_now = True
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +0000119 continue
Kurt B. Kaisera2792be2003-05-17 21:04:10 +0000120 except SystemExit:
121 raise
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000122 except:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000123 type, value, tb = sys.exc_info()
Kurt B. Kaisera2792be2003-05-17 21:04:10 +0000124 try:
125 print_exception()
126 rpc.response_queue.put((seq, None))
127 except:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000128 # Link didn't work, print same exception to __stderr__
129 traceback.print_exception(type, value, tb, file=sys.__stderr__)
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000130 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000131 else:
132 continue
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000133
134def manage_socket(address):
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000135 for i in range(3):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000136 time.sleep(i)
137 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000138 server = MyRPCServer(address, MyHandler)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000139 break
Andrew Svetlov0832af62012-12-18 23:10:48 +0200140 except OSError as err:
141 print("IDLE Subprocess: OSError: " + err.args[1] +
Georg Brandl6464d472007-10-22 16:16:13 +0000142 ", retrying....", file=sys.__stderr__)
Amaury Forgeot d'Arcefae8c42008-11-21 23:08:09 +0000143 socket_error = err
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000144 else:
Amaury Forgeot d'Arcefae8c42008-11-21 23:08:09 +0000145 print("IDLE Subprocess: Connection to "
146 "IDLE GUI failed, exiting.", file=sys.__stderr__)
147 show_socket_error(socket_error, address)
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000148 global exit_now
149 exit_now = True
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000150 return
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000151 server.handle_request() # A single request only
152
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000153def show_socket_error(err, address):
Georg Brandl14fc4272008-05-17 18:39:55 +0000154 import tkinter
155 import tkinter.messagebox as tkMessageBox
156 root = tkinter.Tk()
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000157 root.withdraw()
Georg Brandl6464d472007-10-22 16:16:13 +0000158 if err.args[0] == 61: # connection refused
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000159 msg = "IDLE's subprocess can't connect to %s:%d. This may be due "\
160 "to your personal firewall configuration. It is safe to "\
161 "allow this internal connection because no data is visible on "\
162 "external ports." % address
163 tkMessageBox.showerror("IDLE Subprocess Error", msg, parent=root)
164 else:
Georg Brandl6464d472007-10-22 16:16:13 +0000165 tkMessageBox.showerror("IDLE Subprocess Error",
166 "Socket Error: %s" % err.args[1])
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000167 root.destroy()
168
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000169def print_exception():
Kurt B. Kaisere9802a32004-01-02 04:04:04 +0000170 import linecache
171 linecache.checkcache()
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000172 flush_stdout()
173 efile = sys.stderr
Kurt B. Kaiser924f6162003-11-19 04:52:32 +0000174 typ, val, tb = excinfo = sys.exc_info()
175 sys.last_type, sys.last_value, sys.last_traceback = excinfo
Serhiy Storchaka78470b42013-01-09 12:21:57 +0200176 seen = set()
177
178 def print_exc(typ, exc, tb):
179 seen.add(exc)
180 context = exc.__context__
181 cause = exc.__cause__
182 if cause is not None and cause not in seen:
183 print_exc(type(cause), cause, cause.__traceback__)
184 print("\nThe above exception was the direct cause "
185 "of the following exception:\n", file=efile)
Serhiy Storchaka71317492013-01-09 12:24:48 +0200186 elif (context is not None and
187 not exc.__suppress_context__ and
188 context not in seen):
Serhiy Storchaka78470b42013-01-09 12:21:57 +0200189 print_exc(type(context), context, context.__traceback__)
190 print("\nDuring handling of the above exception, "
191 "another exception occurred:\n", file=efile)
192 if tb:
193 tbe = traceback.extract_tb(tb)
194 print('Traceback (most recent call last):', file=efile)
195 exclude = ("run.py", "rpc.py", "threading.py", "queue.py",
196 "RemoteDebugger.py", "bdb.py")
197 cleanup_traceback(tbe, exclude)
198 traceback.print_list(tbe, file=efile)
199 lines = traceback.format_exception_only(typ, exc)
200 for line in lines:
201 print(line, end='', file=efile)
202
203 print_exc(typ, val, tb)
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000204
205def cleanup_traceback(tb, exclude):
206 "Remove excluded traces from beginning/end of tb; get cached lines"
207 orig_tb = tb[:]
208 while tb:
209 for rpcfile in exclude:
210 if tb[0][0].count(rpcfile):
211 break # found an exclude, break for: and delete tb[0]
212 else:
213 break # no excludes, have left RPC code, break while:
214 del tb[0]
215 while tb:
216 for rpcfile in exclude:
217 if tb[-1][0].count(rpcfile):
218 break
219 else:
220 break
221 del tb[-1]
222 if len(tb) == 0:
223 # exception was in IDLE internals, don't prune!
224 tb[:] = orig_tb[:]
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000225 print("** IDLE Internal Exception: ", file=sys.stderr)
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000226 rpchandler = rpc.objecttable['exec'].rpchandler
227 for i in range(len(tb)):
228 fn, ln, nm, line = tb[i]
229 if nm == '?':
230 nm = "-toplevel-"
231 if not line and fn.startswith("<pyshell#"):
232 line = rpchandler.remotecall('linecache', 'getline',
233 (fn, ln), {})
234 tb[i] = fn, ln, nm, line
235
236def flush_stdout():
Guido van Rossum79139b22007-02-09 23:20:19 +0000237 """XXX How to do this now?"""
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000238
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000239def exit():
Guido van Rossumc76a2502007-08-09 14:26:58 +0000240 """Exit subprocess, possibly after first clearing exit functions.
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000241
242 If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any
Guido van Rossumc76a2502007-08-09 14:26:58 +0000243 functions registered with atexit will be removed before exiting.
244 (VPython support)
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000245
246 """
247 if no_exitfunc:
Guido van Rossumc76a2502007-08-09 14:26:58 +0000248 import atexit
249 atexit._clear()
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000250 sys.exit(0)
Chui Tey5d2af632002-05-26 13:36:41 +0000251
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000252class MyRPCServer(rpc.RPCServer):
253
254 def handle_error(self, request, client_address):
255 """Override RPCServer method for IDLE
256
257 Interrupt the MainThread and exit server if link is dropped.
258
259 """
Kurt B. Kaisere9535112004-11-19 15:46:49 +0000260 global quitting
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000261 try:
262 raise
263 except SystemExit:
264 raise
265 except EOFError:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000266 global exit_now
267 exit_now = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000268 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000269 except:
270 erf = sys.__stderr__
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000271 print('\n' + '-'*40, file=erf)
272 print('Unhandled server exception!', file=erf)
Amaury Forgeot d'Arcbed17102008-11-29 01:48:47 +0000273 print('Thread: %s' % threading.current_thread().name, file=erf)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000274 print('Client Address: ', client_address, file=erf)
275 print('Request: ', repr(request), file=erf)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000276 traceback.print_exc(file=erf)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000277 print('\n*** Unrecoverable, server exiting!', file=erf)
278 print('-'*40, file=erf)
Kurt B. Kaisere9535112004-11-19 15:46:49 +0000279 quitting = True
280 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000281
Chui Tey5d2af632002-05-26 13:36:41 +0000282class MyHandler(rpc.RPCHandler):
283
284 def handle(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000285 """Override base method"""
Chui Tey5d2af632002-05-26 13:36:41 +0000286 executive = Executive(self)
287 self.register("exec", executive)
Serhiy Storchaka39e70a42013-01-25 15:30:58 +0200288 self.console = self.get_remote_proxy("console")
289 sys.stdin = PyShell.PseudoInputFile(self.console, "stdin",
290 IOBinding.encoding)
291 sys.stdout = PyShell.PseudoOutputFile(self.console, "stdout",
292 IOBinding.encoding)
293 sys.stderr = PyShell.PseudoOutputFile(self.console, "stderr",
294 IOBinding.encoding)
295
Andrew Svetlovcd49d532012-03-25 11:43:02 +0300296 sys.displayhook = rpc.displayhook
Kurt B. Kaiserf609a342007-12-28 03:57:56 +0000297 # page help() text to shell.
298 import pydoc # import must be done here to capture i/o binding
299 pydoc.pager = pydoc.plainpager
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000300 self.interp = self.get_remote_proxy("interp")
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000301 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
302
303 def exithook(self):
304 "override SocketIO method - wait for MainThread to shut us down"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000305 time.sleep(10)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000306
307 def EOFhook(self):
308 "Override SocketIO method - terminate wait on callback and exit thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000309 global quitting
310 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000311 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000312
313 def decode_interrupthook(self):
314 "interrupt awakened thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000315 global quitting
316 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000317 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000318
Chui Tey5d2af632002-05-26 13:36:41 +0000319
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000320class Executive(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000321
322 def __init__(self, rpchandler):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000323 self.rpchandler = rpchandler
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000324 self.locals = __main__.__dict__
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000325 self.calltip = CallTips.CallTips()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000326 self.autocomplete = AutoComplete.AutoComplete()
Chui Tey5d2af632002-05-26 13:36:41 +0000327
328 def runcode(self, code):
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000329 global interruptable
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000330 try:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000331 self.usr_exc_info = None
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000332 interruptable = True
333 try:
334 exec(code, self.locals)
335 finally:
336 interruptable = False
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000337 except:
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000338 self.usr_exc_info = sys.exc_info()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000339 if quitting:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000340 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000341 # even print a user code SystemExit exception, continue
342 print_exception()
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000343 jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
344 if jit:
345 self.rpchandler.interp.open_remote_stack_viewer()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000346 else:
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000347 flush_stdout()
Chui Tey5d2af632002-05-26 13:36:41 +0000348
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000349 def interrupt_the_server(self):
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000350 if interruptable:
351 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000352
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000353 def start_the_debugger(self, gui_adap_oid):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000354 return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
355
356 def stop_the_debugger(self, idb_adap_oid):
357 "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
358 self.rpchandler.unregister(idb_adap_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000359
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000360 def get_the_calltip(self, name):
361 return self.calltip.fetch_tip(name)
362
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000363 def get_the_completion_list(self, what, mode):
364 return self.autocomplete.fetch_completions(what, mode)
365
Chui Tey5d2af632002-05-26 13:36:41 +0000366 def stackviewer(self, flist_oid=None):
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000367 if self.usr_exc_info:
368 typ, val, tb = self.usr_exc_info
369 else:
Chui Tey5d2af632002-05-26 13:36:41 +0000370 return None
371 flist = None
372 if flist_oid is not None:
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000373 flist = self.rpchandler.get_remote_proxy(flist_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000374 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
375 tb = tb.tb_next
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000376 sys.last_type = typ
377 sys.last_value = val
Chui Tey5d2af632002-05-26 13:36:41 +0000378 item = StackViewer.StackTreeItem(flist, tb)
379 return RemoteObjectBrowser.remote_object_tree_item(item)