@@ -191,8 +191,32 @@ def show_code(co, *, file=None):
191191 """
192192 print (code_info (co ), file = file )
193193
194- _Instruction = collections .namedtuple ("_Instruction" ,
195- "opname opcode arg argval argrepr offset starts_line is_jump_target" )
194+ Positions = collections .namedtuple (
195+ 'Positions' ,
196+ [
197+ 'lineno' ,
198+ 'end_lineno' ,
199+ 'col_offset' ,
200+ 'end_col_offset' ,
201+ ],
202+ defaults = [None ] * 4
203+ )
204+
205+ _Instruction = collections .namedtuple (
206+ "_Instruction" ,
207+ [
208+ 'opname' ,
209+ 'opcode' ,
210+ 'arg' ,
211+ 'argval' ,
212+ 'argrepr' ,
213+ 'offset' ,
214+ 'starts_line' ,
215+ 'is_jump_target' ,
216+ 'positions'
217+ ],
218+ defaults = [None ]
219+ )
196220
197221_Instruction .opname .__doc__ = "Human readable name for operation"
198222_Instruction .opcode .__doc__ = "Numeric code for operation"
@@ -202,6 +226,7 @@ def show_code(co, *, file=None):
202226_Instruction .offset .__doc__ = "Start index of operation within bytecode sequence"
203227_Instruction .starts_line .__doc__ = "Line started by this opcode (if any), otherwise None"
204228_Instruction .is_jump_target .__doc__ = "True if other code jumps to here, otherwise False"
229+ _Instruction .positions .__doc__ = "dis.Positions object holding the span of source code covered by this instruction"
205230
206231_ExceptionTableEntry = collections .namedtuple ("_ExceptionTableEntry" ,
207232 "start end target depth lasti" )
@@ -221,6 +246,8 @@ class Instruction(_Instruction):
221246 offset - start index of operation within bytecode sequence
222247 starts_line - line started by this opcode (if any), otherwise None
223248 is_jump_target - True if other code jumps to here, otherwise False
249+ positions - Optional dis.Positions object holding the span of source code
250+ covered by this instruction
224251 """
225252
226253 def _disassemble (self , lineno_width = 3 , mark_as_current = False , offset_width = 4 ):
@@ -281,7 +308,7 @@ def get_instructions(x, *, first_line=None):
281308 return _get_instructions_bytes (co .co_code ,
282309 co ._varname_from_oparg ,
283310 co .co_names , co .co_consts ,
284- linestarts , line_offset )
311+ linestarts , line_offset , co_positions = co . co_positions () )
285312
286313def _get_const_info (const_index , const_list ):
287314 """Helper to get optional details about const references
@@ -339,7 +366,7 @@ def parse_exception_table(code):
339366def _get_instructions_bytes (code , varname_from_oparg = None ,
340367 names = None , constants = None ,
341368 linestarts = None , line_offset = 0 ,
342- exception_entries = ()):
369+ exception_entries = (), co_positions = None ):
343370 """Iterate over the instructions in a bytecode string.
344371
345372 Generates a sequence of Instruction namedtuples giving the details of each
@@ -348,6 +375,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
348375 arguments.
349376
350377 """
378+ co_positions = co_positions or iter (())
351379 get_name = None if names is None else names .__getitem__
352380 labels = set (findlabels (code ))
353381 for start , end , target , _ , _ in exception_entries :
@@ -362,6 +390,10 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
362390 is_jump_target = offset in labels
363391 argval = None
364392 argrepr = ''
393+ try :
394+ positions = next (co_positions )
395+ except StopIteration :
396+ positions = None
365397 if arg is not None :
366398 # Set argval to the dereferenced value of the argument when
367399 # available, and argrepr to the string representation of argval.
@@ -395,7 +427,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
395427 if arg & (1 << i ))
396428 yield Instruction (opname [op ], op ,
397429 arg , argval , argrepr ,
398- offset , starts_line , is_jump_target )
430+ offset , starts_line , is_jump_target , positions )
399431
400432def disassemble (co , lasti = - 1 , * , file = None ):
401433 """Disassemble a code object."""
@@ -404,7 +436,7 @@ def disassemble(co, lasti=-1, *, file=None):
404436 _disassemble_bytes (co .co_code , lasti ,
405437 co ._varname_from_oparg ,
406438 co .co_names , co .co_consts , linestarts , file = file ,
407- exception_entries = exception_entries )
439+ exception_entries = exception_entries , co_positions = co . co_positions () )
408440
409441def _disassemble_recursive (co , * , file = None , depth = None ):
410442 disassemble (co , file = file )
@@ -419,7 +451,8 @@ def _disassemble_recursive(co, *, file=None, depth=None):
419451
420452def _disassemble_bytes (code , lasti = - 1 , varname_from_oparg = None ,
421453 names = None , constants = None , linestarts = None ,
422- * , file = None , line_offset = 0 , exception_entries = ()):
454+ * , file = None , line_offset = 0 , exception_entries = (),
455+ co_positions = None ):
423456 # Omit the line number column entirely if we have no line number info
424457 show_lineno = bool (linestarts )
425458 if show_lineno :
@@ -437,7 +470,8 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
437470 offset_width = 4
438471 for instr in _get_instructions_bytes (code , varname_from_oparg , names ,
439472 constants , linestarts ,
440- line_offset = line_offset , exception_entries = exception_entries ):
473+ line_offset = line_offset , exception_entries = exception_entries ,
474+ co_positions = co_positions ):
441475 new_source_line = (show_lineno and
442476 instr .starts_line is not None and
443477 instr .offset > 0 )
@@ -562,7 +596,8 @@ def dis(self):
562596 line_offset = self ._line_offset ,
563597 file = output ,
564598 lasti = offset ,
565- exception_entries = self .exception_entries )
599+ exception_entries = self .exception_entries ,
600+ co_positions = co .co_positions ())
566601 return output .getvalue ()
567602
568603
0 commit comments