@@ -262,6 +262,7 @@ def show_code(co, *, file=None):
262262 'offset' ,
263263 'start_offset' ,
264264 'starts_line' ,
265+ 'line_number' ,
265266 'is_jump_target' ,
266267 'positions'
267268 ],
@@ -278,7 +279,8 @@ def show_code(co, *, file=None):
278279 "Start index of operation within bytecode sequence, including extended args if present; "
279280 "otherwise equal to Instruction.offset"
280281)
281- _Instruction .starts_line .__doc__ = "Line started by this opcode (if any), otherwise None"
282+ _Instruction .starts_line .__doc__ = "True if this opcode starts a source line, otherwise False"
283+ _Instruction .line_number .__doc__ = "source line number associated with this opcode (if any), otherwise None"
282284_Instruction .is_jump_target .__doc__ = "True if other code jumps to here, otherwise False"
283285_Instruction .positions .__doc__ = "dis.Positions object holding the span of source code covered by this instruction"
284286
@@ -321,7 +323,8 @@ class Instruction(_Instruction):
321323 offset - start index of operation within bytecode sequence
322324 start_offset - start index of operation within bytecode sequence including extended args if present;
323325 otherwise equal to Instruction.offset
324- starts_line - line started by this opcode (if any), otherwise None
326+ starts_line - True if this opcode starts a source line, otherwise False
327+ line_number - source line number associated with this opcode (if any), otherwise None
325328 is_jump_target - True if other code jumps to here, otherwise False
326329 positions - Optional dis.Positions object holding the span of source code
327330 covered by this instruction
@@ -376,9 +379,10 @@ def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=4):
376379 fields = []
377380 # Column: Source code line number
378381 if lineno_width :
379- if self .starts_line is not None :
380- lineno_fmt = "%%%dd" % lineno_width
381- fields .append (lineno_fmt % self .starts_line )
382+ if self .starts_line :
383+ lineno_fmt = "%%%dd" if self .line_number is not None else "%%%ds"
384+ lineno_fmt = lineno_fmt % lineno_width
385+ fields .append (lineno_fmt % self .line_number )
382386 else :
383387 fields .append (' ' * lineno_width )
384388 # Column: Current instruction indicator
@@ -527,12 +531,18 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
527531 for start , end , target , _ , _ in exception_entries :
528532 for i in range (start , end ):
529533 labels .add (target )
530- starts_line = None
534+ starts_line = False
535+ local_line_number = None
536+ line_number = None
531537 for offset , start_offset , op , arg in _unpack_opargs (original_code ):
532538 if linestarts is not None :
533- starts_line = linestarts .get (offset , None )
534- if starts_line is not None :
535- starts_line += line_offset
539+ starts_line = offset in linestarts
540+ if starts_line :
541+ local_line_number = linestarts [offset ]
542+ if local_line_number is not None :
543+ line_number = local_line_number + line_offset
544+ else :
545+ line_number = None
536546 is_jump_target = offset in labels
537547 argval = None
538548 argrepr = ''
@@ -599,7 +609,8 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
599609 argrepr = _intrinsic_2_descs [arg ]
600610 yield Instruction (_all_opname [op ], op ,
601611 arg , argval , argrepr ,
602- offset , start_offset , starts_line , is_jump_target , positions )
612+ offset , start_offset , starts_line , line_number ,
613+ is_jump_target , positions )
603614 if not caches :
604615 continue
605616 if not show_caches :
@@ -618,7 +629,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
618629 else :
619630 argrepr = ""
620631 yield Instruction (
621- "CACHE" , CACHE , 0 , None , argrepr , offset , offset , None , False ,
632+ "CACHE" , CACHE , 0 , None , argrepr , offset , offset , False , None , False ,
622633 Positions (* next (co_positions , ()))
623634 )
624635
@@ -651,13 +662,21 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
651662 * , file = None , line_offset = 0 , exception_entries = (),
652663 co_positions = None , show_caches = False , original_code = None ):
653664 # Omit the line number column entirely if we have no line number info
654- show_lineno = bool (linestarts )
665+ if bool (linestarts ):
666+ linestarts_ints = [line for line in linestarts .values () if line is not None ]
667+ show_lineno = len (linestarts_ints ) > 0
668+ else :
669+ show_lineno = False
670+
655671 if show_lineno :
656- maxlineno = max (linestarts . values () ) + line_offset
672+ maxlineno = max (linestarts_ints ) + line_offset
657673 if maxlineno >= 1000 :
658674 lineno_width = len (str (maxlineno ))
659675 else :
660676 lineno_width = 3
677+
678+ if lineno_width < len (str (None )) and None in linestarts .values ():
679+ lineno_width = len (str (None ))
661680 else :
662681 lineno_width = 0
663682 maxoffset = len (code ) - 2
@@ -673,7 +692,7 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
673692 show_caches = show_caches ,
674693 original_code = original_code ):
675694 new_source_line = (show_lineno and
676- instr .starts_line is not None and
695+ instr .starts_line and
677696 instr .offset > 0 )
678697 if new_source_line :
679698 print (file = file )
@@ -755,10 +774,12 @@ def findlinestarts(code):
755774 """Find the offsets in a byte code which are start of lines in the source.
756775
757776 Generate pairs (offset, lineno)
777+ lineno will be an integer or None the offset does not have a source line.
758778 """
759- lastline = None
779+
780+ lastline = False # None is a valid line number
760781 for start , end , line in code .co_lines ():
761- if line is not None and line != lastline :
782+ if line is not lastline :
762783 lastline = line
763784 yield start , line
764785 return
0 commit comments