Forked from ifahadone/decompile-pyc-dir-recursively.py
Last active
December 23, 2022 14:55
-
-
Save jarrodnorwell/298cb5501384431be3f19a33e0a2c991 to your computer and use it in GitHub Desktop.
Decompile compiled Python files preserving directory structure.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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