New submission from S Murthy <smur...@pm.me>:
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 <rep...@bugs.python.org> <https://bugs.python.org/issue39800> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com