@@ -191,8 +191,32 @@ def show_code(co, *, file=None):
191
191
"""
192
192
print (code_info (co ), file = file )
193
193
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
+ )
196
220
197
221
_Instruction .opname .__doc__ = "Human readable name for operation"
198
222
_Instruction .opcode .__doc__ = "Numeric code for operation"
@@ -202,6 +226,7 @@ def show_code(co, *, file=None):
202
226
_Instruction .offset .__doc__ = "Start index of operation within bytecode sequence"
203
227
_Instruction .starts_line .__doc__ = "Line started by this opcode (if any), otherwise None"
204
228
_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"
205
230
206
231
_ExceptionTableEntry = collections .namedtuple ("_ExceptionTableEntry" ,
207
232
"start end target depth lasti" )
@@ -221,6 +246,8 @@ class Instruction(_Instruction):
221
246
offset - start index of operation within bytecode sequence
222
247
starts_line - line started by this opcode (if any), otherwise None
223
248
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
224
251
"""
225
252
226
253
def _disassemble (self , lineno_width = 3 , mark_as_current = False , offset_width = 4 ):
@@ -281,7 +308,7 @@ def get_instructions(x, *, first_line=None):
281
308
return _get_instructions_bytes (co .co_code ,
282
309
co ._varname_from_oparg ,
283
310
co .co_names , co .co_consts ,
284
- linestarts , line_offset )
311
+ linestarts , line_offset , co_positions = co . co_positions () )
285
312
286
313
def _get_const_info (const_index , const_list ):
287
314
"""Helper to get optional details about const references
@@ -339,7 +366,7 @@ def parse_exception_table(code):
339
366
def _get_instructions_bytes (code , varname_from_oparg = None ,
340
367
names = None , constants = None ,
341
368
linestarts = None , line_offset = 0 ,
342
- exception_entries = ()):
369
+ exception_entries = (), co_positions = None ):
343
370
"""Iterate over the instructions in a bytecode string.
344
371
345
372
Generates a sequence of Instruction namedtuples giving the details of each
@@ -348,6 +375,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
348
375
arguments.
349
376
350
377
"""
378
+ co_positions = co_positions or iter (())
351
379
get_name = None if names is None else names .__getitem__
352
380
labels = set (findlabels (code ))
353
381
for start , end , target , _ , _ in exception_entries :
@@ -362,6 +390,10 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
362
390
is_jump_target = offset in labels
363
391
argval = None
364
392
argrepr = ''
393
+ try :
394
+ positions = next (co_positions )
395
+ except StopIteration :
396
+ positions = None
365
397
if arg is not None :
366
398
# Set argval to the dereferenced value of the argument when
367
399
# available, and argrepr to the string representation of argval.
@@ -395,7 +427,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
395
427
if arg & (1 << i ))
396
428
yield Instruction (opname [op ], op ,
397
429
arg , argval , argrepr ,
398
- offset , starts_line , is_jump_target )
430
+ offset , starts_line , is_jump_target , positions )
399
431
400
432
def disassemble (co , lasti = - 1 , * , file = None ):
401
433
"""Disassemble a code object."""
@@ -404,7 +436,7 @@ def disassemble(co, lasti=-1, *, file=None):
404
436
_disassemble_bytes (co .co_code , lasti ,
405
437
co ._varname_from_oparg ,
406
438
co .co_names , co .co_consts , linestarts , file = file ,
407
- exception_entries = exception_entries )
439
+ exception_entries = exception_entries , co_positions = co . co_positions () )
408
440
409
441
def _disassemble_recursive (co , * , file = None , depth = None ):
410
442
disassemble (co , file = file )
@@ -419,7 +451,8 @@ def _disassemble_recursive(co, *, file=None, depth=None):
419
451
420
452
def _disassemble_bytes (code , lasti = - 1 , varname_from_oparg = None ,
421
453
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 ):
423
456
# Omit the line number column entirely if we have no line number info
424
457
show_lineno = bool (linestarts )
425
458
if show_lineno :
@@ -437,7 +470,8 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
437
470
offset_width = 4
438
471
for instr in _get_instructions_bytes (code , varname_from_oparg , names ,
439
472
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 ):
441
475
new_source_line = (show_lineno and
442
476
instr .starts_line is not None and
443
477
instr .offset > 0 )
@@ -562,7 +596,8 @@ def dis(self):
562
596
line_offset = self ._line_offset ,
563
597
file = output ,
564
598
lasti = offset ,
565
- exception_entries = self .exception_entries )
599
+ exception_entries = self .exception_entries ,
600
+ co_positions = co .co_positions ())
566
601
return output .getvalue ()
567
602
568
603
0 commit comments