ยปCore Development>Code coverage>Lib/idlelib/RemoteDebugger.py

Python code coverage for Lib/idlelib/RemoteDebugger.py

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