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