AutoCython-zhang 2.3.3__tar.gz → 2.3.4__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.3 → autocython_zhang-2.3.4}/AutoCython/_version.py +1 -1
  2. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/AutoCython/compile.py +49 -47
  3. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/AutoCython_zhang.egg-info/PKG-INFO +5 -3
  4. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/AutoCython_zhang.egg-info/requires.txt +1 -1
  5. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/PKG-INFO +5 -3
  6. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/README.md +3 -1
  7. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/pyproject.toml +1 -1
  8. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/AutoCython/AutoCython.py +0 -0
  9. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/AutoCython/__init__.py +0 -0
  10. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/AutoCython/obfuscate.py +0 -0
  11. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/AutoCython/run_tasks.py +0 -0
  12. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/AutoCython/tools.py +0 -0
  13. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/AutoCython_zhang.egg-info/SOURCES.txt +0 -0
  14. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/AutoCython_zhang.egg-info/dependency_links.txt +0 -0
  15. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/AutoCython_zhang.egg-info/entry_points.txt +0 -0
  16. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/AutoCython_zhang.egg-info/top_level.txt +0 -0
  17. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/LICENSE +0 -0
  18. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/MANIFEST.in +0 -0
  19. {autocython_zhang-2.3.3 → autocython_zhang-2.3.4}/setup.cfg +0 -0
@@ -2,4 +2,4 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  """版本号模块 - 单一版本号来源"""
4
4
 
5
- __version__ = "2.3.3"
5
+ __version__ = "2.3.4"
@@ -1,13 +1,14 @@
1
- import os
2
- import sys
3
1
  import glob
2
+ import os
4
3
  import shutil
5
- import platform
6
- import tempfile
7
4
  import subprocess
5
+ import sys
6
+ import tempfile
8
7
 
9
8
  from .obfuscate import obfuscate_source
10
9
 
10
+ _SUPPORTED_CYTHON_MAJOR = 3
11
+
11
12
 
12
13
  def _strip_binary(path):
13
14
  """strip 符号表,失败静默"""
@@ -19,12 +20,41 @@ def _strip_binary(path):
19
20
  except Exception:
20
21
  pass
21
22
 
23
+
24
+ def _get_cython_major_version():
25
+ """返回当前运行环境的 Cython 主版本号。"""
26
+ try:
27
+ import Cython
28
+ except ImportError as exc: # pragma: no cover
29
+ raise RuntimeError('编译 Python 源码需要先安装 Cython。') from exc
30
+
31
+ version = getattr(Cython, '__version__', '0')
32
+ major = str(version).split('.', 1)[0]
33
+ try:
34
+ return int(major)
35
+ except ValueError as exc: # pragma: no cover
36
+ raise RuntimeError(f'无法解析 Cython 版本: {version}') from exc
37
+
38
+
39
+ def _ensure_supported_cython():
40
+ """阻断已知会破坏运行时注解的旧版 Cython。"""
41
+ major = _get_cython_major_version()
42
+ if major != _SUPPORTED_CYTHON_MAJOR:
43
+ raise RuntimeError(
44
+ '检测到不受支持的 Cython 版本。'
45
+ 'AutoCython 要求 Cython>=3,<4;'
46
+ '因为 Cython<3 可能剥离编译产物中的运行时注解,'
47
+ '导致 Pydantic/FastAPI/dataclass 的字段发现失效。'
48
+ )
49
+
50
+
22
51
  def get_platform_extension() -> str:
23
52
  """返回当前平台的扩展名"""
24
53
  if sys.platform.startswith('win'):
25
54
  return '.pyd'
26
55
  return '.so'
27
56
 
57
+
28
58
  def compile_to_binary(file_path: str, del_source=False, obfuscate=True, obfuscate_seed=None):
29
59
  """
30
60
  将指定的 Python 文件(.py)通过 Cython 编译为二进制扩展文件
@@ -35,39 +65,30 @@ def compile_to_binary(file_path: str, del_source=False, obfuscate=True, obfuscat
35
65
  :param obfuscate_seed: 混淆随机种子(None 表示不固定)
36
66
  :return: 生成的二进制文件路径(与输入保持相同的路径类型)
37
67
  """
38
- # 保存原始路径类型(相对/绝对)
39
- is_absolute = os.path.isabs(file_path)
68
+ _ensure_supported_cython()
40
69
 
41
- # 获取绝对路径用于内部操作
70
+ is_absolute = os.path.isabs(file_path)
42
71
  abs_file_path = os.path.abspath(file_path)
43
72
 
44
73
  if not os.path.isfile(abs_file_path):
45
74
  raise FileNotFoundError(f"FileNotFoundError: {file_path}.")
46
75
 
47
- # 获取文件名和目录
48
76
  file_name = os.path.basename(abs_file_path)
49
77
  module_name, ext = os.path.splitext(file_name)
50
- source_dir = os.path.dirname(abs_file_path) # 源文件所在目录(绝对路径)
78
+ source_dir = os.path.dirname(abs_file_path)
51
79
 
52
- if ext != ".py":
80
+ if ext != '.py':
53
81
  raise ValueError(f"ValueError: The file {file_path} is not a valid Python file (.py)!")
54
82
 
55
- # 连字符文件名转下划线(Cython 模块名不允许连字符)
56
83
  safe_module_name = module_name.replace('-', '_')
57
84
  safe_file_name = safe_module_name + ext
58
-
59
- # 获取平台特定扩展名
60
85
  target_ext = get_platform_extension()
61
-
62
- # 创建临时工作目录
63
86
  temp_dir = tempfile.mkdtemp()
64
87
 
65
88
  try:
66
- # 将目标文件复制到临时目录(使用安全文件名)
67
89
  temp_file_path = os.path.join(temp_dir, safe_file_name)
68
90
  shutil.copy2(abs_file_path, temp_file_path)
69
91
 
70
- # 混淆源码(失败则显式抛错,避免静默降级)
71
92
  if obfuscate:
72
93
  try:
73
94
  with open(temp_file_path, 'r', encoding='utf-8') as f:
@@ -78,12 +99,10 @@ def compile_to_binary(file_path: str, del_source=False, obfuscate=True, obfuscat
78
99
  except Exception as exc:
79
100
  raise RuntimeError(f"Obfuscation failed for {file_path}: {exc}") from exc
80
101
 
81
- # 创建临时的 setup.py 文件
82
102
  setup_code = f"""
83
103
  from setuptools import setup
84
104
  from Cython.Build import cythonize
85
105
 
86
- # 编译器指令
87
106
  compiler_directives = {{
88
107
  'language_level': '3',
89
108
  'annotation_typing': False,
@@ -101,19 +120,18 @@ setup(
101
120
  )
102
121
  )
103
122
  """
104
- setup_path = os.path.join(temp_dir, "setup.py")
105
- with open(setup_path, "w", encoding='utf-8') as f:
123
+ setup_path = os.path.join(temp_dir, 'setup.py')
124
+ with open(setup_path, 'w', encoding='utf-8') as f:
106
125
  f.write(setup_code)
107
126
 
108
- # 执行编译命令
109
- command = [sys.executable, "setup.py", "build_ext", "--inplace"]
127
+ command = [sys.executable, 'setup.py', 'build_ext', '--inplace']
110
128
  try:
111
129
  result = subprocess.run(
112
130
  command,
113
131
  cwd=temp_dir,
114
132
  stdout=subprocess.PIPE,
115
133
  stderr=subprocess.PIPE,
116
- timeout=300
134
+ timeout=300,
117
135
  )
118
136
  except subprocess.TimeoutExpired:
119
137
  raise RuntimeError(f"Compilation timed out after 300s: {file_path}")
@@ -122,57 +140,41 @@ setup(
122
140
  error_msg = result.stderr.decode('utf-8', errors='replace')
123
141
  raise RuntimeError(f"Compilation failed: {error_msg}")
124
142
 
125
- # 查找生成的二进制文件
126
143
  pattern = os.path.join(temp_dir, f"{safe_module_name}*{target_ext}")
127
144
  matches = glob.glob(pattern)
128
-
129
145
  if not matches:
130
146
  pattern = os.path.join(temp_dir, f"*{safe_module_name}*{target_ext}")
131
147
  matches = glob.glob(pattern)
132
148
 
133
149
  if not matches:
134
- raise FileNotFoundError(f"FileNotFoundError: The file {file_path} is not a valid Python file (.py)! Generated file {target_ext} not found, in {temp_dir} possible file: {os.listdir(temp_dir)}")
150
+ raise FileNotFoundError(
151
+ f"FileNotFoundError: The file {file_path} is not a valid Python file (.py)! "
152
+ f"Generated file {target_ext} not found, in {temp_dir} possible file: {os.listdir(temp_dir)}"
153
+ )
135
154
 
136
- # 取最新生成的文件
137
155
  generated_file = max(matches, key=os.path.getmtime)
138
-
139
- # 获取源文件的目录(使用原始路径类型)
140
- if is_absolute:
141
- output_dir = source_dir
142
- else:
143
- # 如果输入是相对路径,保持相对路径
144
- output_dir = os.path.dirname(file_path) or '.'
145
-
146
- # 创建目标目录(如果不存在)
156
+ output_dir = source_dir if is_absolute else (os.path.dirname(file_path) or '.')
147
157
  if output_dir and not os.path.exists(output_dir):
148
158
  os.makedirs(output_dir, exist_ok=True)
149
159
 
150
- # 目标文件路径(保持原始路径类型)
151
160
  output_file_name = os.path.basename(generated_file)
152
161
  output_path = os.path.join(output_dir, output_file_name)
153
-
154
- # 移动文件(先删除已存在的目标)
155
162
  if os.path.exists(output_path):
156
163
  os.remove(output_path)
157
164
  shutil.move(generated_file, output_path)
158
165
 
159
- # strip 符号表
160
166
  _strip_binary(output_path)
161
167
 
162
168
  if del_source:
163
169
  os.remove(file_path)
164
170
 
165
- # 返回与输入相同类型的路径
166
171
  return output_path
167
172
  finally:
168
- # 清理临时目录
169
173
  shutil.rmtree(temp_dir, ignore_errors=True)
170
174
 
171
- # 测试函数
172
- if __name__ == "__main__": # pragma: no cover
173
- # 替换为你的 Python 文件路径
174
- target_file = "test/example.py" # 请确保文件路径正确
175
175
 
176
+ if __name__ == '__main__': # pragma: no cover
177
+ target_file = 'test/example.py'
176
178
  try:
177
179
  output_file = compile_to_binary(target_file)
178
180
  print(output_file)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AutoCython-zhang
3
- Version: 2.3.3
3
+ Version: 2.3.4
4
4
  Summary: 自动Cython,使用Cython批量编译.py文件为.pyd文件!
5
5
  Author-email: zhang_gavin <qq814608@163.com>
6
6
  License-Expression: MIT
@@ -14,7 +14,7 @@ Requires-Python: >=3.9
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
16
  Requires-Dist: setuptools
17
- Requires-Dist: cython
17
+ Requires-Dist: cython<4,>=3
18
18
  Requires-Dist: rich
19
19
  Dynamic: license-file
20
20
 
@@ -47,11 +47,13 @@ AutoCython 是一个 Python 源码保护工具,通过 Cython 编译 + AST 混
47
47
  pip install AutoCython-zhang
48
48
  ```
49
49
 
50
+ > 当前编译链要求 `Cython>=3,<4`。该约束用于避免旧版 Cython 在编译后破坏 `Pydantic` / `FastAPI` / `dataclass` 等依赖运行时注解的框架行为。
51
+
50
52
  ### 依赖
51
53
 
52
54
  | 包 | 用途 |
53
55
  |---|------|
54
- | `cython` | Python → C 编译核心 |
56
+ | `cython>=3,<4` | Python → C 编译核心;确保运行时注解不被旧版编译链破坏 |
55
57
  | `setuptools` | 构建扩展模块 |
56
58
  | `rich` | 终端实时进度面板 |
57
59
 
@@ -1,3 +1,3 @@
1
1
  setuptools
2
- cython
2
+ cython<4,>=3
3
3
  rich
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AutoCython-zhang
3
- Version: 2.3.3
3
+ Version: 2.3.4
4
4
  Summary: 自动Cython,使用Cython批量编译.py文件为.pyd文件!
5
5
  Author-email: zhang_gavin <qq814608@163.com>
6
6
  License-Expression: MIT
@@ -14,7 +14,7 @@ Requires-Python: >=3.9
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
16
  Requires-Dist: setuptools
17
- Requires-Dist: cython
17
+ Requires-Dist: cython<4,>=3
18
18
  Requires-Dist: rich
19
19
  Dynamic: license-file
20
20
 
@@ -47,11 +47,13 @@ AutoCython 是一个 Python 源码保护工具,通过 Cython 编译 + AST 混
47
47
  pip install AutoCython-zhang
48
48
  ```
49
49
 
50
+ > 当前编译链要求 `Cython>=3,<4`。该约束用于避免旧版 Cython 在编译后破坏 `Pydantic` / `FastAPI` / `dataclass` 等依赖运行时注解的框架行为。
51
+
50
52
  ### 依赖
51
53
 
52
54
  | 包 | 用途 |
53
55
  |---|------|
54
- | `cython` | Python → C 编译核心 |
56
+ | `cython>=3,<4` | Python → C 编译核心;确保运行时注解不被旧版编译链破坏 |
55
57
  | `setuptools` | 构建扩展模块 |
56
58
  | `rich` | 终端实时进度面板 |
57
59
 
@@ -27,11 +27,13 @@ AutoCython 是一个 Python 源码保护工具,通过 Cython 编译 + AST 混
27
27
  pip install AutoCython-zhang
28
28
  ```
29
29
 
30
+ > 当前编译链要求 `Cython>=3,<4`。该约束用于避免旧版 Cython 在编译后破坏 `Pydantic` / `FastAPI` / `dataclass` 等依赖运行时注解的框架行为。
31
+
30
32
  ### 依赖
31
33
 
32
34
  | 包 | 用途 |
33
35
  |---|------|
34
- | `cython` | Python → C 编译核心 |
36
+ | `cython>=3,<4` | Python → C 编译核心;确保运行时注解不被旧版编译链破坏 |
35
37
  | `setuptools` | 构建扩展模块 |
36
38
  | `rich` | 终端实时进度面板 |
37
39
 
@@ -18,7 +18,7 @@ classifiers = [
18
18
  ]
19
19
  dependencies = [
20
20
  "setuptools",
21
- "cython",
21
+ "cython>=3,<4",
22
22
  "rich",
23
23
  ]
24
24