New submission from S Murthy <[email protected]>:
I am using the dis module to look at source (and logical) lines of code vs
corresponding bytecode instructions. I am bit confused by the output of dis.dis
when disassembling a given method vs the corresponding source string, e.g.
>>> def f(x): return x**2
>>> dis.dis(f)
1 0 LOAD_FAST 0 (x)
2 LOAD_CONST 1 (2)
4 BINARY_POWER
6 RETURN_VALUE
This is the bytecode instruction block for the body only (not the method
header), but dis.dis('def f(x): return x**2') produces the instructions for the
header and body:
>>> dis.dis('def f(x): return x**2')
1 0 LOAD_CONST 0 (<code object f at 0x10b0f7f60, file "<dis>",
line 1>)
2 LOAD_CONST 1 ('f')
4 MAKE_FUNCTION 0
6 STORE_NAME 0 (f)
8 LOAD_CONST 2 (None)
10 RETURN_VALUE
Disassembly of <code object f at 0x10b0f7f60, file "<dis>", line 1>:
1 0 LOAD_FAST 0 (x)
2 LOAD_CONST 1 (2)
4 BINARY_POWER
6 RETURN_VALUE
I have traced this difference to the different behaviour of dis.dis for methods
vs source code strings:
def dis(x=None, *, file=None, depth=None):
...
...
if hasattr(x, '__code__'):
x = x.__code__
...
# Perform the disassembly
...
elif hasattr(x, 'co_code'): # Code object
_disassemble_recursive(x, file=file, depth=depth)
...
elif isinstance(x, str): # Source code
_disassemble_str(x, file=file, depth=depth)
...
It appears as if the method body is contained in the code object produced from
compiling the source (_try_compile(source, '<dis>', ...)) but not if the code
object was obtained from f.__code__.
Why is this the case, and would it not be better to for dis.dis to behave
consistently for methods and source strings of methods, and to generate/produce
the complete instruction set, including for any headers? The current behaviour
of dis.dis means that Bytecode(x) is also affected, as iterating over the
instructions gives you different instructions depending on whether x is a
method or a source string of x:
>>> for instr in dis.Bytecode(f):
... print(instr)
...
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='x', argrepr='x',
offset=0, starts_line=1, is_jump_target=False)
Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval=2, argrepr='2',
offset=2, starts_line=None, is_jump_target=False)
Instruction(opname='BINARY_POWER', opcode=19, arg=None, argval=None,
argrepr='', offset=4, starts_line=None, is_jump_target=False)
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None,
argrepr='', offset=6, starts_line=None, is_jump_target=False
>>> for instr in dis.Bytecode(inspect.getsource(f)):
... print(instr)
...
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=<code object f at
0x11e4036f0, file "<disassembly>", line 1>, argrepr='<code object f at
0x11e4036f0, file "<disassembly>", line 1>', offset=0, starts_line=1,
is_jump_target=False)
Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval='f', argrepr="'f'",
offset=2, starts_line=None, is_jump_target=False)
Instruction(opname='MAKE_FUNCTION', opcode=132, arg=0, argval=0, argrepr='',
offset=4, starts_line=None, is_jump_target=False)
Instruction(opname='STORE_NAME', opcode=90, arg=0, argval='f', argrepr='f',
offset=6, starts_line=None, is_jump_target=False)
Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=None,
argrepr='None', offset=8, starts_line=None, is_jump_target=False)
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None,
argrepr='', offset=10, starts_line=None, is_jump_target=False)
----------
components: Library (Lib)
messages: 362985
nosy: smurthy
priority: normal
severity: normal
status: open
title: Inconsistent/incomplete disassembly of methods vs method source code
type: behavior
versions: Python 3.7
_______________________________________
Python tracker <[email protected]>
<https://bugs.python.org/issue39800>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com