Skip to content

Scan directories recursively decompiling .pyc files back to .py source file

Notifications You must be signed in to change notification settings

jazlopez/py-recursive-uncompyle6

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Welcome new ideas, feedback and comments

Jaziel Lopez

Software Engineer

Tijuana Area, BC. Mexico

Implementation

# Jaziel Lopez <[email protected]>
# Software Developer
# http://jlopez.mx
import argparse
import os
import glob
import subprocess

FROM_EXTENSION = "pyc"
TO_EXTENSION = "py"

parser = argparse.ArgumentParser(
    description='Decompile .pyc files recursively. '

                'It takes a directory in --path argument to scan for .pyc files to uncompile')

parser.add_argument('-p', '--path', dest='path',

                    default=os.path.abspath("."),

                    help="Specify root directory to scan pyc files from")


class uncompyle6:

    def __init__(self):
        pass

    @staticmethod
    def check_uncompyle(self):

        try:

            subprocess.call(["uncompyle6", "--version"])

        except OSError as e:

            print("[ERROR] - uncompyle6 or any of its dependencies are not installed")
            print("[ERROR] - refer to https://pypi.org/project/uncompyle6/ to install uncompyle6 and try again")

            exit(e.errno)

    @staticmethod
    def swap_extension(self, compiled):

        """
        Swap file extension from .pyc to .py
        :param compiled:
        :return:
        """

        metadata = os.path.splitext(compiled)

        return "{0}.{1}".format(metadata[0], TO_EXTENSION)

    def normalize_path(self, paths):

        """
        Normalize paths
        :param paths:
        :return:
        """

        clean = []                              # hold clean list of normalized matching paths

        prefix = os.path.commonprefix(paths)

        for i in paths:

            source = i

            base = self.swap_extension(i.replace(prefix, ""))

            clean.append({"source": source, "dirname": os.path.dirname(base), "name": os.path.basename(base)})

        return clean

    @staticmethod
    def search_pyc(self, use_directory):

        """
        Search .pyc files
        :param use_directory:
        :return:
        """

        return glob.glob("/".join([use_directory, "*." + FROM_EXTENSION]))

    @staticmethod
    def setup_directories(self, directories,  parent):

        """
        Mimic file organization
        :param directories:
        :param parent:
        :return:
        """

        for i in directories:

            if i["dirname"]:

                directory_name = os.path.join(parent, i["dirname"])

                if os.path.exists(directory_name):

                    continue

                os.mkdir(directory_name)

                print("[INFO] - Done creating %s directory" % directory_name)

    @staticmethod
    def uncompyle(self, locations, parent):

        for i in locations:

            uncompyle_source = i["source"]

            uncompyle_to = os.path.join(parent, i["dirname"], i["name"])

            subprocess.call(["uncompyle6",  "-o", uncompyle_to, uncompyle_source])

            print("[INFO] - {0} decompiled at {1}".format(uncompyle_source, uncompyle_to))


if __name__ == "__main__":

    matches = []

    args = parser.parse_args()

    path = args.path

    cwd = os.path.abspath(".")

    uncompyler = uncompyle6()

    uncompyler.check_uncompyle()

    for root, dirs, files in os.walk(path):

        for result in uncompyler.search_pyc(root):

            matches.append(result)

    normalized = uncompyler.normalize_path(matches)

    uncompyler.setup_directories(normalized, cwd)

    uncompyler.uncompyle(normalized, cwd)

    exit(0)

About

Scan directories recursively decompiling .pyc files back to .py source file

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages