Skip to content

Commit 292d7df

Browse files
gh-152966: Add a disassembly browser to IDLE
Add a Disassembly Browser command to the Browse menu (Shell and editor). It shows the disassembled bytecode of the editor content, the Shell input, or the selection, as collapsible code objects colored by operand kind. Selecting an instruction highlights the matching source, and selecting source selects the instructions. While the debugger is stopped, the browser instead shows the code object that is executing -- obtained from the subprocess through the debugger RPC via marshal, not by recompiling the editor -- and marks the current instruction. It shares the window skeleton and editor-sync mechanism with the token and AST browsers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent eacd68e commit 292d7df

11 files changed

Lines changed: 1131 additions & 0 deletions

File tree

Doc/library/idle.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,22 @@ AST Browser
321321
Double-click a node, or press :kbd:`Escape`,
322322
to hide the browser and return to the editor at the node.
323323

324+
Disassembly Browser
325+
Open a window showing the disassembled bytecode of the editor content
326+
(or, in the Shell, the current input), or of the selection if there is one.
327+
Each code object is a collapsible row of its instructions,
328+
colored by the kind of operand they use;
329+
the one holding the cursor is opened and its instructions selected.
330+
Selecting an instruction highlights the matching region in the editor
331+
and moves the cursor there;
332+
selecting text or moving the cursor in the editor
333+
selects the instructions built from it.
334+
Double-click a row, or press :kbd:`Escape`,
335+
to hide the browser and return to the editor at the instruction.
336+
While the debugger is stopped, the browser follows it instead of the
337+
editor, showing the code object that is executing
338+
and marking the current instruction.
339+
324340
Options menu (Shell and Editor)
325341
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
326342

Lib/idlelib/News3.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ Released on 2026-10-01
44
=========================
55

66

7+
gh-152966: Add a Disassembly Browser to IDLE, opened from the Browse
8+
menu. It shows the disassembled bytecode of the editor content, the
9+
Shell input, or the selection, as collapsible code objects colored by
10+
operand kind. Selecting an instruction highlights the matching source,
11+
and selecting source selects the instructions. While the debugger is
12+
stopped, the browser instead shows the code object that is executing and
13+
marks the current instruction. Patch by Serhiy Storchaka and Claude
14+
Code.
15+
716
gh-152942: Add an AST Browser to IDLE, opened from the Browse menu. It
817
shows the abstract syntax tree of the editor content, the Shell input,
918
or the selection. Selecting a node highlights the matching region in

Lib/idlelib/debugger.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ def close(self, event=None):
170170
# (Causes a harmless extra cycle through close_debugger() if user
171171
# toggled debugger from pyshell Debug menu)
172172
self.pyshell.close_debugger()
173+
# Tell observers (e.g. the disassembly browser) debugging has ended.
174+
try:
175+
self.pyshell.text.event_generate("<<debugger-off>>", when="tail")
176+
except Exception:
177+
pass
173178
# Now close the debugger control window....
174179
self.top.destroy()
175180

@@ -282,6 +287,9 @@ def interaction(self, message, frame, info=None):
282287
if self.vsource.get():
283288
self.sync_source_line()
284289

290+
# Let observers (e.g. the disassembly browser) react to the stop.
291+
self.pyshell.text.event_generate("<<debugger-stopped>>", when="tail")
292+
285293
for b in self.buttons:
286294
b.configure(state="normal")
287295

@@ -313,6 +321,20 @@ def __frame2fileline(self, frame):
313321
lineno = frame.f_lineno
314322
return filename, lineno
315323

324+
def current_frame_code(self):
325+
"""Return (code object, last-instruction offset) for the current frame.
326+
327+
The code object is the one actually executing: obtained via marshal
328+
from a remote FrameProxy, or directly from a local frame. Return None
329+
when the program is not stopped.
330+
"""
331+
frame = self.frame
332+
if frame is None:
333+
return None
334+
get_code = getattr(frame, "code_object", None) # A remote FrameProxy.
335+
code = get_code() if get_code else frame.f_code # Else a local frame.
336+
return code, frame.f_lasti
337+
316338
def cont(self):
317339
self.idb.set_continue()
318340
self.abort_loop()

Lib/idlelib/debugger_r.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
barrier, in particular frame and traceback objects.
2020
2121
"""
22+
import marshal
2223
import reprlib
2324
import types
2425
from idlelib import debugger
@@ -144,6 +145,12 @@ def frame_code(self, fid):
144145
codetable[cid] = code
145146
return cid
146147

148+
def frame_code_marshal(self, fid):
149+
# A code object cannot be pickled through the RPC, but it can be
150+
# marshalled; the two interpreters are the same build, so the bytes
151+
# load back into an equivalent code object in the IDLE process.
152+
return marshal.dumps(frametable[fid].f_code)
153+
147154
#----------called by a CodeProxy----------
148155

149156
def code_name(self, cid):
@@ -219,6 +226,12 @@ def _get_f_code(self):
219226
cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})
220227
return CodeProxy(self._conn, self._oid, cid)
221228

229+
def code_object(self):
230+
"Return the frame's real code object, transported via marshal."
231+
blob = self._conn.remotecall(self._oid, "frame_code_marshal",
232+
(self._fid,), {})
233+
return marshal.loads(blob)
234+
222235
def _get_f_globals(self):
223236
did = self._conn.remotecall(self._oid, "frame_globals",
224237
(self._fid,), {})

0 commit comments

Comments
 (0)