Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
debugger_r.py391 linesDownload Raw Back to idlelib
1"""Support for remote Python debugging.2 3Some ASCII art to describe the structure:4 5       IN PYTHON SUBPROCESS          #             IN IDLE PROCESS6                                     #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 17The purpose of the Proxy and Adapter classes is to translate certain18arguments and return values that cannot be transported through the RPC19barrier, in particular frame and traceback objects.20 21"""22import reprlib23import types24from idlelib import debugger25 26debugging = 027 28idb_adap_oid = "idb_adapter"29gui_adap_oid = "gui_adapter"30 31#=======================================32#33# In the PYTHON subprocess:34 35frametable = {}36dicttable = {}37codetable = {}38tracebacktable = {}39 40def wrap_frame(frame):41    fid = id(frame)42    frametable[fid] = frame43    return fid44 45def wrap_info(info):46    "replace info[2], a traceback instance, by its ID"47    if info is None:48        return None49    else:50        traceback = info[2]51        assert isinstance(traceback, types.TracebackType)52        traceback_id = id(traceback)53        tracebacktable[traceback_id] = traceback54        modified_info = (info[0], info[1], traceback_id)55        return modified_info56 57class GUIProxy:58 59    def __init__(self, conn, gui_adap_oid):60        self.conn = conn61        self.oid = gui_adap_oid62 63    def interaction(self, message, frame, info=None):64        # calls rpc.SocketIO.remotecall() via run.MyHandler instance65        # pass frame and traceback object IDs instead of the objects themselves66        self.conn.remotecall(self.oid, "interaction",67                             (message, wrap_frame(frame), wrap_info(info)),68                             {})69 70class IdbAdapter:71 72    def __init__(self, idb):73        self.idb = idb74 75    #----------called by an IdbProxy----------76 77    def set_step(self):78        self.idb.set_step()79 80    def set_quit(self):81        self.idb.set_quit()82 83    def set_continue(self):84        self.idb.set_continue()85 86    def set_next(self, fid):87        frame = frametable[fid]88        self.idb.set_next(frame)89 90    def set_return(self, fid):91        frame = frametable[fid]92        self.idb.set_return(frame)93 94    def get_stack(self, fid, tbid):95        frame = frametable[fid]96        if tbid is None:97            tb = None98        else:99            tb = tracebacktable[tbid]100        stack, i = self.idb.get_stack(frame, tb)101        stack = [(wrap_frame(frame2), k) for frame2, k in stack]102        return stack, i103 104    def run(self, cmd):105        import __main__106        self.idb.run(cmd, __main__.__dict__)107 108    def set_break(self, filename, lineno):109        msg = self.idb.set_break(filename, lineno)110        return msg111 112    def clear_break(self, filename, lineno):113        msg = self.idb.clear_break(filename, lineno)114        return msg115 116    def clear_all_file_breaks(self, filename):117        msg = self.idb.clear_all_file_breaks(filename)118        return msg119 120    #----------called by a FrameProxy----------121 122    def frame_attr(self, fid, name):123        frame = frametable[fid]124        return getattr(frame, name)125 126    def frame_globals(self, fid):127        frame = frametable[fid]128        gdict = frame.f_globals129        did = id(gdict)130        dicttable[did] = gdict131        return did132 133    def frame_locals(self, fid):134        frame = frametable[fid]135        ldict = frame.f_locals136        did = id(ldict)137        dicttable[did] = ldict138        return did139 140    def frame_code(self, fid):141        frame = frametable[fid]142        code = frame.f_code143        cid = id(code)144        codetable[cid] = code145        return cid146 147    #----------called by a CodeProxy----------148 149    def code_name(self, cid):150        code = codetable[cid]151        return code.co_name152 153    def code_filename(self, cid):154        code = codetable[cid]155        return code.co_filename156 157    #----------called by a DictProxy----------158 159    def dict_keys(self, did):160        raise NotImplementedError("dict_keys not public or pickleable")161##         return dicttable[did].keys()162 163    ### Needed until dict_keys type is finished and pickleable.164    # xxx finished. pickleable?165    ### Will probably need to extend rpc.py:SocketIO._proxify at that time.166    def dict_keys_list(self, did):167        return list(dicttable[did].keys())168 169    def dict_item(self, did, key):170        value = dicttable[did][key]171        return reprlib.repr(value) # Can't pickle module 'builtins'.172 173#----------end class IdbAdapter----------174 175 176def start_debugger(rpchandler, gui_adap_oid):177    """Start the debugger and its RPC link in the Python subprocess178 179    Start the subprocess side of the split debugger and set up that side of the180    RPC link by instantiating the GUIProxy, Idb debugger, and IdbAdapter181    objects and linking them together.  Register the IdbAdapter with the182    RPCServer to handle RPC requests from the split debugger GUI via the183    IdbProxy.184 185    """186    gui_proxy = GUIProxy(rpchandler, gui_adap_oid)187    idb = debugger.Idb(gui_proxy)188    idb_adap = IdbAdapter(idb)189    rpchandler.register(idb_adap_oid, idb_adap)190    return idb_adap_oid191 192 193#=======================================194#195# In the IDLE process:196 197 198class FrameProxy:199 200    def __init__(self, conn, fid):201        self._conn = conn202        self._fid = fid203        self._oid = "idb_adapter"204        self._dictcache = {}205 206    def __getattr__(self, name):207        if name[:1] == "_":208            raise AttributeError(name)209        if name == "f_code":210            return self._get_f_code()211        if name == "f_globals":212            return self._get_f_globals()213        if name == "f_locals":214            return self._get_f_locals()215        return self._conn.remotecall(self._oid, "frame_attr",216                                     (self._fid, name), {})217 218    def _get_f_code(self):219        cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})220        return CodeProxy(self._conn, self._oid, cid)221 222    def _get_f_globals(self):223        did = self._conn.remotecall(self._oid, "frame_globals",224                                    (self._fid,), {})225        return self._get_dict_proxy(did)226 227    def _get_f_locals(self):228        did = self._conn.remotecall(self._oid, "frame_locals",229                                    (self._fid,), {})230        return self._get_dict_proxy(did)231 232    def _get_dict_proxy(self, did):233        if did in self._dictcache:234            return self._dictcache[did]235        dp = DictProxy(self._conn, self._oid, did)236        self._dictcache[did] = dp237        return dp238 239 240class CodeProxy:241 242    def __init__(self, conn, oid, cid):243        self._conn = conn244        self._oid = oid245        self._cid = cid246 247    def __getattr__(self, name):248        if name == "co_name":249            return self._conn.remotecall(self._oid, "code_name",250                                         (self._cid,), {})251        if name == "co_filename":252            return self._conn.remotecall(self._oid, "code_filename",253                                         (self._cid,), {})254 255 256class DictProxy:257 258    def __init__(self, conn, oid, did):259        self._conn = conn260        self._oid = oid261        self._did = did262 263##    def keys(self):264##        return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {})265 266    # 'temporary' until dict_keys is a pickleable built-in type267    def keys(self):268        return self._conn.remotecall(self._oid,269                                     "dict_keys_list", (self._did,), {})270 271    def __getitem__(self, key):272        return self._conn.remotecall(self._oid, "dict_item",273                                     (self._did, key), {})274 275    def __getattr__(self, name):276        ##print("*** Failed DictProxy.__getattr__:", name)277        raise AttributeError(name)278 279 280class GUIAdapter:281 282    def __init__(self, conn, gui):283        self.conn = conn284        self.gui = gui285 286    def interaction(self, message, fid, modified_info):287        ##print("*** Interaction: (%s, %s, %s)" % (message, fid, modified_info))288        frame = FrameProxy(self.conn, fid)289        self.gui.interaction(message, frame, modified_info)290 291 292class IdbProxy:293 294    def __init__(self, conn, shell, oid):295        self.oid = oid296        self.conn = conn297        self.shell = shell298 299    def call(self, methodname, /, *args, **kwargs):300        ##print("*** IdbProxy.call %s %s %s" % (methodname, args, kwargs))301        value = self.conn.remotecall(self.oid, methodname, args, kwargs)302        ##print("*** IdbProxy.call %s returns %r" % (methodname, value))303        return value304 305    def run(self, cmd, locals):306        # Ignores locals on purpose!307        seq = self.conn.asyncqueue(self.oid, "run", (cmd,), {})308        self.shell.interp.active_seq = seq309 310    def get_stack(self, frame, tbid):311        # passing frame and traceback IDs, not the objects themselves312        stack, i = self.call("get_stack", frame._fid, tbid)313        stack = [(FrameProxy(self.conn, fid), k) for fid, k in stack]314        return stack, i315 316    def set_continue(self):317        self.call("set_continue")318 319    def set_step(self):320        self.call("set_step")321 322    def set_next(self, frame):323        self.call("set_next", frame._fid)324 325    def set_return(self, frame):326        self.call("set_return", frame._fid)327 328    def set_quit(self):329        self.call("set_quit")330 331    def set_break(self, filename, lineno):332        msg = self.call("set_break", filename, lineno)333        return msg334 335    def clear_break(self, filename, lineno):336        msg = self.call("clear_break", filename, lineno)337        return msg338 339    def clear_all_file_breaks(self, filename):340        msg = self.call("clear_all_file_breaks", filename)341        return msg342 343def start_remote_debugger(rpcclt, pyshell):344    """Start the subprocess debugger, initialize the debugger GUI and RPC link345 346    Request the RPCServer start the Python subprocess debugger and link.  Set347    up the Idle side of the split debugger by instantiating the IdbProxy,348    debugger GUI, and debugger GUIAdapter objects and linking them together.349 350    Register the GUIAdapter with the RPCClient to handle debugger GUI351    interaction requests coming from the subprocess debugger via the GUIProxy.352 353    The IdbAdapter will pass execution and environment requests coming from the354    Idle debugger GUI to the subprocess debugger via the IdbProxy.355 356    """357    global idb_adap_oid358 359    idb_adap_oid = rpcclt.remotecall("exec", "start_the_debugger",\360                                   (gui_adap_oid,), {})361    idb_proxy = IdbProxy(rpcclt, pyshell, idb_adap_oid)362    gui = debugger.Debugger(pyshell, idb_proxy)363    gui_adap = GUIAdapter(rpcclt, gui)364    rpcclt.register(gui_adap_oid, gui_adap)365    return gui366 367def close_remote_debugger(rpcclt):368    """Shut down subprocess debugger and Idle side of debugger RPC link369 370    Request that the RPCServer shut down the subprocess debugger and link.371    Unregister the GUIAdapter, which will cause a GC on the Idle process372    debugger and RPC link objects.  (The second reference to the debugger GUI373    is deleted in pyshell.close_remote_debugger().)374 375    """376    close_subprocess_debugger(rpcclt)377    rpcclt.unregister(gui_adap_oid)378 379def close_subprocess_debugger(rpcclt):380    rpcclt.remotecall("exec", "stop_the_debugger", (idb_adap_oid,), {})381 382def restart_subprocess_debugger(rpcclt):383    idb_adap_oid_ret = rpcclt.remotecall("exec", "start_the_debugger",\384                                         (gui_adap_oid,), {})385    assert idb_adap_oid_ret == idb_adap_oid, 'Idb restarted with different oid'386 387 388if __name__ == "__main__":389    from unittest import main390    main('idlelib.idle_test.test_debugger_r', verbosity=2, exit=False)391 
codekingpro/portable-devtools · Team Ai