Skip to content

Commit 693cec0

Browse files
authored
bpo-43950: include position in dis.Instruction (GH-27015)
Automerge-Triggered-By: GH:isidentical
1 parent 44f91fc commit 693cec0

File tree

2 files changed

+287
-197
lines changed

2 files changed

+287
-197
lines changed

Lib/dis.py

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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

286313
def _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):
339366
def _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

400432
def 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

409441
def _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

420452
def _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

Comments
 (0)