How to fully disassemble Python source
Late answer but I would have been glad to find it when needed. If you want to fully disassemble a script with functions without importing it, you have to implement the sub_byte_code function mentioned in the question. This is done by scanning byte_code.co_consts to find types.CodeType literals.
The following completes the script from the question:
import dis
import types
source_py = "test.py"
with open(source_py) as f_source:
source_code = f_source.read()
byte_code = compile(source_code, source_py, "exec")
dis.dis(byte_code)
for x in byte_code.co_consts:
if isinstance(x, types.CodeType):
sub_byte_code = x
func_name = sub_byte_code.co_name
print('\nDisassembly of %s:' % func_name)
dis.dis(sub_byte_code)
And the result will be something like that:
1 0 LOAD_CONST 0 (<code object foo at 0x02CB99C0, file "test.py", line 1>)
2 LOAD_CONST 1 ('foo')
4 MAKE_FUNCTION 0
6 STORE_NAME 0 (foo)
4 8 LOAD_NAME 0 (foo)
10 LOAD_CONST 2 (42)
12 CALL_FUNCTION 1
14 STORE_NAME 1 (x)
16 LOAD_CONST 3 (None)
18 RETURN_VALUE
Disassembly of foo:
2 0 LOAD_FAST 0 (n)
2 UNARY_NEGATIVE
4 RETURN_VALUE
Edit: starting from python 3.7, dis.dis disassembles functions and does this recursively. dis.dis has a depth
additional argument to control the depth of function definitions to be disassembled.
Import the file as a module and call dis.dis()
on that module.
import dis
import test
dis.dis(test)
You can also do this from the command-line:
python -m dis test.py
Quoting from the documentation for dis.dis
:
For a module, it disassembles all functions.
Edit: As of python 3.7, dis.dis
is recursive.