python-2.5.2/win32/Lib/idlelib/RemoteDebugger.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     1 """Support for remote Python debugging.
       
     2 
       
     3 Some ASCII art to describe the structure:
       
     4 
       
     5        IN PYTHON SUBPROCESS          #             IN IDLE PROCESS
       
     6                                      #
       
     7                                      #        oid='gui_adapter'
       
     8                  +----------+        #       +------------+          +-----+
       
     9                  | GUIProxy |--remote#call-->| GUIAdapter |--calls-->| GUI |
       
    10 +-----+--calls-->+----------+        #       +------------+          +-----+
       
    11 | Idb |                               #                             /
       
    12 +-----+<-calls--+------------+         #      +----------+<--calls-/
       
    13                 | IdbAdapter |<--remote#call--| IdbProxy |
       
    14                 +------------+         #      +----------+
       
    15                 oid='idb_adapter'      #
       
    16 
       
    17 The purpose of the Proxy and Adapter classes is to translate certain
       
    18 arguments and return values that cannot be transported through the RPC
       
    19 barrier, in particular frame and traceback objects.
       
    20 
       
    21 """
       
    22 
       
    23 import sys
       
    24 import types
       
    25 import rpc
       
    26 import Debugger
       
    27 
       
    28 debugging = 0
       
    29 
       
    30 idb_adap_oid = "idb_adapter"
       
    31 gui_adap_oid = "gui_adapter"
       
    32 
       
    33 #=======================================
       
    34 #
       
    35 # In the PYTHON subprocess:
       
    36 
       
    37 frametable = {}
       
    38 dicttable = {}
       
    39 codetable = {}
       
    40 tracebacktable = {}
       
    41 
       
    42 def wrap_frame(frame):
       
    43     fid = id(frame)
       
    44     frametable[fid] = frame
       
    45     return fid
       
    46 
       
    47 def wrap_info(info):
       
    48     "replace info[2], a traceback instance, by its ID"
       
    49     if info is None:
       
    50         return None
       
    51     else:
       
    52         traceback = info[2]
       
    53         assert isinstance(traceback, types.TracebackType)
       
    54         traceback_id = id(traceback)
       
    55         tracebacktable[traceback_id] = traceback
       
    56         modified_info = (info[0], info[1], traceback_id)
       
    57         return modified_info
       
    58 
       
    59 class GUIProxy:
       
    60 
       
    61     def __init__(self, conn, gui_adap_oid):
       
    62         self.conn = conn
       
    63         self.oid = gui_adap_oid
       
    64 
       
    65     def interaction(self, message, frame, info=None):
       
    66         # calls rpc.SocketIO.remotecall() via run.MyHandler instance
       
    67         # pass frame and traceback object IDs instead of the objects themselves
       
    68         self.conn.remotecall(self.oid, "interaction",
       
    69                              (message, wrap_frame(frame), wrap_info(info)),
       
    70                              {})
       
    71 
       
    72 class IdbAdapter:
       
    73 
       
    74     def __init__(self, idb):
       
    75         self.idb = idb
       
    76 
       
    77     #----------called by an IdbProxy----------
       
    78 
       
    79     def set_step(self):
       
    80         self.idb.set_step()
       
    81 
       
    82     def set_quit(self):
       
    83         self.idb.set_quit()
       
    84 
       
    85     def set_continue(self):
       
    86         self.idb.set_continue()
       
    87 
       
    88     def set_next(self, fid):
       
    89         frame = frametable[fid]
       
    90         self.idb.set_next(frame)
       
    91 
       
    92     def set_return(self, fid):
       
    93         frame = frametable[fid]
       
    94         self.idb.set_return(frame)
       
    95 
       
    96     def get_stack(self, fid, tbid):
       
    97         ##print >>sys.__stderr__, "get_stack(%r, %r)" % (fid, tbid)
       
    98         frame = frametable[fid]
       
    99         if tbid is None:
       
   100             tb = None
       
   101         else:
       
   102             tb = tracebacktable[tbid]
       
   103         stack, i = self.idb.get_stack(frame, tb)
       
   104         ##print >>sys.__stderr__, "get_stack() ->", stack
       
   105         stack = [(wrap_frame(frame), k) for frame, k in stack]
       
   106         ##print >>sys.__stderr__, "get_stack() ->", stack
       
   107         return stack, i
       
   108 
       
   109     def run(self, cmd):
       
   110         import __main__
       
   111         self.idb.run(cmd, __main__.__dict__)
       
   112 
       
   113     def set_break(self, filename, lineno):
       
   114         msg = self.idb.set_break(filename, lineno)
       
   115         return msg
       
   116 
       
   117     def clear_break(self, filename, lineno):
       
   118         msg = self.idb.clear_break(filename, lineno)
       
   119         return msg
       
   120 
       
   121     def clear_all_file_breaks(self, filename):
       
   122         msg = self.idb.clear_all_file_breaks(filename)
       
   123         return msg
       
   124 
       
   125     #----------called by a FrameProxy----------
       
   126 
       
   127     def frame_attr(self, fid, name):
       
   128         frame = frametable[fid]
       
   129         return getattr(frame, name)
       
   130 
       
   131     def frame_globals(self, fid):
       
   132         frame = frametable[fid]
       
   133         dict = frame.f_globals
       
   134         did = id(dict)
       
   135         dicttable[did] = dict
       
   136         return did
       
   137 
       
   138     def frame_locals(self, fid):
       
   139         frame = frametable[fid]
       
   140         dict = frame.f_locals
       
   141         did = id(dict)
       
   142         dicttable[did] = dict
       
   143         return did
       
   144 
       
   145     def frame_code(self, fid):
       
   146         frame = frametable[fid]
       
   147         code = frame.f_code
       
   148         cid = id(code)
       
   149         codetable[cid] = code
       
   150         return cid
       
   151 
       
   152     #----------called by a CodeProxy----------
       
   153 
       
   154     def code_name(self, cid):
       
   155         code = codetable[cid]
       
   156         return code.co_name
       
   157 
       
   158     def code_filename(self, cid):
       
   159         code = codetable[cid]
       
   160         return code.co_filename
       
   161 
       
   162     #----------called by a DictProxy----------
       
   163 
       
   164     def dict_keys(self, did):
       
   165         dict = dicttable[did]
       
   166         return dict.keys()
       
   167 
       
   168     def dict_item(self, did, key):
       
   169         dict = dicttable[did]
       
   170         value = dict[key]
       
   171         value = repr(value)
       
   172         return value
       
   173 
       
   174 #----------end class IdbAdapter----------
       
   175 
       
   176 
       
   177 def start_debugger(rpchandler, gui_adap_oid):
       
   178     """Start the debugger and its RPC link in the Python subprocess
       
   179 
       
   180     Start the subprocess side of the split debugger and set up that side of the
       
   181     RPC link by instantiating the GUIProxy, Idb debugger, and IdbAdapter
       
   182     objects and linking them together.  Register the IdbAdapter with the
       
   183     RPCServer to handle RPC requests from the split debugger GUI via the
       
   184     IdbProxy.
       
   185 
       
   186     """
       
   187     gui_proxy = GUIProxy(rpchandler, gui_adap_oid)
       
   188     idb = Debugger.Idb(gui_proxy)
       
   189     idb_adap = IdbAdapter(idb)
       
   190     rpchandler.register(idb_adap_oid, idb_adap)
       
   191     return idb_adap_oid
       
   192 
       
   193 
       
   194 #=======================================
       
   195 #
       
   196 # In the IDLE process:
       
   197 
       
   198 
       
   199 class FrameProxy:
       
   200 
       
   201     def __init__(self, conn, fid):
       
   202         self._conn = conn
       
   203         self._fid = fid
       
   204         self._oid = "idb_adapter"
       
   205         self._dictcache = {}
       
   206 
       
   207     def __getattr__(self, name):
       
   208         if name[:1] == "_":
       
   209             raise AttributeError, name
       
   210         if name == "f_code":
       
   211             return self._get_f_code()
       
   212         if name == "f_globals":
       
   213             return self._get_f_globals()
       
   214         if name == "f_locals":
       
   215             return self._get_f_locals()
       
   216         return self._conn.remotecall(self._oid, "frame_attr",
       
   217                                      (self._fid, name), {})
       
   218 
       
   219     def _get_f_code(self):
       
   220         cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})
       
   221         return CodeProxy(self._conn, self._oid, cid)
       
   222 
       
   223     def _get_f_globals(self):
       
   224         did = self._conn.remotecall(self._oid, "frame_globals",
       
   225                                     (self._fid,), {})
       
   226         return self._get_dict_proxy(did)
       
   227 
       
   228     def _get_f_locals(self):
       
   229         did = self._conn.remotecall(self._oid, "frame_locals",
       
   230                                     (self._fid,), {})
       
   231         return self._get_dict_proxy(did)
       
   232 
       
   233     def _get_dict_proxy(self, did):
       
   234         if self._dictcache.has_key(did):
       
   235             return self._dictcache[did]
       
   236         dp = DictProxy(self._conn, self._oid, did)
       
   237         self._dictcache[did] = dp
       
   238         return dp
       
   239 
       
   240 
       
   241 class CodeProxy:
       
   242 
       
   243     def __init__(self, conn, oid, cid):
       
   244         self._conn = conn
       
   245         self._oid = oid
       
   246         self._cid = cid
       
   247 
       
   248     def __getattr__(self, name):
       
   249         if name == "co_name":
       
   250             return self._conn.remotecall(self._oid, "code_name",
       
   251                                          (self._cid,), {})
       
   252         if name == "co_filename":
       
   253             return self._conn.remotecall(self._oid, "code_filename",
       
   254                                          (self._cid,), {})
       
   255 
       
   256 
       
   257 class DictProxy:
       
   258 
       
   259     def __init__(self, conn, oid, did):
       
   260         self._conn = conn
       
   261         self._oid = oid
       
   262         self._did = did
       
   263 
       
   264     def keys(self):
       
   265         return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {})
       
   266 
       
   267     def __getitem__(self, key):
       
   268         return self._conn.remotecall(self._oid, "dict_item",
       
   269                                      (self._did, key), {})
       
   270 
       
   271     def __getattr__(self, name):
       
   272         ##print >>sys.__stderr__, "failed DictProxy.__getattr__:", name
       
   273         raise AttributeError, name
       
   274 
       
   275 
       
   276 class GUIAdapter:
       
   277 
       
   278     def __init__(self, conn, gui):
       
   279         self.conn = conn
       
   280         self.gui = gui
       
   281 
       
   282     def interaction(self, message, fid, modified_info):
       
   283         ##print "interaction: (%s, %s, %s)" % (message, fid, modified_info)
       
   284         frame = FrameProxy(self.conn, fid)
       
   285         self.gui.interaction(message, frame, modified_info)
       
   286 
       
   287 
       
   288 class IdbProxy:
       
   289 
       
   290     def __init__(self, conn, shell, oid):
       
   291         self.oid = oid
       
   292         self.conn = conn
       
   293         self.shell = shell
       
   294 
       
   295     def call(self, methodname, *args, **kwargs):
       
   296         ##print "**IdbProxy.call %s %s %s" % (methodname, args, kwargs)
       
   297         value = self.conn.remotecall(self.oid, methodname, args, kwargs)
       
   298         ##print "**IdbProxy.call %s returns %r" % (methodname, value)
       
   299         return value
       
   300 
       
   301     def run(self, cmd, locals):
       
   302         # Ignores locals on purpose!
       
   303         seq = self.conn.asyncqueue(self.oid, "run", (cmd,), {})
       
   304         self.shell.interp.active_seq = seq
       
   305 
       
   306     def get_stack(self, frame, tbid):
       
   307         # passing frame and traceback IDs, not the objects themselves
       
   308         stack, i = self.call("get_stack", frame._fid, tbid)
       
   309         stack = [(FrameProxy(self.conn, fid), k) for fid, k in stack]
       
   310         return stack, i
       
   311 
       
   312     def set_continue(self):
       
   313         self.call("set_continue")
       
   314 
       
   315     def set_step(self):
       
   316         self.call("set_step")
       
   317 
       
   318     def set_next(self, frame):
       
   319         self.call("set_next", frame._fid)
       
   320 
       
   321     def set_return(self, frame):
       
   322         self.call("set_return", frame._fid)
       
   323 
       
   324     def set_quit(self):
       
   325         self.call("set_quit")
       
   326 
       
   327     def set_break(self, filename, lineno):
       
   328         msg = self.call("set_break", filename, lineno)
       
   329         return msg
       
   330 
       
   331     def clear_break(self, filename, lineno):
       
   332         msg = self.call("clear_break", filename, lineno)
       
   333         return msg
       
   334 
       
   335     def clear_all_file_breaks(self, filename):
       
   336         msg = self.call("clear_all_file_breaks", filename)
       
   337         return msg
       
   338 
       
   339 def start_remote_debugger(rpcclt, pyshell):
       
   340     """Start the subprocess debugger, initialize the debugger GUI and RPC link
       
   341 
       
   342     Request the RPCServer start the Python subprocess debugger and link.  Set
       
   343     up the Idle side of the split debugger by instantiating the IdbProxy,
       
   344     debugger GUI, and debugger GUIAdapter objects and linking them together.
       
   345 
       
   346     Register the GUIAdapter with the RPCClient to handle debugger GUI
       
   347     interaction requests coming from the subprocess debugger via the GUIProxy.
       
   348 
       
   349     The IdbAdapter will pass execution and environment requests coming from the
       
   350     Idle debugger GUI to the subprocess debugger via the IdbProxy.
       
   351 
       
   352     """
       
   353     global idb_adap_oid
       
   354 
       
   355     idb_adap_oid = rpcclt.remotecall("exec", "start_the_debugger",\
       
   356                                    (gui_adap_oid,), {})
       
   357     idb_proxy = IdbProxy(rpcclt, pyshell, idb_adap_oid)
       
   358     gui = Debugger.Debugger(pyshell, idb_proxy)
       
   359     gui_adap = GUIAdapter(rpcclt, gui)
       
   360     rpcclt.register(gui_adap_oid, gui_adap)
       
   361     return gui
       
   362 
       
   363 def close_remote_debugger(rpcclt):
       
   364     """Shut down subprocess debugger and Idle side of debugger RPC link
       
   365 
       
   366     Request that the RPCServer shut down the subprocess debugger and link.
       
   367     Unregister the GUIAdapter, which will cause a GC on the Idle process
       
   368     debugger and RPC link objects.  (The second reference to the debugger GUI
       
   369     is deleted in PyShell.close_remote_debugger().)
       
   370 
       
   371     """
       
   372     close_subprocess_debugger(rpcclt)
       
   373     rpcclt.unregister(gui_adap_oid)
       
   374 
       
   375 def close_subprocess_debugger(rpcclt):
       
   376     rpcclt.remotecall("exec", "stop_the_debugger", (idb_adap_oid,), {})
       
   377 
       
   378 def restart_subprocess_debugger(rpcclt):
       
   379     idb_adap_oid_ret = rpcclt.remotecall("exec", "start_the_debugger",\
       
   380                                          (gui_adap_oid,), {})
       
   381     assert idb_adap_oid_ret == idb_adap_oid, 'Idb restarted with different oid'