4
4
import types
5
5
import collections
6
6
import io
7
+ from fileinput import lineno
7
8
8
9
from opcode import *
9
10
from opcode import (
@@ -436,6 +437,8 @@ def __init__(self, file=None, lineno_width=0, offset_width=0, positions_width=0,
436
437
*positions_width* sets the width of the instruction positions field (0 omits it)
437
438
*label_width* sets the width of the label field
438
439
*show_caches* is a boolean indicating whether to display cache lines
440
+
441
+ If *positions_width* is specified, *lineno_width* is ignored.
439
442
"""
440
443
self .file = file
441
444
self .lineno_width = lineno_width
@@ -465,25 +468,36 @@ def print_instruction(self, instr, mark_as_current=False):
465
468
def print_instruction_line (self , instr , mark_as_current ):
466
469
"""Format instruction details for inclusion in disassembly output."""
467
470
lineno_width = self .lineno_width
471
+ positions_width = self .positions_width
468
472
offset_width = self .offset_width
469
473
label_width = self .label_width
470
474
471
- new_source_line = (lineno_width > 0 and
475
+ new_source_line = (( lineno_width > 0 or positions_width > 0 ) and
472
476
instr .starts_line and
473
477
instr .offset > 0 )
474
478
if new_source_line :
475
479
print (file = self .file )
476
480
477
481
fields = []
478
482
# 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 )
483
+ if lineno_width or positions_width :
484
+ if positions_width :
485
+ # reporting positions instead of just line numbers
486
+ assert lineno_width > 0
487
+ if instr_positions := instr .positions :
488
+ ps = tuple ('?' if p is None else p for p in instr_positions )
489
+ positions_str = "%s:%s-%s:%s" % ps
490
+ fields .append (f'{ positions_str :{positions_width }} ' )
491
+ else :
492
+ fields .append (' ' * positions_width )
485
493
else :
486
- fields .append (' ' * lineno_width )
494
+ if instr .starts_line :
495
+ lineno_fmt = "%%%dd" if instr .line_number is not None else "%%%ds"
496
+ lineno_fmt = lineno_fmt % lineno_width
497
+ lineno = _NO_LINENO if instr .line_number is None else instr .line_number
498
+ fields .append (lineno_fmt % lineno )
499
+ else :
500
+ fields .append (' ' * lineno_width )
487
501
# Column: Label
488
502
if instr .label is not None :
489
503
lbl = f"L{ instr .label } :"
@@ -821,7 +835,7 @@ def _make_labels_map(original_code, exception_entries=()):
821
835
e .target_label = labels_map [e .target ]
822
836
return labels_map
823
837
824
- _NO_LINENO = ' --'
838
+ _NO_LINENO = ' --'
825
839
826
840
def _get_lineno_width (linestarts ):
827
841
if linestarts is None :
@@ -836,6 +850,21 @@ def _get_lineno_width(linestarts):
836
850
return lineno_width
837
851
838
852
def _get_positions_width (code ):
853
+ # Positions are formatted as 'LINE:COL-ENDLINE:ENDCOL' with an additional
854
+ # whitespace after the end column. If one of the component is missing, we
855
+ # will print ? instead, thus the minimum width is 8 = 1 + len('?:?-?:?'),
856
+ # except if all positions are undefined, in which case positions are not
857
+ # printed (i.e. positions_width = 0).
858
+ has_value = True
859
+ values_width = 0
860
+ for positions in code .co_positions ():
861
+ if not has_value and any (isinstance (p ) for p in positions ):
862
+ has_value = True
863
+ width = sum (1 if p is None else len (str (p )) for p in positions )
864
+ values_width = max (width , values_width )
865
+ if has_value :
866
+ # 3 = number of separators in a normal format
867
+ return 1 + max (7 , 3 + values_width )
839
868
return 0
840
869
841
870
def _disassemble_bytes (code , lasti = - 1 , linestarts = None ,
0 commit comments