diff --git a/README.md b/README.md index ca133a8..b38080e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,17 @@ # Python Obfuscated Package Clean Architecture Template -a clean architecture template for python obfuscated package with stubs \ No newline at end of file + +a clean architecture template for python obfuscated package with stubs + +## Run + +simple run project with the following command: + +```bash +python .\src\obfpkg\main.py -s .\template\src_pkg\ -t .\template\stubs-pkg\ -v + +``` + +Then go to `.\template\dist` and see the outputs! + +1. The `animal` package is the obfuscated package of the animal +2. The `animal_stubs` package for keep readability and see the exist structures and methods in your IDE! diff --git a/requirements.txt b/requirements.txt index a44fb78..e9a8846 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -pyarmor==6.6.2 +pyarmor<=6.6.2,>=6.5.0 mypy==0.982 wheel \ No newline at end of file diff --git a/src/obfpkg/core/packager.py b/src/obfpkg/core/packager.py index dc50ec4..d2bd507 100644 --- a/src/obfpkg/core/packager.py +++ b/src/obfpkg/core/packager.py @@ -11,15 +11,25 @@ def __init__(self, build_path: Path, output_path: Path, verbose: bool = False): self.output_path = output_path self.verbose = verbose - def build(self): + def build(self, with_version=False): + version = ( + [ + "--python-tag", + f"cp{sys.version_info.major}{sys.version_info.minor}", + ] + if with_version + else [] + ) process = Popen( [ sys.executable, "setup.py", + "build", "bdist_wheel", "--dist-dir", "whl", - ], + ] + + version, cwd=f"{self.build_path}", stdout=PIPE, stderr=PIPE, diff --git a/src/obfpkg/core/stub_maker.py b/src/obfpkg/core/stub_maker.py index 7703755..b3ef983 100644 --- a/src/obfpkg/core/stub_maker.py +++ b/src/obfpkg/core/stub_maker.py @@ -1,9 +1,9 @@ -from os import rename, listdir -from pathlib import Path -from subprocess import Popen, PIPE import shutil +from os import listdir, rename +from pathlib import Path +from subprocess import PIPE, Popen -STUBS_PACKAGE_DIR_NAME = "stubs-pkg" +from utils import remove_intersection_path class Stubs: @@ -12,14 +12,19 @@ class Stubs: def __init__( self, src_path: Path, build_path: Path, package_name: str, verbose: bool = False ): - self.src_path = src_path + self.src_path = src_path.resolve() self.build_path = build_path.resolve() self.package_name = package_name self.verbose = verbose self.clean_up() - def _rename(self): + def __log(self, items): + if items and self.verbose: + for item in items: + print(f"The {item!r} overwrited from source project to stubs ") + + def __rename(self): for i in range(3, 0, -1): try: rename( @@ -57,13 +62,25 @@ def generate(self): raise Exception( f"Error while generating stubs: {stderr.decode('utf-8')}\n\nSTDOUT:\n{stdout.decode('utf-8')}" ) - else: - self._rename() + + overwrited_pyi_files = self.overwrite_stubs_from_src() + self.__log(overwrited_pyi_files) + self.__rename() return stdout, stderr + def __copy(self, items, src: Path, dest: Path): + for item in items: + shutil.copy(src / item, dest / item) + def overwrite_stubs_from_src(self): - raise NotImplementedError + pyi_files = self.src_path.glob("*/*.pyi") + pure_pypi_paths = [ + remove_intersection_path(self.src_path, item) for item in pyi_files + ] + self.__copy(pure_pypi_paths, self.src_path, self.build_path) + + return pure_pypi_paths def clean_up(self): build_abspath = self.build_path.resolve().absolute() diff --git a/src/obfpkg/main.py b/src/obfpkg/main.py index 12367ce..fbdf035 100644 --- a/src/obfpkg/main.py +++ b/src/obfpkg/main.py @@ -1,18 +1,18 @@ import argparse +import tempfile from os import mkdir from pathlib import Path -import tempfile try: # package style import - from .core import Package, Stubs, Obfuscate - from .utils import PathType, PathCheck, Exist + from .core import Obfuscate, Package, Stubs + from .utils import Exist, PathCheck, PathType except ImportError: # project style import - from core import Package, Stubs, Obfuscate # type: ignore - from utils import PathType, PathCheck, Exist # type: ignore + from core import Obfuscate, Package, Stubs # type: ignore + from utils import Exist, PathCheck, PathType # type: ignore + -STUBS_PACKAGE_DIR_NAME = "stubs-pkg" MANIFEST_FILE_NAME = "MANIFEST.in" @@ -47,7 +47,7 @@ def get_args() -> argparse.ArgumentParser: "--stubs-dir", metavar="stubs_pkg_dir", type=PathCheck(exists=Exist(check=True), ptype=PathType.DIR), - help=f"The directory of stubs package project (needed 'setup.py' for build pkg-stubs). default is: /../{STUBS_PACKAGE_DIR_NAME}", + help="The directory of stubs package project (needed 'setup.py' for build pkg-stubs).", ) return parser @@ -67,7 +67,7 @@ def get_package_name(src: Path): return package -def rewriter(path: Path): +def rewrite_manifest(path: Path): lines = [ "\nglobal-include *.pyi", # important for stubs package", "\nrecursive-include */pytransform *", # important for obfuscate package", @@ -87,11 +87,6 @@ def rewriter(path: Path): f.writelines(must_write_lines) -def recreate_manifest(src: Path, stubs_build_path: Path): - for path in [src, stubs_build_path]: - rewriter(path.joinpath(MANIFEST_FILE_NAME)) - - def create_stubs_package( src: Path, build_path: Path, @@ -134,7 +129,7 @@ def create_obfuscated_package( output_path=output_dir, verbose=verbose, ) - package_generator.build() + package_generator.build(with_version=True) package_generator.move_to_output() @@ -146,11 +141,6 @@ def main(): print(f"{k:>15}: {v!r:<20}") print() - stubs_build_path = ( - args.src / f"../{STUBS_PACKAGE_DIR_NAME}" - if args.stubs_dir is None - else args.stubs_dir - ) if args.output_dir is None: args.output_dir = (args.src / f"../dist").resolve() try: @@ -158,16 +148,19 @@ def main(): except FileExistsError: pass - recreate_manifest(args.src, stubs_build_path) + if args.stubs_dir: + stubs_build_path = args.stubs_dir.resolve() + PathCheck(exists=Exist(check=True), ptype=PathType.DIR)(stubs_build_path) + rewrite_manifest(stubs_build_path.joinpath(MANIFEST_FILE_NAME)) + create_stubs_package( + src=args.src, + build_path=stubs_build_path, + output_dir=args.output_dir, + package_name=package_name, + verbose=args.verbose, + ) - PathCheck(exists=Exist(check=True), ptype=PathType.DIR)(stubs_build_path) - create_stubs_package( - src=args.src, - build_path=stubs_build_path, - output_dir=args.output_dir, - package_name=package_name, - verbose=args.verbose, - ) + rewrite_manifest(args.src.joinpath(MANIFEST_FILE_NAME)) create_obfuscated_package( src=args.src, diff --git a/src/obfpkg/utils/__init__.py b/src/obfpkg/utils/__init__.py index 8871852..46da390 100644 --- a/src/obfpkg/utils/__init__.py +++ b/src/obfpkg/utils/__init__.py @@ -1,7 +1,9 @@ __all__ = [ + "Exist", "PathCheck", "PathType", - "Exist", + "remove_intersection_path", ] -from .structs import PathCheck, PathType, Exist +from .functions import remove_intersection_path +from .structs import Exist, PathCheck, PathType diff --git a/src/obfpkg/utils/functions.py b/src/obfpkg/utils/functions.py new file mode 100644 index 0000000..1f46d59 --- /dev/null +++ b/src/obfpkg/utils/functions.py @@ -0,0 +1,13 @@ + + +def remove_intersection_path(path1, path2): + str_path1 = str(path1) + str_path2 = str(path2) + + shrt_path = str_path1 if len(str_path1) < len(str_path2) else str_path2 + long_path = str_path1 if len(str_path1) > len(str_path2) else str_path2 + + shrt_idx = long_path.find(shrt_path) + if shrt_idx == 0: + return long_path.replace(shrt_path, "").lstrip("\\/") + raise ValueError() diff --git a/src/obfpkg/utils/structs.py b/src/obfpkg/utils/structs.py index 957aa76..df92350 100644 --- a/src/obfpkg/utils/structs.py +++ b/src/obfpkg/utils/structs.py @@ -1,10 +1,10 @@ -from os import listdir import shutil -from pathlib import Path -from typing import Optional, Union, Tuple, List, Callable -from dataclasses import dataclass from argparse import ArgumentTypeError +from dataclasses import dataclass from enum import Enum +from os import listdir +from pathlib import Path +from typing import Callable, List, Optional, Tuple, Union @dataclass diff --git a/template/src_pkg/animal/__init__.pyi b/template/src_pkg/animal/__init__.pyi index becb717..deeff33 100644 --- a/template/src_pkg/animal/__init__.pyi +++ b/template/src_pkg/animal/__init__.pyi @@ -4,6 +4,6 @@ __all__ = [ "sample_animal", ] +from . import sample_animal from .horse import Horse from .mammalian import Mammal -from . import sample_animal \ No newline at end of file diff --git a/template/src_pkg/setup.py b/template/src_pkg/setup.py index 4b0501e..0787dbd 100644 --- a/template/src_pkg/setup.py +++ b/template/src_pkg/setup.py @@ -1,37 +1,12 @@ -from setuptools import find_packages, setup +from setuptools import find_namespace_packages, setup -version = "0.1.0" -DESCRIPTION = "A print package" -LONG_DESCRIPTION = """A simple class for print python objects with tails.""" +version = "0.2.0" setup( name="animal", version=version, - description=DESCRIPTION, - long_description=LONG_DESCRIPTION, - author="Sadegh Yazdani", - author_email="m.s.yazdani85@gmail.com", - license="GPLv3", - project_urls={ - "Source Code": "https://github.com/aerosadegh/komet/", - }, - packages=find_packages(), + packages=find_namespace_packages(), include_package_data=True, # Importants - install_requires=[], - keywords="printing", - classifiers=[ - "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Utilities", - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - ], - python_requires=">=3.8,<3.11", + python_requires="~=3.8", ) diff --git a/template/stubs-pkg/setup.py b/template/stubs-pkg/setup.py index 785792b..b441c19 100644 --- a/template/stubs-pkg/setup.py +++ b/template/stubs-pkg/setup.py @@ -1,39 +1,20 @@ # -*- coding: utf-8 -*- -import os -from setuptools import setup, find_packages, find_namespace_packages - +from setuptools import find_namespace_packages, find_packages, setup # Collect package from src_pkg Automatically -packages_name = find_namespace_packages(".")[0] +packages = find_namespace_packages(".") +packages_name = packages[0] original_package_name, _, _ = packages_name.partition("-") -# Going to create for stubs of - +# Create for stubs of setup( name=packages_name, - version="0.1.0", + version="0.2.0", description=f"Type annotations for {original_package_name}", long_description=f"{packages_name} package", - author="Sadegh Yazdani", - author_email="m.s.yazdani85@gmail.com", - license="GPLv3", - packages=[x for x in find_namespace_packages() if os.path.isdir(x)], + packages=[x for x in packages], include_package_data=True, # Important - install_requires=[], - classifiers=[ - "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Utilities", - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - ], python_requires=">=3.8,<3.11", )