Commit 5d2af63c authored by Chui Tey's avatar Chui Tey

GvR's rpc patch

parent 38d53451
import os import os
import bdb import bdb
import types
import traceback import traceback
from Tkinter import * from Tkinter import *
from WindowList import ListedToplevel from WindowList import ListedToplevel
...@@ -7,20 +8,66 @@ from WindowList import ListedToplevel ...@@ -7,20 +8,66 @@ from WindowList import ListedToplevel
import StackViewer import StackViewer
class Debugger(bdb.Bdb): class Idb(bdb.Bdb):
def __init__(self, gui):
self.gui = gui
bdb.Bdb.__init__(self)
def user_line(self, frame):
# get the currently executing function
co_filename = frame.f_code.co_filename
co_name = frame.f_code.co_name
try:
func = frame.f_locals[co_name]
if getattr(func, "DebuggerStepThrough", 0):
print "XXXX DEBUGGER STEPPING THROUGH"
self.set_step()
return
except:
pass
if co_filename in ('rpc.py', '<string>'):
self.set_step()
return
if co_filename.endswith('threading.py'):
self.set_step()
return
message = self.__frame2message(frame)
self.gui.interaction(message, frame)
def user_exception(self, frame, info):
message = self.__frame2message(frame)
self.gui.interaction(message, frame, info)
def __frame2message(self, frame):
code = frame.f_code
filename = code.co_filename
lineno = frame.f_lineno
basename = os.path.basename(filename)
message = "%s:%s" % (basename, lineno)
if code.co_name != "?":
message = "%s: %s()" % (message, code.co_name)
return message
interacting = 0
class Debugger:
interacting = 0
vstack = vsource = vlocals = vglobals = None vstack = vsource = vlocals = vglobals = None
def __init__(self, pyshell): def __init__(self, pyshell, idb=None):
bdb.Bdb.__init__(self) if idb is None:
idb = Idb(self)
self.pyshell = pyshell self.pyshell = pyshell
self.idb = idb
self.make_gui() self.make_gui()
def canonic(self, filename): def run(self, *args):
# Canonicalize filename -- called by Bdb try:
return os.path.normcase(os.path.abspath(filename)) self.interacting = 1
return self.idb.run(*args)
finally:
self.interacting = 0
def close(self, event=None): def close(self, event=None):
if self.interacting: if self.interacting:
...@@ -31,24 +78,6 @@ class Debugger(bdb.Bdb): ...@@ -31,24 +78,6 @@ class Debugger(bdb.Bdb):
self.pyshell.close_debugger() self.pyshell.close_debugger()
self.top.destroy() self.top.destroy()
def run(self, *args):
try:
self.interacting = 1
return apply(bdb.Bdb.run, (self,) + args)
finally:
self.interacting = 0
def user_line(self, frame):
self.interaction(frame)
def user_return(self, frame, rv):
# XXX show rv?
##self.interaction(frame)
pass
def user_exception(self, frame, info):
self.interaction(frame, info)
def make_gui(self): def make_gui(self):
pyshell = self.pyshell pyshell = self.pyshell
self.flist = pyshell.flist self.flist = pyshell.flist
...@@ -128,16 +157,8 @@ class Debugger(bdb.Bdb): ...@@ -128,16 +157,8 @@ class Debugger(bdb.Bdb):
frame = None frame = None
def interaction(self, frame, info=None): def interaction(self, message, frame, info=None):
self.frame = frame self.frame = frame
code = frame.f_code
file = code.co_filename
base = os.path.basename(file)
lineno = frame.f_lineno
#
message = "%s:%s" % (base, lineno)
if code.co_name != "?":
message = "%s: %s()" % (message, code.co_name)
self.status.configure(text=message) self.status.configure(text=message)
# #
if info: if info:
...@@ -160,7 +181,7 @@ class Debugger(bdb.Bdb): ...@@ -160,7 +181,7 @@ class Debugger(bdb.Bdb):
# #
sv = self.stackviewer sv = self.stackviewer
if sv: if sv:
stack, i = self.get_stack(self.frame, tb) stack, i = self.idb.get_stack(self.frame, tb)
sv.load_stack(stack, i) sv.load_stack(stack, i)
# #
self.show_variables(1) self.show_variables(1)
...@@ -184,32 +205,34 @@ class Debugger(bdb.Bdb): ...@@ -184,32 +205,34 @@ class Debugger(bdb.Bdb):
frame = self.frame frame = self.frame
if not frame: if not frame:
return return
filename, lineno = self.__frame2fileline(frame)
if filename[:1] + filename[-1:] != "<>" and os.path.exists(filename):
self.flist.gotofileline(filename, lineno)
def __frame2fileline(self, frame):
code = frame.f_code code = frame.f_code
file = code.co_filename filename = code.co_filename
lineno = frame.f_lineno lineno = frame.f_lineno
if file[:1] + file[-1:] != "<>" and os.path.exists(file): return filename, lineno
edit = self.flist.open(file)
if edit:
edit.gotoline(lineno)
def cont(self): def cont(self):
self.set_continue() self.idb.set_continue()
self.root.quit() self.root.quit()
def step(self): def step(self):
self.set_step() self.idb.set_step()
self.root.quit() self.root.quit()
def next(self): def next(self):
self.set_next(self.frame) self.idb.set_next(self.frame)
self.root.quit() self.root.quit()
def ret(self): def ret(self):
self.set_return(self.frame) self.idb.set_return(self.frame)
self.root.quit() self.root.quit()
def quit(self): def quit(self):
self.set_quit() self.idb.set_quit()
self.root.quit() self.root.quit()
stackviewer = None stackviewer = None
...@@ -219,7 +242,7 @@ class Debugger(bdb.Bdb): ...@@ -219,7 +242,7 @@ class Debugger(bdb.Bdb):
self.stackviewer = sv = StackViewer.StackViewer( self.stackviewer = sv = StackViewer.StackViewer(
self.fstack, self.flist, self) self.fstack, self.flist, self)
if self.frame: if self.frame:
stack, i = self.get_stack(self.frame, None) stack, i = self.idb.get_stack(self.frame, None)
sv.load_stack(stack, i) sv.load_stack(stack, i)
else: else:
sv = self.stackviewer sv = self.stackviewer
...@@ -233,6 +256,7 @@ class Debugger(bdb.Bdb): ...@@ -233,6 +256,7 @@ class Debugger(bdb.Bdb):
self.sync_source_line() self.sync_source_line()
def show_frame(self, (frame, lineno)): def show_frame(self, (frame, lineno)):
# Called from OldStackViewer
self.frame = frame self.frame = frame
self.show_variables() self.show_variables()
...@@ -295,15 +319,15 @@ class Debugger(bdb.Bdb): ...@@ -295,15 +319,15 @@ class Debugger(bdb.Bdb):
text.tag_add("BREAK", "insert linestart", "insert lineend +1char") text.tag_add("BREAK", "insert linestart", "insert lineend +1char")
# A literal copy of Bdb.set_break() without the print statement at the end # A literal copy of Bdb.set_break() without the print statement at the end
def set_break(self, filename, lineno, temporary=0, cond = None): #def set_break(self, filename, lineno, temporary=0, cond = None):
import linecache # Import as late as possible # import linecache # Import as late as possible
filename = self.canonic(filename) # filename = self.canonic(filename)
line = linecache.getline(filename, lineno) # line = linecache.getline(filename, lineno)
if not line: # if not line:
return 'That line does not exist!' # return 'That line does not exist!'
if not self.breaks.has_key(filename): # if not self.breaks.has_key(filename):
self.breaks[filename] = [] # self.breaks[filename] = []
list = self.breaks[filename] # list = self.breaks[filename]
if not lineno in list: # if not lineno in list:
list.append(lineno) # list.append(lineno)
bp = bdb.Breakpoint(filename, lineno, temporary, cond) # bp = bdb.Breakpoint(filename, lineno, temporary, cond)
This diff is collapsed.
"""Support for remote Python debugging.
Some ASCII art to describe the structure:
IN PYTHON SUBPROCESS # IN IDLE PROCESS
#
# oid='gui_adapter'
+----------+ # +------------+ +-----+
| GUIProxy |--remote#call-->| GUIAdapter |--calls-->| GUI |
+-----+--calls-->+----------+ # +------------+ +-----+
| Idb | # /
+-----+<-calls--+------------+ # +----------+<--calls-/
| IdbAdapter |<--remote#call--| IdbProxy |
+------------+ # +----------+
oid='idb_adapter' #
The purpose of the Proxy and Adapter classes is to translate certain
arguments and return values that cannot be transported through the RPC
barrier, in particular frame and traceback objects.
"""
import sys
import rpc
import Debugger
# In the PYTHON subprocess
frametable = {}
dicttable = {}
codetable = {}
def wrap_frame(frame):
fid = id(frame)
frametable[fid] = frame
return fid
def wrap_info(info):
if info is None:
return None
else:
return None # XXX for now
class GUIProxy:
def __init__(self, conn, oid):
self.conn = conn
self.oid = oid
def interaction(self, message, frame, info=None):
self.conn.remotecall(self.oid, "interaction",
(message, wrap_frame(frame), wrap_info(info)),
{})
class IdbAdapter:
def __init__(self, idb):
self.idb = idb
def set_step(self):
self.idb.set_step()
def set_quit(self):
self.idb.set_quit()
def set_continue(self):
self.idb.set_continue()
def set_next(self, fid):
frame = frametable[fid]
self.idb.set_next(frame)
def set_return(self, fid):
frame = frametable[fid]
self.idb.set_return(frame)
def get_stack(self, fid, tbid):
##print >>sys.__stderr__, "get_stack(%s, %s)" % (`fid`, `tbid`)
frame = frametable[fid]
tb = None # XXX for now
stack, i = self.idb.get_stack(frame, tb)
##print >>sys.__stderr__, "get_stack() ->", stack
stack = [(wrap_frame(frame), k) for frame, k in stack]
##print >>sys.__stderr__, "get_stack() ->", stack
return stack, i
def run(self, cmd):
import __main__
self.idb.run(cmd, __main__.__dict__)
def frame_attr(self, fid, name):
frame = frametable[fid]
return getattr(frame, name)
def frame_globals(self, fid):
frame = frametable[fid]
dict = frame.f_globals
did = id(dict)
dicttable[did] = dict
return did
def frame_locals(self, fid):
frame = frametable[fid]
dict = frame.f_locals
did = id(dict)
dicttable[did] = dict
return did
def frame_code(self, fid):
frame = frametable[fid]
code = frame.f_code
cid = id(code)
codetable[cid] = code
return cid
def code_name(self, cid):
code = codetable[cid]
return code.co_name
def code_filename(self, cid):
code = codetable[cid]
return code.co_filename
def dict_keys(self, did):
dict = dicttable[did]
return dict.keys()
def dict_item(self, did, key):
dict = dicttable[did]
value = dict[key]
try:
# Test for picklability
import cPickle
cPickle.dumps(value)
except:
value = None
return value
def start_debugger(conn, gui_oid):
#
# launched in the python subprocess
#
gui = GUIProxy(conn, gui_oid)
idb = Debugger.Idb(gui)
ada = IdbAdapter(idb)
ada_oid = "idb_adapter"
conn.register(ada_oid, ada)
return ada_oid
# In the IDLE process
class FrameProxy:
def __init__(self, conn, fid):
self._conn = conn
self._fid = fid
self._oid = "idb_adapter"
self._dictcache = {}
def __getattr__(self, name):
if name[:1] == "_":
raise AttributeError, name
if name == "f_code":
return self._get_f_code()
if name == "f_globals":
return self._get_f_globals()
if name == "f_locals":
return self._get_f_locals()
return self._conn.remotecall(self._oid, "frame_attr",
(self._fid, name), {})
def _get_f_code(self):
cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})
return CodeProxy(self._conn, self._oid, cid)
def _get_f_globals(self):
did = self._conn.remotecall(self._oid, "frame_globals",
(self._fid,), {})
return self._get_dict_proxy(did)
def _get_f_locals(self):
did = self._conn.remotecall(self._oid, "frame_locals",
(self._fid,), {})
return self._get_dict_proxy(did)
def _get_dict_proxy(self, did):
if self._dictcache.has_key(did):
return self._dictcache[did]
dp = DictProxy(self._conn, self._oid, did)
self._dictcache[did] = dp
return dp
class CodeProxy:
def __init__(self, conn, oid, cid):
self._conn = conn
self._oid = oid
self._cid = cid
def __getattr__(self, name):
if name == "co_name":
return self._conn.remotecall(self._oid, "code_name",
(self._cid,), {})
if name == "co_filename":
return self._conn.remotecall(self._oid, "code_filename",
(self._cid,), {})
class DictProxy:
def __init__(self, conn, oid, did):
self._conn = conn
self._oid = oid
self._did = did
def keys(self):
return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {})
def __getitem__(self, key):
return self._conn.remotecall(self._oid, "dict_item",
(self._did, key), {})
def __getattr__(self, name):
##print >>sys.__stderr__, "failed DictProxy.__getattr__:", name
raise AttributeError, name
class GUIAdaper:
def __init__(self, conn, gui):
self.conn = conn
self.gui = gui
def interaction(self, message, fid, iid):
print "interaction(%s, %s, %s)" % (`message`, `fid`, `iid`)
frame = FrameProxy(self.conn, fid)
info = None # XXX for now
self.gui.interaction(message, frame, info)
class IdbProxy:
def __init__(self, conn, oid):
self.oid = oid
self.conn = conn
def call(self, methodname, *args, **kwargs):
##print "call %s %s %s" % (methodname, args, kwargs)
value = self.conn.remotecall(self.oid, methodname, args, kwargs)
##print "return %s" % `value`
return value
def run(self, cmd, locals):
# Ignores locals on purpose!
self.call("run", cmd)
def get_stack(self, frame, tb):
stack, i = self.call("get_stack", frame._fid, None)
stack = [(FrameProxy(self.conn, fid), k) for fid, k in stack]
return stack, i
def set_continue(self):
self.call("set_continue")
def set_step(self):
self.call("set_step")
def set_next(self, frame):
self.call("set_next", frame._fid)
def set_return(self, frame):
self.call("set_return", frame._fid)
def set_quit(self):
self.call("set_quit")
def start_remote_debugger(conn, pyshell):
#
# instruct the (remote) subprocess to create
# a debugger instance, and lets it know that
# the local GUIAdapter called "gui_adapter"
# is waiting notification of debugging events
#
ada_oid = "gui_adapter"
idb_oid = conn.remotecall("exec", "start_debugger", (ada_oid,), {})
idb = IdbProxy(conn, idb_oid)
gui = Debugger.Debugger(pyshell, idb)
ada = GUIAdaper(conn, gui)
conn.register(ada_oid, ada)
return gui
import rpc
def remote_object_tree_item(item):
wrapper = WrappedObjectTreeItem(item)
oid = id(wrapper)
rpc.objecttable[oid] = wrapper
return oid
class WrappedObjectTreeItem:
# Lives in PYTHON subprocess
def __init__(self, item):
self.__item = item
def __getattr__(self, name):
value = getattr(self.__item, name)
return value
def _GetSubList(self):
list = self.__item._GetSubList()
return map(remote_object_tree_item, list)
class StubObjectTreeItem:
# Lives in IDLE process
def __init__(self, sockio, oid):
self.sockio = sockio
self.oid = oid
def __getattr__(self, name):
value = rpc.MethodProxy(self.sockio, self.oid, name)
return value
def _GetSubList(self):
list = self.sockio.remotecall(self.oid, "_GetSubList", (), {})
return [StubObjectTreeItem(self.sockio, oid) for oid in list]
...@@ -14,6 +14,14 @@ namespace. Output goes to the shell window. ...@@ -14,6 +14,14 @@ namespace. Output goes to the shell window.
- Run module (Control-F5) does the same but executes the module's - Run module (Control-F5) does the same but executes the module's
code in the __main__ namespace. code in the __main__ namespace.
XXX Redesign this interface (yet again) as follows:
- Present a dialog box for ``Run script''
- Allow specify command line arguments in the dialog box
- Restart the interpreter when running a script
""" """
import sys import sys
...@@ -25,9 +33,9 @@ indent_message = """Error: Inconsistent indentation detected! ...@@ -25,9 +33,9 @@ indent_message = """Error: Inconsistent indentation detected!
This means that either: This means that either:
(1) your indentation is outright incorrect (easy to fix), or 1) your indentation is outright incorrect (easy to fix), or
(2) your indentation mixes tabs and spaces in a way that depends on \ 2) your indentation mixes tabs and spaces in a way that depends on \
how many spaces a tab is worth. how many spaces a tab is worth.
To fix case 2, change all tabs to spaces by using Select All followed \ To fix case 2, change all tabs to spaces by using Select All followed \
...@@ -105,28 +113,31 @@ class ScriptBinding: ...@@ -105,28 +113,31 @@ class ScriptBinding:
return 1 return 1
def import_module_event(self, event): def import_module_event(self, event):
flist = self.editwin.flist
shell = flist.open_shell()
interp = shell.interp
filename = self.getfilename() filename = self.getfilename()
if not filename: if not filename:
return return
modname, ext = os.path.splitext(os.path.basename(filename)) modname, ext = os.path.splitext(os.path.basename(filename))
if sys.modules.has_key(modname):
mod = sys.modules[modname]
else:
mod = imp.new_module(modname)
sys.modules[modname] = mod
mod.__file__ = filename
setattr(sys.modules['__main__'], modname, mod)
dir = os.path.dirname(filename) dir = os.path.dirname(filename)
dir = os.path.normpath(os.path.abspath(dir)) dir = os.path.normpath(os.path.abspath(dir))
if dir not in sys.path:
sys.path.insert(0, dir)
flist = self.editwin.flist interp.runcode("""if 1:
shell = flist.open_shell() import sys as _sys
interp = shell.interp if %s not in _sys.path:
interp.runcode("reload(%s)" % modname) _sys.path.insert(0, %s)
if _sys.modules.get(%s):
del _sys
import %s
reload(%s)
else:
del _sys
import %s
\n""" % (`dir`, `dir`, `modname`, modname, modname, modname))
def run_script_event(self, event): def run_script_event(self, event):
filename = self.getfilename() filename = self.getfilename()
...@@ -136,10 +147,16 @@ class ScriptBinding: ...@@ -136,10 +147,16 @@ class ScriptBinding:
flist = self.editwin.flist flist = self.editwin.flist
shell = flist.open_shell() shell = flist.open_shell()
interp = shell.interp interp = shell.interp
if (not sys.argv or # XXX Too often this discards arguments the user just set...
os.path.basename(sys.argv[0]) != os.path.basename(filename)): interp.runcommand("""if 1:
# XXX Too often this discards arguments the user just set... _filename = %s
sys.argv = [filename] import sys as _sys
from os.path import basename as _basename
if (not _sys.argv or
_basename(_sys.argv[0]) != _basename(_filename)):
_sys.argv = [_filename]
del _filename, _sys, _basename
\n""" % `filename`)
interp.execfile(filename) interp.execfile(filename)
def getfilename(self): def getfilename(self):
......
This diff is collapsed.
import sys
import rpc
def main():
port = 8833
if sys.argv[1:]:
port = int(sys.argv[1])
sys.argv[:] = [""]
addr = ("localhost", port)
svr = rpc.RPCServer(addr, MyHandler)
svr.handle_request() # A single request only
class MyHandler(rpc.RPCHandler):
def handle(self):
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.handle(self)
class Executive:
def __init__(self, rpchandler):
self.conn = rpchandler
import __main__
self.locals = __main__.__dict__
def runcode(self, code):
exec code in self.locals
def start_debugger(self, gui_oid):
import RemoteDebugger
return RemoteDebugger.start_debugger(self.conn, gui_oid)
def stackviewer(self, flist_oid=None):
if not hasattr(sys, "last_traceback"):
return None
flist = None
if flist_oid is not None:
flist = self.conn.get_remote_proxy(flist_oid)
import RemoteObjectBrowser
import StackViewer
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)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment