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.
Files changed (19) hide show
  1. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython/_version.py +1 -1
  2. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython/compile.py +88 -7
  3. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython/obfuscate.py +1 -2
  4. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython_zhang.egg-info/PKG-INFO +2 -1
  5. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/PKG-INFO +2 -1
  6. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/README.md +1 -0
  7. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython/AutoCython.py +0 -0
  8. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython/__init__.py +0 -0
  9. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython/run_tasks.py +0 -0
  10. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython/tools.py +0 -0
  11. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython_zhang.egg-info/SOURCES.txt +0 -0
  12. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython_zhang.egg-info/dependency_links.txt +0 -0
  13. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython_zhang.egg-info/entry_points.txt +0 -0
  14. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython_zhang.egg-info/requires.txt +0 -0
  15. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/AutoCython_zhang.egg-info/top_level.txt +0 -0
  16. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/LICENSE +0 -0
  17. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/MANIFEST.in +0 -0
  18. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/pyproject.toml +0 -0
  19. {autocython_zhang-2.3.4 → autocython_zhang-2.3.6}/setup.cfg +0 -0
@@ -2,4 +2,4 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  """版本号模块 - 单一版本号来源"""
4
4
 
5
- __version__ = "2.3.4"
5
+ __version__ = "2.3.6"
@@ -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
- module_name, ext = os.path.splitext(file_name)
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
- safe_module_name = module_name.replace('-', '_')
84
- safe_file_name = safe_module_name + ext
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, safe_file_name)
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
- {repr(safe_file_name)},
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
- pattern = os.path.join(temp_dir, f"{safe_module_name}*{target_ext}")
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(temp_dir, f"*{safe_module_name}*{target_ext}")
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
- """对源码执行七重 AST 变换:去 docstring、去 annotation、局部变量重命名、
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.4
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.4
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 帮助信息和进度面板自动适配系统语言