PyXR

c:\python24\lib \ idlelib \ RemoteDebugger.py



0001 """Support for remote Python debugging.
0002 
0003 Some ASCII art to describe the structure:
0004 
0005        IN PYTHON SUBPROCESS          #             IN IDLE PROCESS
0006                                      #
0007                                      #        oid='gui_adapter'
0008                  +----------+        #       +------------+          +-----+
0009                  | GUIProxy |--remote#call-->| GUIAdapter |--calls-->| GUI |
0010 +-----+--calls-->+----------+        #       +------------+          +-----+
0011 | Idb |                               #                             /
0012 +-----+<-calls--+------------+         #      +----------+<--calls-/
0013                 | IdbAdapter |<--remote#call--| IdbProxy |
0014                 +------------+         #      +----------+
0015                 oid='idb_adapter'      #
0016 
0017 The purpose of the Proxy and Adapter classes is to translate certain
0018 arguments and return values that cannot be transported through the RPC
0019 barrier, in particular frame and traceback objects.
0020 
0021 """
0022 
0023 import sys
0024 import types
0025 import rpc
0026 import Debugger
0027 
0028 debugging = 0
0029 
0030 idb_adap_oid = "idb_adapter"
0031 gui_adap_oid = "gui_adapter"
0032 
0033 #=======================================
0034 #
0035 # In the PYTHON subprocess:
0036 
0037 frametable = {}
0038 dicttable = {}
0039 codetable = {}
0040 tracebacktable = {}
0041 
0042 def wrap_frame(frame):
0043     fid = id(frame)
0044     frametable[fid] = frame
0045     return fid
0046 
0047 def wrap_info(info):
0048     "replace info[2], a traceback instance, by its ID"
0049     if info is None:
0050         return None
0051     else:
0052         traceback = info[2]
0053         assert isinstance(traceback, types.TracebackType)
0054         traceback_id = id(traceback)
0055         tracebacktable[traceback_id] = traceback
0056         modified_info = (info[0], info[1], traceback_id)
0057         return modified_info
0058 
0059 class GUIProxy:
0060 
0061     def __init__(self, conn, gui_adap_oid):
0062         self.conn = conn
0063         self.oid = gui_adap_oid
0064 
0065     def interaction(self, message, frame, info=None):
0066         # calls rpc.SocketIO.remotecall() via run.MyHandler instance
0067         # pass frame and traceback object IDs instead of the objects themselves
0068         self.conn.remotecall(self.oid, "interaction",
0069                              (message, wrap_frame(frame), wrap_info(info)),
0070                              {})
0071 
0072 class IdbAdapter:
0073 
0074     def __init__(self, idb):
0075         self.idb = idb
0076 
0077     #----------called by an IdbProxy----------
0078 
0079     def set_step(self):
0080         self.idb.set_step()
0081 
0082     def set_quit(self):
0083         self.idb.set_quit()
0084 
0085     def set_continue(self):
0086         self.idb.set_continue()
0087 
0088     def set_next(self, fid):
0089         frame = frametable[fid]
0090         self.idb.set_next(frame)
0091 
0092     def set_return(self, fid):
0093         frame = frametable[fid]
0094         self.idb.set_return(frame)
0095 
0096     def get_stack(self, fid, tbid):
0097         ##print >>sys.__stderr__, "get_stack(%r, %r)" % (fid, tbid)
0098         frame = frametable[fid]
0099         if tbid is None:
0100             tb = None
0101         else:
0102             tb = tracebacktable[tbid]
0103         stack, i = self.idb.get_stack(frame, tb)
0104         ##print >>sys.__stderr__, "get_stack() ->", stack
0105         stack = [(wrap_frame(frame), k) for frame, k in stack]
0106         ##print >>sys.__stderr__, "get_stack() ->", stack
0107         return stack, i
0108 
0109     def run(self, cmd):
0110         import __main__
0111         self.idb.run(cmd, __main__.__dict__)
0112 
0113     def set_break(self, filename, lineno):
0114         msg = self.idb.set_break(filename, lineno)
0115         return msg
0116 
0117     def clear_break(self, filename, lineno):
0118         msg = self.idb.clear_break(filename, lineno)
0119         return msg
0120 
0121     def clear_all_file_breaks(self, filename):
0122         msg = self.idb.clear_all_file_breaks(filename)
0123         return msg
0124 
0125     #----------called by a FrameProxy----------
0126 
0127     def frame_attr(self, fid, name):
0128         frame = frametable[fid]
0129         return getattr(frame, name)
0130 
0131     def frame_globals(self, fid):
0132         frame = frametable[fid]
0133         dict = frame.f_globals
0134         did = id(dict)
0135         dicttable[did] = dict
0136         return did
0137 
0138     def frame_locals(self, fid):
0139         frame = frametable[fid]
0140         dict = frame.f_locals
0141         did = id(dict)
0142         dicttable[did] = dict
0143         return did
0144 
0145     def frame_code(self, fid):
0146         frame = frametable[fid]
0147         code = frame.f_code
0148         cid = id(code)
0149         codetable[cid] = code
0150         return cid
0151 
0152     #----------called by a CodeProxy----------
0153 
0154     def code_name(self, cid):
0155         code = codetable[cid]
0156         return code.co_name
0157 
0158     def code_filename(self, cid):
0159         code = codetable[cid]
0160         return code.co_filename
0161 
0162     #----------called by a DictProxy----------
0163 
0164     def dict_keys(self, did):
0165         dict = dicttable[did]
0166         return dict.keys()
0167 
0168     def dict_item(self, did, key):
0169         dict = dicttable[did]
0170         value = dict[key]
0171         value = repr(value)
0172         return value
0173 
0174 #----------end class IdbAdapter----------
0175 
0176 
0177 def start_debugger(rpchandler, gui_adap_oid):
0178     """Start the debugger and its RPC link in the Python subprocess
0179 
0180     Start the subprocess side of the split debugger and set up that side of the
0181     RPC link by instantiating the GUIProxy, Idb debugger, and IdbAdapter
0182     objects and linking them together.  Register the IdbAdapter with the
0183     RPCServer to handle RPC requests from the split debugger GUI via the
0184     IdbProxy.
0185 
0186     """
0187     gui_proxy = GUIProxy(rpchandler, gui_adap_oid)
0188     idb = Debugger.Idb(gui_proxy)
0189     idb_adap = IdbAdapter(idb)
0190     rpchandler.register(idb_adap_oid, idb_adap)
0191     return idb_adap_oid
0192 
0193 
0194 #=======================================
0195 #
0196 # In the IDLE process:
0197 
0198 
0199 class FrameProxy:
0200 
0201     def __init__(self, conn, fid):
0202         self._conn = conn
0203         self._fid = fid
0204         self._oid = "idb_adapter"
0205         self._dictcache = {}
0206 
0207     def __getattr__(self, name):
0208         if name[:1] == "_":
0209             raise AttributeError, name
0210         if name == "f_code":
0211             return self._get_f_code()
0212         if name == "f_globals":
0213             return self._get_f_globals()
0214         if name == "f_locals":
0215             return self._get_f_locals()
0216         return self._conn.remotecall(self._oid, "frame_attr",
0217                                      (self._fid, name), {})
0218 
0219     def _get_f_code(self):
0220         cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})
0221         return CodeProxy(self._conn, self._oid, cid)
0222 
0223     def _get_f_globals(self):
0224         did = self._conn.remotecall(self._oid, "frame_globals",
0225                                     (self._fid,), {})
0226         return self._get_dict_proxy(did)
0227 
0228     def _get_f_locals(self):
0229         did = self._conn.remotecall(self._oid, "frame_locals",
0230                                     (self._fid,), {})
0231         return self._get_dict_proxy(did)
0232 
0233     def _get_dict_proxy(self, did):
0234         if self._dictcache.has_key(did):
0235             return self._dictcache[did]
0236         dp = DictProxy(self._conn, self._oid, did)
0237         self._dictcache[did] = dp
0238         return dp
0239 
0240 
0241 class CodeProxy:
0242 
0243     def __init__(self, conn, oid, cid):
0244         self._conn = conn
0245         self._oid = oid
0246         self._cid = cid
0247 
0248     def __getattr__(self, name):
0249         if name == "co_name":
0250             return self._conn.remotecall(self._oid, "code_name",
0251                                          (self._cid,), {})
0252         if name == "co_filename":
0253             return self._conn.remotecall(self._oid, "code_filename",
0254                                          (self._cid,), {})
0255 
0256 
0257 class DictProxy:
0258 
0259     def __init__(self, conn, oid, did):
0260         self._conn = conn
0261         self._oid = oid
0262         self._did = did
0263 
0264     def keys(self):
0265         return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {})
0266 
0267     def __getitem__(self, key):
0268         return self._conn.remotecall(self._oid, "dict_item",
0269                                      (self._did, key), {})
0270 
0271     def __getattr__(self, name):
0272         ##print >>sys.__stderr__, "failed DictProxy.__getattr__:", name
0273         raise AttributeError, name
0274 
0275 
0276 class GUIAdapter:
0277 
0278     def __init__(self, conn, gui):
0279         self.conn = conn
0280         self.gui = gui
0281 
0282     def interaction(self, message, fid, modified_info):
0283         ##print "interaction: (%s, %s, %s)" % (message, fid, modified_info)
0284         frame = FrameProxy(self.conn, fid)
0285         self.gui.interaction(message, frame, modified_info)
0286 
0287 
0288 class IdbProxy:
0289 
0290     def __init__(self, conn, shell, oid):
0291         self.oid = oid
0292         self.conn = conn
0293         self.shell = shell
0294 
0295     def call(self, methodname, *args, **kwargs):
0296         ##print "**IdbProxy.call %s %s %s" % (methodname, args, kwargs)
0297         value = self.conn.remotecall(self.oid, methodname, args, kwargs)
0298         ##print "**IdbProxy.call %s returns %r" % (methodname, value)
0299         return value
0300 
0301     def run(self, cmd, locals):
0302         # Ignores locals on purpose!
0303         seq = self.conn.asyncqueue(self.oid, "run", (cmd,), {})
0304         self.shell.interp.active_seq = seq
0305 
0306     def get_stack(self, frame, tbid):
0307         # passing frame and traceback IDs, not the objects themselves
0308         stack, i = self.call("get_stack", frame._fid, tbid)
0309         stack = [(FrameProxy(self.conn, fid), k) for fid, k in stack]
0310         return stack, i
0311 
0312     def set_continue(self):
0313         self.call("set_continue")
0314 
0315     def set_step(self):
0316         self.call("set_step")
0317 
0318     def set_next(self, frame):
0319         self.call("set_next", frame._fid)
0320 
0321     def set_return(self, frame):
0322         self.call("set_return", frame._fid)
0323 
0324     def set_quit(self):
0325         self.call("set_quit")
0326 
0327     def set_break(self, filename, lineno):
0328         msg = self.call("set_break", filename, lineno)
0329         return msg
0330 
0331     def clear_break(self, filename, lineno):
0332         msg = self.call("clear_break", filename, lineno)
0333         return msg
0334 
0335     def clear_all_file_breaks(self, filename):
0336         msg = self.call("clear_all_file_breaks", filename)
0337         return msg
0338 
0339 def start_remote_debugger(rpcclt, pyshell):
0340     """Start the subprocess debugger, initialize the debugger GUI and RPC link
0341 
0342     Request the RPCServer start the Python subprocess debugger and link.  Set
0343     up the Idle side of the split debugger by instantiating the IdbProxy,
0344     debugger GUI, and debugger GUIAdapter objects and linking them together.
0345 
0346     Register the GUIAdapter with the RPCClient to handle debugger GUI
0347     interaction requests coming from the subprocess debugger via the GUIProxy.
0348 
0349     The IdbAdapter will pass execution and environment requests coming from the
0350     Idle debugger GUI to the subprocess debugger via the IdbProxy.
0351 
0352     """
0353     global idb_adap_oid
0354 
0355     idb_adap_oid = rpcclt.remotecall("exec", "start_the_debugger",\
0356                                    (gui_adap_oid,), {})
0357     idb_proxy = IdbProxy(rpcclt, pyshell, idb_adap_oid)
0358     gui = Debugger.Debugger(pyshell, idb_proxy)
0359     gui_adap = GUIAdapter(rpcclt, gui)
0360     rpcclt.register(gui_adap_oid, gui_adap)
0361     return gui
0362 
0363 def close_remote_debugger(rpcclt):
0364     """Shut down subprocess debugger and Idle side of debugger RPC link
0365 
0366     Request that the RPCServer shut down the subprocess debugger and link.
0367     Unregister the GUIAdapter, which will cause a GC on the Idle process
0368     debugger and RPC link objects.  (The second reference to the debugger GUI
0369     is deleted in PyShell.close_remote_debugger().)
0370 
0371     """
0372     close_subprocess_debugger(rpcclt)
0373     rpcclt.unregister(gui_adap_oid)
0374 
0375 def close_subprocess_debugger(rpcclt):
0376     rpcclt.remotecall("exec", "stop_the_debugger", (idb_adap_oid,), {})
0377 
0378 def restart_subprocess_debugger(rpcclt):
0379     idb_adap_oid_ret = rpcclt.remotecall("exec", "start_the_debugger",\
0380                                          (gui_adap_oid,), {})
0381     assert idb_adap_oid_ret == idb_adap_oid, 'Idb restarted with different oid'
0382 

Generated by PyXR 0.9.4
SourceForge.net Logo