Skip to content

Instantly share code, notes, and snippets.

@jarrodnorwell
Forked from ifahadone/decompile-pyc-dir-recursively.py
Last active December 23, 2022 14:55
Show Gist options
  • Save jarrodnorwell/298cb5501384431be3f19a33e0a2c991 to your computer and use it in GitHub Desktop.
Save jarrodnorwell/298cb5501384431be3f19a33e0a2c991 to your computer and use it in GitHub Desktop.
Decompile compiled Python files preserving directory structure.
from argparse import ArgumentParser
from glob import glob
from os import mkdir, path, sep, walk
from subprocess import call
from sys import version_info
PY_EXT = 'py'
PYC_EXT = 'pyc'
class Colors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
parser = ArgumentParser(description='Decompile compiled Python files preserving directory structure.')
parser.add_argument('-p', dest='path')
def decompile(output: str, filepath: str):
call(['uncompyle6', '-o', output, filepath])
def swap_extension(string: str):
start, _ = path.splitext(string)
return f'{start}.{PY_EXT}'
major, minor, micro, releaselevel, serial = version_info
if __name__ == '__main__' and f'{major}.{minor}' not in ['3.12', '3.11']:
root_path = parser.parse_args().path
if root_path is None:
print(f'{Colors.FAIL}[ERROR]{Colors.ENDC}: No path was provided.')
exit(0)
decompiled_path = root_path + sep + 'decompiled'
if not path.exists(decompiled_path):
mkdir(decompiled_path)
matches = []
for root, dirs, files in walk(root_path):
for file in glob(sep.join([root, f'*.{PYC_EXT}'])):
matches.append(file)
clean_paths = []
prefix = path.commonprefix(matches)
for unclean_path in matches:
base = swap_extension(str(unclean_path).replace(prefix, ''))
clean_paths.append({'dirname' : path.dirname(base), 'filepath' : unclean_path, 'filename' : path.basename(base)})
for directory in clean_paths:
if directory['dirname']:
dirname = path.join(decompiled_path, directory['dirname'])
if not path.exists(dirname):
mkdir(dirname)
for directory in clean_paths:
output = path.join(decompiled_path, directory['dirname'], directory['filename'])
decompile(output, directory['filepath'])
else:
print(f'[{Colors.FAIL}ERROR{Colors.ENDC}]: Invalid Python version, supported versions are: 3.10, 3.9, 3.8 and 3.7.')
exit(0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment