Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
# Python Obfuscated Package Clean Architecture Template
a clean architecture template for python obfuscated package with stubs

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!
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pyarmor==6.6.2
pyarmor<=6.6.2,>=6.5.0
mypy==0.982
wheel
14 changes: 12 additions & 2 deletions src/obfpkg/core/packager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
35 changes: 26 additions & 9 deletions src/obfpkg/core/stub_maker.py
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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(
Expand Down Expand Up @@ -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()
Expand Down
49 changes: 21 additions & 28 deletions src/obfpkg/main.py
Original file line number Diff line number Diff line change
@@ -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"


Expand Down Expand Up @@ -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: <src>/../{STUBS_PACKAGE_DIR_NAME}",
help="The directory of stubs package project (needed 'setup.py' for build pkg-stubs).",
)

return parser
Expand All @@ -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",
Expand All @@ -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,
Expand Down Expand Up @@ -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()


Expand All @@ -146,28 +141,26 @@ 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:
mkdir(args.output_dir)
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,
Expand Down
6 changes: 4 additions & 2 deletions src/obfpkg/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -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
13 changes: 13 additions & 0 deletions src/obfpkg/utils/functions.py
Original file line number Diff line number Diff line change
@@ -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()
8 changes: 4 additions & 4 deletions src/obfpkg/utils/structs.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion template/src_pkg/animal/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ __all__ = [
"sample_animal",
]

from . import sample_animal
from .horse import Horse
from .mammalian import Mammal
from . import sample_animal
33 changes: 4 additions & 29 deletions template/src_pkg/setup.py
Original file line number Diff line number Diff line change
@@ -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",
)
31 changes: 6 additions & 25 deletions template/stubs-pkg/setup.py
Original file line number Diff line number Diff line change
@@ -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 <packages_name> for stubs of <original_package_name>

# Create <packages_name> for stubs of <original_package_name>

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",
)