217 lines
7.0 KiB
Python
217 lines
7.0 KiB
Python
import sys
|
|
import time
|
|
import socket
|
|
import traceback
|
|
import threading
|
|
import Queue
|
|
|
|
import boolcheck
|
|
|
|
import CallTips
|
|
import RemoteDebugger
|
|
import RemoteObjectBrowser
|
|
import StackViewer
|
|
import rpc
|
|
import interrupt
|
|
|
|
import __main__
|
|
|
|
# Thread shared globals: Establish a queue between a subthread (which handles
|
|
# the socket) and the main thread (which runs user code), plus global
|
|
# completion and exit flags:
|
|
|
|
server = None # RPCServer instance
|
|
queue = Queue.Queue(0)
|
|
execution_finished = False
|
|
exit_requested = False
|
|
|
|
|
|
def main():
|
|
"""Start the Python execution server in a subprocess
|
|
|
|
In the Python subprocess, RPCServer is instantiated with handlerclass
|
|
MyHandler, which inherits register/unregister methods from RPCHandler via
|
|
the mix-in class SocketIO.
|
|
|
|
When the RPCServer 'server' is instantiated, the TCPServer initialization
|
|
creates an instance of run.MyHandler and calls its handle() method.
|
|
handle() instantiates a run.Executive object, passing it a reference to the
|
|
MyHandler object. That reference is saved as attribute rpchandler of the
|
|
Executive instance. The Executive methods have access to the reference and
|
|
can pass it on to entities that they command
|
|
(e.g. RemoteDebugger.Debugger.start_debugger()). The latter, in turn, can
|
|
call MyHandler(SocketIO) register/unregister methods via the reference to
|
|
register and unregister themselves.
|
|
|
|
"""
|
|
global queue, execution_finished, exit_requested
|
|
|
|
port = 8833
|
|
if sys.argv[1:]:
|
|
port = int(sys.argv[1])
|
|
sys.argv[:] = [""]
|
|
sockthread = threading.Thread(target=manage_socket,
|
|
name='SockThread',
|
|
args=(('localhost', port),))
|
|
sockthread.setDaemon(True)
|
|
sockthread.start()
|
|
while 1:
|
|
try:
|
|
if exit_requested:
|
|
sys.exit()
|
|
# XXX KBK 22Mar03 eventually check queue here!
|
|
pass
|
|
time.sleep(0.05)
|
|
except KeyboardInterrupt:
|
|
##execution_finished = True
|
|
continue
|
|
|
|
def manage_socket(address):
|
|
global server, exit_requested
|
|
|
|
for i in range(6):
|
|
time.sleep(i)
|
|
try:
|
|
server = rpc.RPCServer(address, MyHandler)
|
|
break
|
|
except socket.error, err:
|
|
if i < 3:
|
|
print>>sys.__stderr__, ".. ",
|
|
else:
|
|
print>>sys.__stderr__,"\nPython subprocess socket error: "\
|
|
+ err[1] + ", retrying...."
|
|
else:
|
|
print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
|
|
exit_requested = True
|
|
server.handle_request() # A single request only
|
|
|
|
|
|
class MyHandler(rpc.RPCHandler):
|
|
|
|
def handle(self):
|
|
"""Override base method"""
|
|
executive = Executive(self)
|
|
self.register("exec", executive)
|
|
sys.stdin = self.get_remote_proxy("stdin")
|
|
sys.stdout = self.get_remote_proxy("stdout")
|
|
sys.stderr = self.get_remote_proxy("stderr")
|
|
rpc.RPCHandler.getresponse(self, myseq=None, wait=0.5)
|
|
|
|
|
|
class Executive:
|
|
|
|
def __init__(self, rpchandler):
|
|
self.rpchandler = rpchandler
|
|
self.locals = __main__.__dict__
|
|
self.calltip = CallTips.CallTips()
|
|
|
|
def runcode(self, code):
|
|
global queue, execution_finished
|
|
|
|
execution_finished = False
|
|
queue.put(code)
|
|
# dequeue and run in subthread
|
|
self.runcode_from_queue()
|
|
while not execution_finished:
|
|
time.sleep(0.05)
|
|
|
|
def runcode_from_queue(self):
|
|
global queue, execution_finished
|
|
|
|
# poll until queue has code object, using threads, just block?
|
|
while True:
|
|
try:
|
|
code = queue.get(0)
|
|
break
|
|
except Queue.Empty:
|
|
time.sleep(0.05)
|
|
try:
|
|
exec code in self.locals
|
|
except:
|
|
self.flush_stdout()
|
|
efile = sys.stderr
|
|
typ, val, tb = info = sys.exc_info()
|
|
sys.last_type, sys.last_value, sys.last_traceback = info
|
|
tbe = traceback.extract_tb(tb)
|
|
print >>efile, 'Traceback (most recent call last):'
|
|
exclude = ("run.py", "rpc.py", "RemoteDebugger.py", "bdb.py")
|
|
self.cleanup_traceback(tbe, exclude)
|
|
traceback.print_list(tbe, file=efile)
|
|
lines = traceback.format_exception_only(typ, val)
|
|
for line in lines:
|
|
print>>efile, line,
|
|
execution_finished = True
|
|
else:
|
|
self.flush_stdout()
|
|
execution_finished = True
|
|
|
|
def flush_stdout(self):
|
|
try:
|
|
if sys.stdout.softspace:
|
|
sys.stdout.softspace = 0
|
|
sys.stdout.write("\n")
|
|
except (AttributeError, EOFError):
|
|
pass
|
|
|
|
def cleanup_traceback(self, tb, exclude):
|
|
"Remove excluded traces from beginning/end of tb; get cached lines"
|
|
orig_tb = tb[:]
|
|
while tb:
|
|
for rpcfile in exclude:
|
|
if tb[0][0].count(rpcfile):
|
|
break # found an exclude, break for: and delete tb[0]
|
|
else:
|
|
break # no excludes, have left RPC code, break while:
|
|
del tb[0]
|
|
while tb:
|
|
for rpcfile in exclude:
|
|
if tb[-1][0].count(rpcfile):
|
|
break
|
|
else:
|
|
break
|
|
del tb[-1]
|
|
if len(tb) == 0:
|
|
# exception was in IDLE internals, don't prune!
|
|
tb[:] = orig_tb[:]
|
|
print>>sys.stderr, "** IDLE Internal Exception: "
|
|
for i in range(len(tb)):
|
|
fn, ln, nm, line = tb[i]
|
|
if nm == '?':
|
|
nm = "-toplevel-"
|
|
if not line and fn.startswith("<pyshell#"):
|
|
line = self.rpchandler.remotecall('linecache', 'getline',
|
|
(fn, ln), {})
|
|
tb[i] = fn, ln, nm, line
|
|
|
|
def interrupt_the_server(self):
|
|
self.rpchandler.interrupted = True
|
|
##print>>sys.__stderr__, "** Interrupt main!"
|
|
interrupt.interrupt_main()
|
|
|
|
def shutdown_the_server(self):
|
|
global exit_requested
|
|
|
|
exit_requested = True
|
|
|
|
def start_the_debugger(self, gui_adap_oid):
|
|
return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
|
|
|
|
def stop_the_debugger(self, idb_adap_oid):
|
|
"Unregister the Idb Adapter. Link objects and Idb then subject to GC"
|
|
self.rpchandler.unregister(idb_adap_oid)
|
|
|
|
def get_the_calltip(self, name):
|
|
return self.calltip.fetch_tip(name)
|
|
|
|
def stackviewer(self, flist_oid=None):
|
|
if not hasattr(sys, "last_traceback"):
|
|
return None
|
|
flist = None
|
|
if flist_oid is not None:
|
|
flist = self.rpchandler.get_remote_proxy(flist_oid)
|
|
tb = sys.last_traceback
|
|
while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
|
|
tb = tb.tb_next
|
|
item = StackViewer.StackTreeItem(flist, tb)
|
|
return RemoteObjectBrowser.remote_object_tree_item(item)
|