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

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