Skip to content

Commit a8eab11

Browse files
committed
allow to show code's positions in dis
1 parent 3157e24 commit a8eab11

File tree

1 file changed

+37
-9
lines changed

1 file changed

+37
-9
lines changed

Lib/dis.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,8 @@ def __init__(self, file=None, lineno_width=0, offset_width=0, positions_width=0,
436436
*positions_width* sets the width of the instruction positions field (0 omits it)
437437
*label_width* sets the width of the label field
438438
*show_caches* is a boolean indicating whether to display cache lines
439+
440+
If *positions_width* is specified, *lineno_width* is ignored.
439441
"""
440442
self.file = file
441443
self.lineno_width = lineno_width
@@ -465,25 +467,36 @@ def print_instruction(self, instr, mark_as_current=False):
465467
def print_instruction_line(self, instr, mark_as_current):
466468
"""Format instruction details for inclusion in disassembly output."""
467469
lineno_width = self.lineno_width
470+
positions_width = self.positions_width
468471
offset_width = self.offset_width
469472
label_width = self.label_width
470473

471-
new_source_line = (lineno_width > 0 and
474+
new_source_line = ((lineno_width > 0 or positions_width > 0) and
472475
instr.starts_line and
473476
instr.offset > 0)
474477
if new_source_line:
475478
print(file=self.file)
476479

477480
fields = []
478481
# Column: Source code line number
479-
if lineno_width:
480-
if instr.starts_line:
481-
lineno_fmt = "%%%dd" if instr.line_number is not None else "%%%ds"
482-
lineno_fmt = lineno_fmt % lineno_width
483-
lineno = _NO_LINENO if instr.line_number is None else instr.line_number
484-
fields.append(lineno_fmt % lineno)
482+
if lineno_width or positions_width:
483+
if positions_width:
484+
# reporting positions instead of just line numbers
485+
assert lineno_width > 0
486+
if instr_positions := instr.positions:
487+
ps = tuple('?' if p is None else p for p in instr_positions)
488+
positions_str = "%s:%s-%s:%s" % ps
489+
fields.append(f'{positions_str:{positions_width}}')
490+
else:
491+
fields.append(' ' * positions_width)
485492
else:
486-
fields.append(' ' * lineno_width)
493+
if instr.starts_line:
494+
lineno_fmt = "%%%dd" if instr.line_number is not None else "%%%ds"
495+
lineno_fmt = lineno_fmt % lineno_width
496+
lineno = _NO_LINENO if instr.line_number is None else instr.line_number
497+
fields.append(lineno_fmt % lineno)
498+
else:
499+
fields.append(' ' * lineno_width)
487500
# Column: Label
488501
if instr.label is not None:
489502
lbl = f"L{instr.label}:"
@@ -821,7 +834,7 @@ def _make_labels_map(original_code, exception_entries=()):
821834
e.target_label = labels_map[e.target]
822835
return labels_map
823836

824-
_NO_LINENO = ' --'
837+
_NO_LINENO = ' --'
825838

826839
def _get_lineno_width(linestarts):
827840
if linestarts is None:
@@ -836,6 +849,21 @@ def _get_lineno_width(linestarts):
836849
return lineno_width
837850

838851
def _get_positions_width(code):
852+
# Positions are formatted as 'LINE:COL-ENDLINE:ENDCOL' with an additional
853+
# whitespace after the end column. If one of the component is missing, we
854+
# will print ? instead, thus the minimum width is 8 = 1 + len('?:?-?:?'),
855+
# except if all positions are undefined, in which case positions are not
856+
# printed (i.e. positions_width = 0).
857+
has_value = True
858+
values_width = 0
859+
for positions in code.co_positions():
860+
if not has_value and any(isinstance(p) for p in positions):
861+
has_value = True
862+
width = sum(1 if p is None else len(str(p)) for p in positions)
863+
values_width = max(width, values_width)
864+
if has_value:
865+
# 3 = number of separators in a normal format
866+
return 1 + max(7, 3 + values_width)
839867
return 0
840868

841869
def _disassemble_bytes(code, lasti=-1, linestarts=None,

0 commit comments

Comments
 (0)