#!/usr/bin/env python2.7 from __future__ import print_function import marshal import struct import sys import time import uncompyle6 from pathlib2 import Path def _pack_uint32(val): """ Convert integer to 32-bit little-endian bytes """ return struct.pack("= (3,4): from importlib.util import MAGIC_NUMBER else: import imp MAGIC_NUMBER = imp.get_magic() # Add the magic number that indicates the version of Python the bytecode is for # # The .pyc may not decompile if this four-byte value is wrong. Either hardcode the # value for the target version (eg. b'\x33\x0D\x0D\x0A' instead of MAGIC_NUMBER) # or see trymagicnum.py to step through different values to find a valid one. data = bytearray(MAGIC_NUMBER) # Handle extra 32-bit field in header from Python 3.7 onwards # See: https://www.python.org/dev/peps/pep-0552 if sys.version_info >= (3,7): # Blank bit field value to indicate traditional pyc header data.extend(_pack_uint32(0)) data.extend(_pack_uint32(int(mtime))) # Handle extra 32-bit field for source size from Python 3.2 onwards # See: https://www.python.org/dev/peps/pep-3147/ if sys.version_info >= (3,2): data.extend(_pack_uint32(source_size)) data.extend(marshal.dumps(code)) return data if len(sys.argv) < 3: print("Usage %s " % sys.argv[0]) sys.exit(1) in_path = Path(sys.argv[1]) pyc_path = Path(sys.argv[2]) py_path = Path(sys.argv[3]) for path in in_path.rglob("*.co"): try: co = marshal.loads(path.read_bytes()) pyc = code_to_bytecode(co, time.time()) pyc_file = pyc_path / path.relative_to(in_path).with_suffix(".pyc") pyc_file.parent.mkdir(parents=True, exist_ok=True) with open(str(pyc_file), "wb") as file: file.write(pyc) py_file = py_path / path.relative_to(in_path).with_suffix(".py") py_file.parent.mkdir(parents=True, exist_ok=True) with open(str(py_file), 'w') as decompiled: uncompyle6.main.decompile_file(str(pyc_file), decompiled) except: print("Failed to decompile %s" % path)