AutoCython-zhang 2.3.4__tar.gz → 2.3.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython/_version.py +1 -1
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython/compile.py +88 -7
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython/obfuscate.py +1 -2
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython_zhang.egg-info/PKG-INFO +2 -1
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/PKG-INFO +2 -1
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/README.md +1 -0
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython/AutoCython.py +0 -0
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython/__init__.py +0 -0
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython/run_tasks.py +0 -0
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython/tools.py +0 -0
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython_zhang.egg-info/SOURCES.txt +0 -0
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython_zhang.egg-info/dependency_links.txt +0 -0
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython_zhang.egg-info/entry_points.txt +0 -0
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython_zhang.egg-info/requires.txt +0 -0
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython_zhang.egg-info/top_level.txt +0 -0
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/LICENSE +0 -0
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/MANIFEST.in +0 -0
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/pyproject.toml +0 -0
- {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/setup.cfg +0 -0
|
@@ -55,6 +55,78 @@ def get_platform_extension() -> str:
|
|
|
55
55
|
return '.so'
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
def _infer_compile_root(abs_file_path: str) -> str:
|
|
59
|
+
"""推导编译根目录。
|
|
60
|
+
|
|
61
|
+
对包内模块,返回最外层 package 目录的父目录,以便保留完整 qualified module name。
|
|
62
|
+
对普通脚本,返回源码所在目录,保持现有行为。
|
|
63
|
+
"""
|
|
64
|
+
source_dir = os.path.dirname(abs_file_path)
|
|
65
|
+
current_dir = source_dir
|
|
66
|
+
top_level_package_dir = None
|
|
67
|
+
|
|
68
|
+
while os.path.isfile(os.path.join(current_dir, '__init__.py')):
|
|
69
|
+
top_level_package_dir = current_dir
|
|
70
|
+
parent_dir = os.path.dirname(current_dir)
|
|
71
|
+
if parent_dir == current_dir:
|
|
72
|
+
break
|
|
73
|
+
current_dir = parent_dir
|
|
74
|
+
|
|
75
|
+
if top_level_package_dir is None:
|
|
76
|
+
return source_dir
|
|
77
|
+
|
|
78
|
+
return os.path.dirname(top_level_package_dir)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _resolve_module_layout(abs_file_path: str):
|
|
82
|
+
"""解析编译布局,保留 package 相对路径与 qualified module name。"""
|
|
83
|
+
compile_root = _infer_compile_root(abs_file_path)
|
|
84
|
+
rel_path = os.path.relpath(abs_file_path, compile_root)
|
|
85
|
+
rel_dir = os.path.dirname(rel_path)
|
|
86
|
+
file_name = os.path.basename(rel_path)
|
|
87
|
+
module_name, ext = os.path.splitext(file_name)
|
|
88
|
+
safe_module_name = module_name.replace('-', '_')
|
|
89
|
+
|
|
90
|
+
safe_rel_parts = [part for part in rel_dir.split(os.sep) if part and part != '.']
|
|
91
|
+
safe_rel_parts.append(safe_module_name + ext)
|
|
92
|
+
safe_rel_path = os.path.join(*safe_rel_parts) if safe_rel_parts else safe_module_name + ext
|
|
93
|
+
|
|
94
|
+
module_parts = [part for part in rel_dir.split(os.sep) if part and part != '.']
|
|
95
|
+
module_parts.append(safe_module_name)
|
|
96
|
+
qualified_module_name = '.'.join(module_parts)
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
'compile_root': compile_root,
|
|
100
|
+
'rel_path': rel_path,
|
|
101
|
+
'safe_rel_path': safe_rel_path,
|
|
102
|
+
'safe_module_name': safe_module_name,
|
|
103
|
+
'qualified_module_name': qualified_module_name,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _copy_package_inits(abs_file_path: str, compile_root: str, temp_dir: str) -> None:
|
|
108
|
+
"""复制包链上的 __init__.py,确保 build_ext --inplace 输出落到正确包路径。"""
|
|
109
|
+
current_dir = os.path.dirname(abs_file_path)
|
|
110
|
+
|
|
111
|
+
while True:
|
|
112
|
+
init_path = os.path.join(current_dir, '__init__.py')
|
|
113
|
+
if not os.path.isfile(init_path):
|
|
114
|
+
break
|
|
115
|
+
|
|
116
|
+
rel_init_path = os.path.relpath(init_path, compile_root)
|
|
117
|
+
temp_init_path = os.path.join(temp_dir, rel_init_path)
|
|
118
|
+
os.makedirs(os.path.dirname(temp_init_path), exist_ok=True)
|
|
119
|
+
shutil.copy2(init_path, temp_init_path)
|
|
120
|
+
|
|
121
|
+
if current_dir == compile_root:
|
|
122
|
+
break
|
|
123
|
+
|
|
124
|
+
parent_dir = os.path.dirname(current_dir)
|
|
125
|
+
if parent_dir == current_dir:
|
|
126
|
+
break
|
|
127
|
+
current_dir = parent_dir
|
|
128
|
+
|
|
129
|
+
|
|
58
130
|
def compile_to_binary(file_path: str, del_source=False, obfuscate=True, obfuscate_seed=None):
|
|
59
131
|
"""
|
|
60
132
|
将指定的 Python 文件(.py)通过 Cython 编译为二进制扩展文件
|
|
@@ -74,19 +146,21 @@ def compile_to_binary(file_path: str, del_source=False, obfuscate=True, obfuscat
|
|
|
74
146
|
raise FileNotFoundError(f"FileNotFoundError: {file_path}.")
|
|
75
147
|
|
|
76
148
|
file_name = os.path.basename(abs_file_path)
|
|
77
|
-
|
|
149
|
+
_, ext = os.path.splitext(file_name)
|
|
78
150
|
source_dir = os.path.dirname(abs_file_path)
|
|
79
151
|
|
|
80
152
|
if ext != '.py':
|
|
81
153
|
raise ValueError(f"ValueError: The file {file_path} is not a valid Python file (.py)!")
|
|
82
154
|
|
|
83
|
-
|
|
84
|
-
|
|
155
|
+
module_layout = _resolve_module_layout(abs_file_path)
|
|
156
|
+
safe_module_name = module_layout['safe_module_name']
|
|
85
157
|
target_ext = get_platform_extension()
|
|
86
158
|
temp_dir = tempfile.mkdtemp()
|
|
87
159
|
|
|
88
160
|
try:
|
|
89
|
-
temp_file_path = os.path.join(temp_dir,
|
|
161
|
+
temp_file_path = os.path.join(temp_dir, module_layout['safe_rel_path'])
|
|
162
|
+
os.makedirs(os.path.dirname(temp_file_path), exist_ok=True)
|
|
163
|
+
_copy_package_inits(abs_file_path, module_layout['compile_root'], temp_dir)
|
|
90
164
|
shutil.copy2(abs_file_path, temp_file_path)
|
|
91
165
|
|
|
92
166
|
if obfuscate:
|
|
@@ -101,6 +175,7 @@ def compile_to_binary(file_path: str, del_source=False, obfuscate=True, obfuscat
|
|
|
101
175
|
|
|
102
176
|
setup_code = f"""
|
|
103
177
|
from setuptools import setup
|
|
178
|
+
from setuptools import Extension
|
|
104
179
|
from Cython.Build import cythonize
|
|
105
180
|
|
|
106
181
|
compiler_directives = {{
|
|
@@ -114,7 +189,12 @@ compiler_directives = {{
|
|
|
114
189
|
|
|
115
190
|
setup(
|
|
116
191
|
ext_modules=cythonize(
|
|
117
|
-
|
|
192
|
+
[
|
|
193
|
+
Extension(
|
|
194
|
+
{repr(module_layout['qualified_module_name'])},
|
|
195
|
+
[{repr(module_layout['safe_rel_path'])}],
|
|
196
|
+
)
|
|
197
|
+
],
|
|
118
198
|
compiler_directives=compiler_directives,
|
|
119
199
|
force=True
|
|
120
200
|
)
|
|
@@ -140,10 +220,11 @@ setup(
|
|
|
140
220
|
error_msg = result.stderr.decode('utf-8', errors='replace')
|
|
141
221
|
raise RuntimeError(f"Compilation failed: {error_msg}")
|
|
142
222
|
|
|
143
|
-
|
|
223
|
+
temp_output_dir = os.path.dirname(temp_file_path) or temp_dir
|
|
224
|
+
pattern = os.path.join(temp_output_dir, f"{safe_module_name}*{target_ext}")
|
|
144
225
|
matches = glob.glob(pattern)
|
|
145
226
|
if not matches:
|
|
146
|
-
pattern = os.path.join(
|
|
227
|
+
pattern = os.path.join(temp_output_dir, f"*{safe_module_name}*{target_ext}")
|
|
147
228
|
matches = glob.glob(pattern)
|
|
148
229
|
|
|
149
230
|
if not matches:
|
|
@@ -482,7 +482,7 @@ class _OpaquePredicateInserter(ast.NodeTransformer):
|
|
|
482
482
|
|
|
483
483
|
|
|
484
484
|
def obfuscate_source(source_code: str, seed=None) -> str:
|
|
485
|
-
"""
|
|
485
|
+
"""对源码执行六重 AST 变换:去 docstring、局部变量重命名、
|
|
486
486
|
字符串加密、常量折叠、控制流平坦化、虚假分支。
|
|
487
487
|
ast.unparse() 天然丢弃所有注释。
|
|
488
488
|
|
|
@@ -493,7 +493,6 @@ def obfuscate_source(source_code: str, seed=None) -> str:
|
|
|
493
493
|
rng = random.Random(seed) if seed is not None else random
|
|
494
494
|
tree = ast.parse(source_code)
|
|
495
495
|
tree = _DocstringRemover().visit(tree)
|
|
496
|
-
tree = _AnnotationRemover().visit(tree)
|
|
497
496
|
tree = _LocalVarRenamer().visit(tree)
|
|
498
497
|
tree = _ControlFlowFlattener(rng=rng).visit(tree)
|
|
499
498
|
tree = _OpaquePredicateInserter(rng=rng).visit(tree)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AutoCython-zhang
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.6
|
|
4
4
|
Summary: 自动Cython,使用Cython批量编译.py文件为.pyd文件!
|
|
5
5
|
Author-email: zhang_gavin <qq814608@163.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -37,6 +37,7 @@ AutoCython 是一个 Python 源码保护工具,通过 Cython 编译 + AST 混
|
|
|
37
37
|
- **并发编译** — 基于 `ThreadPoolExecutor` 的多线程并行编译,可配置并发数
|
|
38
38
|
- **实时进度面板** — 基于 Rich 的实时任务状态表格、进度条、耗时统计
|
|
39
39
|
- **跨平台** — 支持 Linux、macOS、Windows,自动适配 `.so`/`.pyd` 扩展名
|
|
40
|
+
- **包路径保真** — 编译包内模块时保留 qualified module name,兼容 Pydantic / FastAPI 等依赖运行时模块命名空间的框架
|
|
40
41
|
- **可复现构建** — 通过 `--seed` 参数固定混淆随机种子
|
|
41
42
|
- **智能排除** — 自动跳过 `__init__.py`、虚拟环境、构建目录;支持 `# AutoCython No Compile` 标记豁免
|
|
42
43
|
- **中英双语** — CLI 帮助信息和进度面板自动适配系统语言
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AutoCython-zhang
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.6
|
|
4
4
|
Summary: 自动Cython,使用Cython批量编译.py文件为.pyd文件!
|
|
5
5
|
Author-email: zhang_gavin <qq814608@163.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -37,6 +37,7 @@ AutoCython 是一个 Python 源码保护工具,通过 Cython 编译 + AST 混
|
|
|
37
37
|
- **并发编译** — 基于 `ThreadPoolExecutor` 的多线程并行编译,可配置并发数
|
|
38
38
|
- **实时进度面板** — 基于 Rich 的实时任务状态表格、进度条、耗时统计
|
|
39
39
|
- **跨平台** — 支持 Linux、macOS、Windows,自动适配 `.so`/`.pyd` 扩展名
|
|
40
|
+
- **包路径保真** — 编译包内模块时保留 qualified module name,兼容 Pydantic / FastAPI 等依赖运行时模块命名空间的框架
|
|
40
41
|
- **可复现构建** — 通过 `--seed` 参数固定混淆随机种子
|
|
41
42
|
- **智能排除** — 自动跳过 `__init__.py`、虚拟环境、构建目录;支持 `# AutoCython No Compile` 标记豁免
|
|
42
43
|
- **中英双语** — CLI 帮助信息和进度面板自动适配系统语言
|
|
@@ -17,6 +17,7 @@ AutoCython 是一个 Python 源码保护工具,通过 Cython 编译 + AST 混
|
|
|
17
17
|
- **并发编译** — 基于 `ThreadPoolExecutor` 的多线程并行编译,可配置并发数
|
|
18
18
|
- **实时进度面板** — 基于 Rich 的实时任务状态表格、进度条、耗时统计
|
|
19
19
|
- **跨平台** — 支持 Linux、macOS、Windows,自动适配 `.so`/`.pyd` 扩展名
|
|
20
|
+
- **包路径保真** — 编译包内模块时保留 qualified module name,兼容 Pydantic / FastAPI 等依赖运行时模块命名空间的框架
|
|
20
21
|
- **可复现构建** — 通过 `--seed` 参数固定混淆随机种子
|
|
21
22
|
- **智能排除** — 自动跳过 `__init__.py`、虚拟环境、构建目录;支持 `# AutoCython No Compile` 标记豁免
|
|
22
23
|
- **中英双语** — CLI 帮助信息和进度面板自动适配系统语言
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython_zhang.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython_zhang.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|