19

A Python module is automatically compiled into a .pyc file by CPython interpreter. The .pyc file, which contains the bytecode, is in binary format (marshaled code?). Is there a GUI (or command line) tool that let me view the bytecode?

6
  • Are you fine with importing the module? This would of course execute it. Commented Jun 21, 2012 at 15:23
  • Could you clarify "view the bytecode"? Commented Jun 21, 2012 at 15:31
  • do you want to decompile it to python code (.py) or to understand what each byte means? Commented Jun 21, 2012 at 15:33
  • 1
    I would like to look at and study the bytecode. I especially wants to find out how module global objects are represented. Commented Jun 21, 2012 at 15:34
  • Did a little google search and I found that Python comes with a module called "dis" that let me do that. "import dis; import mymodule; dis.dis(module)". Still wondering if there is a GUI tool for this. Commented Jun 21, 2012 at 15:38

3 Answers 3

17

Every *.pyc file is a binary file containing next things:

  • a four-byte magic number - it's simply bytes that change with each change to the marshalling code;
  • a four-byte modification timestamp - is the Unix modification timestamp of the source file that generated the .pyc, so that it can be recompiled if the source changes;
  • since version Python3.3+ next four bytes is field that encodes the size of the source file as a long;
  • a marshalled code object.

Why not just use the CPython`s built-in features for this task?


A file view_pyc_file.py

import platform import time import sys import binascii import marshal import dis import struct def view_pyc_file(path): """Read and display a content of the Python`s bytecode in a pyc-file.""" file = open(path, 'rb') magic = file.read(4) timestamp = file.read(4) size = None if sys.version_info.major == 3 and sys.version_info.minor >= 3: size = file.read(4) size = struct.unpack('I', size)[0] code = marshal.load(file) magic = binascii.hexlify(magic).decode('utf-8') timestamp = time.asctime(time.localtime(struct.unpack('I', timestamp)[0])) dis.disassemble(code) print('-' * 80) print( 'Python version: {}\nMagic code: {}\nTimestamp: {}\nSize: {}' .format(platform.python_version(), magic, timestamp, size) ) file.close() if __name__ == '__main__': view_pyc_file(sys.argv[1]) 

Tested with next CPython`s versions:

  • 2.7.9
  • 3.4.2
  • 3.5.2

Demonstration

Content of a file main.py

$ cat main.py print("Never give up") 

Create and to read pyc-file by the python2.7

setivolkylany$~/Downloads/temp/temp$ python2.7 -m py_compile main.py setivolkylany$~/Downloads/temp/temp$ python2.7 view_pyc_file.py ./main.pyc 1 0 LOAD_CONST 0 ('Never give up') 3 PRINT_ITEM 4 PRINT_NEWLINE 5 LOAD_CONST 1 (None) 8 RETURN_VALUE -------------------------------------------------------------------------------- Python version: 2.7.9 Magic code: 03f30d0a Timestamp: Fri Mar 10 15:08:20 2017 Size: None 

Create and to read pyc-file by the python3.4

setivolkylany$~/Downloads/temp/temp$ python3.4 -m py_compile main.py setivolkylany$~/Downloads/temp/temp$ python3.4 view_pyc_file.py __pycache__/main.cpython-34.pyc 1 0 LOAD_NAME 0 (print) 3 LOAD_CONST 0 ('Never give up') 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 9 POP_TOP 10 LOAD_CONST 1 (None) 13 RETURN_VALUE -------------------------------------------------------------------------------- Python version: 3.4.2 Magic code: ee0c0d0a Timestamp: Fri Mar 10 15:08:20 2017 Size: 23 

Create and to read pyc-file by the python3.5

setivolkylany$~/Downloads/temp/temp$ python3.5 -m py_compile main.py setivolkylany$~/Downloads/temp/temp$ python3.5 view_pyc_file.py __pycache__/main.cpython-35.pyc 1 0 LOAD_NAME 0 (print) 3 LOAD_CONST 0 ('Never give up') 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 9 POP_TOP 10 LOAD_CONST 1 (None) 13 RETURN_VALUE -------------------------------------------------------------------------------- Python version: 3.5.2 Magic code: 160d0d0a Timestamp: Fri Mar 10 15:08:20 2017 Size: 23 

Based on:

Sign up to request clarification or add additional context in comments.

2 Comments

This now needs to be updated with regards to python.org/dev/peps/pep-0552 (Python 3.7).
I am unable to add the required modification here in the comments, so I've added it as a new answer, but @PADYMKO please feel free to edit your answer with it.
8

Extending the code form @PADYMKO based on @Apteryx 's note about the PEP:

def view_pyc_file(path): """Read and display a content of the Python`s bytecode in a pyc-file.""" with open(path, 'rb') as file: magic = file.read(4) bit_field = None timestamp = None hashstr = None size = None if sys.version_info.major == 3 and sys.version_info.minor >= 7: bit_field = int.from_bytes(file.read(4), byteorder=sys.byteorder) if 1 & bit_field == 1: hashstr = file.read(8) else: timestamp = file.read(4) size = file.read(4) size = struct.unpack('I', size)[0] elif sys.version_info.major == 3 and sys.version_info.minor >= 3: timestamp = file.read(4) size = file.read(4) size = struct.unpack('I', size)[0] else: timestamp = file.read(4) code = marshal.load(file) magic = binascii.hexlify(magic).decode('utf-8') timestamp = time.asctime(time.localtime(struct.unpack('I', timestamp)[0])) dis.disassemble(code) print('-' * 80) print( 'Python version: {}\nMagic code: {}\nTimestamp: {}\nSize: {}\nHash: {}\nBitfield: {}' .format(platform.python_version(), magic, timestamp, size, hashstr, bit_field) ) 

1 Comment

The only answer that works for Python 3.7! Thanks!
2

There's a visual python disassembler called PyChrisanthemum.

To do it the command-line way you can use module dis (python 2.7.3, python 3.2.3), as OP already found out.

1 Comment

This project is now pretty dead, so I wouldn't expect it to be able to read (perfectly) the byte-code produced by recent Python versions.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.