AutoCython-zhang 2.3.6__tar.gz → 2.3.7__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.6 → autocython_zhang-2.3.7}/AutoCython/_version.py +1 -1
  2. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/AutoCython/obfuscate.py +193 -80
  3. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/AutoCython_zhang.egg-info/PKG-INFO +5 -5
  4. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/PKG-INFO +5 -5
  5. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/README.md +4 -4
  6. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/pyproject.toml +1 -1
  7. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/AutoCython/AutoCython.py +0 -0
  8. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/AutoCython/__init__.py +0 -0
  9. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/AutoCython/compile.py +0 -0
  10. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/AutoCython/run_tasks.py +0 -0
  11. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/AutoCython/tools.py +0 -0
  12. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/AutoCython_zhang.egg-info/SOURCES.txt +0 -0
  13. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/AutoCython_zhang.egg-info/dependency_links.txt +0 -0
  14. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/AutoCython_zhang.egg-info/entry_points.txt +0 -0
  15. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/AutoCython_zhang.egg-info/requires.txt +0 -0
  16. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/AutoCython_zhang.egg-info/top_level.txt +0 -0
  17. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/LICENSE +0 -0
  18. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/MANIFEST.in +0 -0
  19. {autocython_zhang-2.3.6 → autocython_zhang-2.3.7}/setup.cfg +0 -0
@@ -2,4 +2,4 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  """版本号模块 - 单一版本号来源"""
4
4
 
5
- __version__ = "2.3.6"
5
+ __version__ = "2.3.7"
@@ -1,4 +1,8 @@
1
- """AST 混淆模块:去 docstring、去 annotation、局部变量重命名、字符串加密、常量折叠、控制流平坦化、虚假分支"""
1
+ """AST 混淆模块:去 docstring、局部变量重命名、字符串加密、常量折叠、控制流平坦化、虚假分支。
2
+
3
+ 注解(annotation)默认完整保留,以兼容 FastAPI/Pydantic/dataclass/attrs 等依赖运行时
4
+ ``__annotations__`` 的框架;字符串与常量混淆会跳过 annotation 子树。
5
+ """
2
6
  import ast
3
7
  import copy
4
8
  import hashlib
@@ -6,6 +10,16 @@ import random
6
10
 
7
11
  _UNSAFE_CALLS = frozenset({'globals', 'locals', 'eval', 'exec', 'vars'})
8
12
  _PATTERN_SKIP_ATTR = '_autocython_skip_literal_obf'
13
+ _SCOPE_BOUNDARY_NODES = (
14
+ ast.FunctionDef,
15
+ ast.AsyncFunctionDef,
16
+ ast.ClassDef,
17
+ ast.Lambda,
18
+ ast.ListComp,
19
+ ast.SetComp,
20
+ ast.DictComp,
21
+ ast.GeneratorExp,
22
+ )
9
23
 
10
24
 
11
25
  def _has_unsafe_call(node):
@@ -21,14 +35,18 @@ def _has_unsafe_call(node):
21
35
 
22
36
 
23
37
  def _walk_no_nested_scope(body):
24
- """遍历 body 中的节点,不穿透嵌套函数/类定义"""
38
+ """遍历 body 中的节点,不穿透嵌套词法作用域。
39
+
40
+ Python 3 中 lambda 与各类 comprehension 也会创建独立词法作用域。
41
+ 局部变量重命名若穿透这些节点,容易把 ``lambda e: ...`` 的参数
42
+ 与外层 ``for e in ...`` 的重命名混在一起,生成
43
+ ``free variable ... referenced before assignment`` 一类运行时错误。
44
+ """
25
45
  for node in body:
26
46
  yield node
27
- if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
47
+ if isinstance(node, _SCOPE_BOUNDARY_NODES):
28
48
  continue
29
49
  for child in ast.iter_child_nodes(node):
30
- if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
31
- continue
32
50
  yield from _walk_no_nested_scope([child])
33
51
 
34
52
 
@@ -49,6 +67,34 @@ def _mark_pattern_constants(pattern):
49
67
  setattr(child, _PATTERN_SKIP_ATTR, True)
50
68
 
51
69
 
70
+ def _mark_annotation_constants(tree):
71
+ """标记 annotation 子树内常量,避免字符串加密/常量折叠改变运行时注解语义。"""
72
+
73
+ def mark(node):
74
+ if node is None:
75
+ return
76
+ for child in ast.walk(node):
77
+ if isinstance(child, ast.Constant):
78
+ setattr(child, _PATTERN_SKIP_ATTR, True)
79
+
80
+ def mark_arg_annotations(args):
81
+ all_args = args.posonlyargs + args.args + args.kwonlyargs
82
+ if args.vararg:
83
+ all_args.append(args.vararg)
84
+ if args.kwarg:
85
+ all_args.append(args.kwarg)
86
+ for arg in all_args:
87
+ mark(arg.annotation)
88
+
89
+ for node in ast.walk(tree):
90
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
91
+ mark_arg_annotations(node.args)
92
+ mark(node.returns)
93
+ elif isinstance(node, ast.AnnAssign):
94
+ mark(node.annotation)
95
+ return tree
96
+
97
+
52
98
  class _DocstringRemover(ast.NodeTransformer):
53
99
  """移除 module/class/function 首部的 docstring"""
54
100
 
@@ -74,80 +120,151 @@ class _DocstringRemover(ast.NodeTransformer):
74
120
  return self._strip_docstring(node)
75
121
 
76
122
 
77
- class _AnnotationRemover(ast.NodeTransformer):
78
- """清除非类体内的变量注解。
79
- 保留函数参数/返回值注解和类体内注解赋值,
80
- 因为 FastAPI/Pydantic/dataclass/attrs 等框架在运行时依赖 __annotations__。"""
123
+ def _collect_names(nodes, ctx_type, *, skip_nested=True):
124
+ """收集一组节点中的 Name;默认不穿透新的词法作用域。"""
125
+ names = set()
126
+ walker = _walk_no_nested_scope(nodes) if skip_nested else ast.walk(ast.Module(body=list(nodes), type_ignores=[]))
127
+ for child in walker:
128
+ if isinstance(child, ast.Name) and isinstance(child.ctx, ctx_type):
129
+ names.add(child.id)
130
+ return names
131
+
132
+
133
+ def _collect_argument_names(args):
134
+ """收集函数/lambda 参数名。"""
135
+ names = set()
136
+ for arg in args.args + args.posonlyargs + args.kwonlyargs:
137
+ names.add(arg.arg)
138
+ if args.vararg:
139
+ names.add(args.vararg.arg)
140
+ if args.kwarg:
141
+ names.add(args.kwarg.arg)
142
+ return names
143
+
144
+
145
+ def _collect_argument_expression_nodes(args):
146
+ """收集默认值与注解表达式,它们在外层作用域求值。"""
147
+ nodes = []
148
+ nodes.extend(args.defaults)
149
+ nodes.extend(default for default in args.kw_defaults if default is not None)
150
+ all_args = args.posonlyargs + args.args + args.kwonlyargs
151
+ if args.vararg:
152
+ all_args.append(args.vararg)
153
+ if args.kwarg:
154
+ all_args.append(args.kwarg)
155
+ for arg in all_args:
156
+ if arg.annotation is not None:
157
+ nodes.append(arg.annotation)
158
+ return nodes
159
+
160
+
161
+ def _collect_nonlocal_global_names(body):
162
+ """收集当前作用域直接声明的 nonlocal/global 名称。"""
163
+ nonlocal_names = set()
164
+ global_names = set()
165
+ for child in _walk_no_nested_scope(body):
166
+ if isinstance(child, ast.Nonlocal):
167
+ nonlocal_names.update(child.names)
168
+ elif isinstance(child, ast.Global):
169
+ global_names.update(child.names)
170
+ return nonlocal_names, global_names
171
+
172
+
173
+ def _collect_direct_bound_names(body):
174
+ """收集当前作用域直接绑定的名称,不穿透嵌套作用域。"""
175
+ bound = _collect_names(body, ast.Store)
176
+ for child in _walk_no_nested_scope(body):
177
+ if isinstance(child, ast.ExceptHandler) and child.name:
178
+ bound.add(child.name)
179
+ elif isinstance(child, (ast.Import, ast.ImportFrom)):
180
+ for alias in child.names:
181
+ bound.add(alias.asname or alias.name.split('.', 1)[0])
182
+ return bound
183
+
184
+
185
+ def _collect_load_names_no_nested(nodes):
186
+ """收集 Load 名称,不穿透嵌套作用域。"""
187
+ return _collect_names(nodes, ast.Load)
188
+
189
+
190
+ def _collect_nested_scope_nodes(body):
191
+ """收集 body 中的直接/间接嵌套词法作用域节点。"""
192
+ nested = []
193
+ for node in body:
194
+ if isinstance(node, _SCOPE_BOUNDARY_NODES):
195
+ nested.append(node)
196
+ continue
197
+ for child in ast.iter_child_nodes(node):
198
+ if isinstance(child, _SCOPE_BOUNDARY_NODES):
199
+ nested.append(child)
200
+ else:
201
+ nested.extend(_collect_nested_scope_nodes([child]))
202
+ return nested
81
203
 
82
- def __init__(self):
83
- self._scope_stack = ['module']
84
204
 
85
- def visit_FunctionDef(self, node):
86
- self._scope_stack.append('function')
87
- self.generic_visit(node)
88
- self._scope_stack.pop()
89
- return node
205
+ def _collect_function_like_free_refs(node, local_names):
206
+ """收集嵌套函数/lambda 中引用的外层变量名。"""
207
+ body = node.body if isinstance(node.body, list) else [node.body]
208
+ param_names = _collect_argument_names(node.args)
209
+ nonlocal_names, global_names = _collect_nonlocal_global_names(body)
210
+ nested_locals = _collect_direct_bound_names(body)
211
+ nested_locals.update(param_names)
212
+ nested_locals -= nonlocal_names
90
213
 
91
- visit_AsyncFunctionDef = visit_FunctionDef
214
+ free = set()
215
+ outer_expr_nodes = _collect_argument_expression_nodes(node.args)
216
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
217
+ outer_expr_nodes.extend(node.decorator_list)
218
+ if node.returns is not None:
219
+ outer_expr_nodes.append(node.returns)
220
+ free.update(_collect_load_names_no_nested(outer_expr_nodes) & local_names)
221
+
222
+ for name in _collect_load_names_no_nested(body):
223
+ if name in local_names and name not in nested_locals and name not in global_names:
224
+ free.add(name)
225
+
226
+ free.update(_collect_nested_free_refs(body, local_names))
227
+ return free
92
228
 
93
- def visit_ClassDef(self, node):
94
- self._scope_stack.append('class')
95
- self.generic_visit(node)
96
- self._scope_stack.pop()
97
- if not node.body:
98
- node.body.append(ast.Pass())
99
- return node
100
229
 
101
- def visit_AnnAssign(self, node):
102
- self.generic_visit(node)
103
- # 类体内的注解赋值必须保留(Pydantic/dataclass 等框架依赖 __annotations__)
104
- if self._scope_stack[-1] == 'class':
105
- return node
106
- if node.value is not None:
107
- return ast.Assign(targets=[node.target], value=node.value,
108
- lineno=node.lineno, col_offset=node.col_offset)
109
- return None # 纯注解无赋值,直接删除
230
+ def _collect_class_free_refs(node, local_names):
231
+ """收集嵌套类体中直接引用的外层变量名。"""
232
+ free = set()
233
+ outer_expr_nodes = list(node.decorator_list)
234
+ outer_expr_nodes.extend(node.bases)
235
+ outer_expr_nodes.extend(keyword.value for keyword in node.keywords)
236
+ free.update(_collect_load_names_no_nested(outer_expr_nodes) & local_names)
237
+
238
+ for name in _collect_load_names_no_nested(node.body):
239
+ if name in local_names:
240
+ free.add(name)
241
+ free.update(_collect_nested_free_refs(node.body, local_names))
242
+ return free
243
+
244
+
245
+ def _collect_comprehension_free_refs(node, local_names):
246
+ """保守收集 comprehension 中对外层变量的引用。
247
+
248
+ comprehension 是隐式函数作用域;为避免 Cython/CPython 闭包细节差异,
249
+ 只要其中出现外层局部名,就将该外层名排除出重命名集合。
250
+ """
251
+ free = set()
252
+ for child in ast.walk(node):
253
+ if isinstance(child, ast.Name) and isinstance(child.ctx, ast.Load) and child.id in local_names:
254
+ free.add(child.id)
255
+ return free
110
256
 
111
257
 
112
258
  def _collect_nested_free_refs(body, local_names):
113
- """收集嵌套函数/类中引用的外层变量名(闭包变量)"""
259
+ """收集嵌套词法作用域中引用的外层变量名(闭包变量)"""
114
260
  free = set()
115
- for node in body:
116
- if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
117
- # 嵌套函数自己的局部名
118
- nested_locals = set()
119
- # nonlocal/global 声明的变量不是局部变量
120
- nonlocal_names = set()
121
- for child in _walk_no_nested_scope(node.body):
122
- if isinstance(child, (ast.Nonlocal, ast.Global)):
123
- nonlocal_names.update(child.names)
124
- for arg in node.args.args + node.args.posonlyargs + node.args.kwonlyargs:
125
- nested_locals.add(arg.arg)
126
- if node.args.vararg:
127
- nested_locals.add(node.args.vararg.arg)
128
- if node.args.kwarg:
129
- nested_locals.add(node.args.kwarg.arg)
130
- for child in _walk_no_nested_scope(node.body):
131
- if isinstance(child, ast.Name) and isinstance(child.ctx, ast.Store):
132
- if child.id not in nonlocal_names:
133
- nested_locals.add(child.id)
134
- # 嵌套函数中读取的、属于外层 local_names 且不被自身遮蔽的名字
135
- for child in ast.walk(node):
136
- if isinstance(child, ast.Name) and child.id in local_names and child.id not in nested_locals:
137
- free.add(child.id)
261
+ for node in _collect_nested_scope_nodes(body):
262
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda)):
263
+ free.update(_collect_function_like_free_refs(node, local_names))
138
264
  elif isinstance(node, ast.ClassDef):
139
- # 类体中直接引用的外层变量
140
- for child in _walk_no_nested_scope(node.body):
141
- if isinstance(child, ast.Name) and child.id in local_names and isinstance(child.ctx, ast.Load):
142
- free.add(child.id)
143
- free |= _collect_nested_free_refs(node.body, local_names)
144
- else:
145
- # 递归搜索所有子节点中的嵌套函数/类(穿透 Try/With/For/If 等)
146
- for child in ast.walk(node):
147
- if child is node:
148
- continue
149
- if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
150
- free |= _collect_nested_free_refs([child], local_names)
265
+ free.update(_collect_class_free_refs(node, local_names))
266
+ elif isinstance(node, (ast.ListComp, ast.SetComp, ast.DictComp, ast.GeneratorExp)):
267
+ free.update(_collect_comprehension_free_refs(node, local_names))
151
268
  return free
152
269
 
153
270
 
@@ -167,26 +284,20 @@ class _LocalVarRenamer(ast.NodeTransformer):
167
284
 
168
285
  # 收集参数名(不重命名)
169
286
  param_names = set()
170
- for arg in node.args.args + node.args.posonlyargs + node.args.kwonlyargs:
171
- param_names.add(arg.arg)
172
- if node.args.vararg:
173
- param_names.add(node.args.vararg.arg)
174
- if node.args.kwarg:
175
- param_names.add(node.args.kwarg.arg)
287
+ param_names.update(_collect_argument_names(node.args))
176
288
 
177
289
  # 收集局部赋值目标(不穿透嵌套作用域)
178
290
  local_names = set()
179
291
  # 排除 nonlocal/global 声明的变量
180
- nonlocal_names = set()
292
+ nonlocal_names, global_names = _collect_nonlocal_global_names(node.body)
293
+ excluded_scope_names = nonlocal_names | global_names
181
294
  for child in _walk_no_nested_scope(node.body):
182
- if isinstance(child, (ast.Nonlocal, ast.Global)):
183
- nonlocal_names.update(child.names)
184
295
  if isinstance(child, ast.Name) and isinstance(child.ctx, ast.Store):
185
296
  name = child.id
186
- if (name not in param_names and name != 'self' and name != 'cls'
297
+ if (name not in param_names and name not in excluded_scope_names
298
+ and name != 'self' and name != 'cls'
187
299
  and not name.startswith('__')):
188
300
  local_names.add(name)
189
- local_names -= nonlocal_names
190
301
 
191
302
  if not local_names:
192
303
  self.generic_visit(node)
@@ -484,6 +595,7 @@ class _OpaquePredicateInserter(ast.NodeTransformer):
484
595
  def obfuscate_source(source_code: str, seed=None) -> str:
485
596
  """对源码执行六重 AST 变换:去 docstring、局部变量重命名、
486
597
  字符串加密、常量折叠、控制流平坦化、虚假分支。
598
+ 函数、类和局部变量 annotation 默认保留。
487
599
  ast.unparse() 天然丢弃所有注释。
488
600
 
489
601
  :param seed: 随机种子;传入后可复现混淆结果
@@ -496,6 +608,7 @@ def obfuscate_source(source_code: str, seed=None) -> str:
496
608
  tree = _LocalVarRenamer().visit(tree)
497
609
  tree = _ControlFlowFlattener(rng=rng).visit(tree)
498
610
  tree = _OpaquePredicateInserter(rng=rng).visit(tree)
611
+ tree = _mark_annotation_constants(tree)
499
612
  tree = _StringEncryptor(rng=rng).visit(tree)
500
613
  tree = _ConstantFoldingObfuscator(rng=rng).visit(tree)
501
614
  ast.fix_missing_locations(tree)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AutoCython-zhang
3
- Version: 2.3.6
3
+ Version: 2.3.7
4
4
  Summary: 自动Cython,使用Cython批量编译.py文件为.pyd文件!
5
5
  Author-email: zhang_gavin <qq814608@163.com>
6
6
  License-Expression: MIT
@@ -20,7 +20,7 @@ Dynamic: license-file
20
20
 
21
21
  # AutoCython
22
22
 
23
- > 自动将 Python 源码编译为 Cython 二进制扩展(`.so`/`.pyd`),支持七重 AST 混淆、并发编译、跨平台运行。
23
+ > 自动将 Python 源码编译为 Cython 二进制扩展(`.so`/`.pyd`),支持六重 AST 混淆、并发编译、跨平台运行。
24
24
 
25
25
  [![PyPI](https://img.shields.io/pypi/v/AutoCython-zhang)](https://pypi.org/project/AutoCython-zhang/)
26
26
  [![Python](https://img.shields.io/pypi/pyversions/AutoCython-zhang)](https://pypi.org/project/AutoCython-zhang/)
@@ -33,7 +33,7 @@ AutoCython 是一个 Python 源码保护工具,通过 Cython 编译 + AST 混
33
33
  ## 特性
34
34
 
35
35
  - **一键编译** — 单文件 (`-f`) 或整目录 (`-p`) 批量编译为 `.so`/`.pyd`
36
- - **七重 AST 混淆** — docstring 移除、注解清除、局部变量重命名、字符串 XOR 加密、常量折叠、控制流平坦化、虚假分支注入
36
+ - **六重 AST 混淆** — docstring 移除、局部变量重命名、字符串 XOR 加密、常量折叠、控制流平坦化、虚假分支注入;默认完整保留运行时 annotation,兼容 FastAPI / Pydantic / dataclass
37
37
  - **并发编译** — 基于 `ThreadPoolExecutor` 的多线程并行编译,可配置并发数
38
38
  - **实时进度面板** — 基于 Rich 的实时任务状态表格、进度条、耗时统计
39
39
  - **跨平台** — 支持 Linux、macOS、Windows,自动适配 `.so`/`.pyd` 扩展名
@@ -108,7 +108,7 @@ print(f"生成: {output}")
108
108
  |------|------|------|
109
109
  | `compile()` | `AutoCython.AutoCython` | 主入口:解析参数并调度编译任务 |
110
110
  | `compile_to_binary()` | `AutoCython.compile` | 将单个 `.py` 文件编译为二进制扩展 |
111
- | `obfuscate_source()` | `AutoCython.obfuscate` | 对源码执行七重 AST 混淆变换 |
111
+ | `obfuscate_source()` | `AutoCython.obfuscate` | 对源码执行六重 AST 混淆变换,并保留运行时 annotation |
112
112
  | `run_tasks()` | `AutoCython.run_tasks` | 并发任务执行引擎(含实时进度面板) |
113
113
  | `find_python_files()` | `AutoCython.tools` | 递归扫描目录下的可编译 `.py` 文件 |
114
114
  | `parse_arguments()` | `AutoCython.tools` | CLI 参数解析(中英双语) |
@@ -124,7 +124,7 @@ AutoCython/
124
124
  │ ├── _version.py # 单一版本号来源
125
125
  │ ├── AutoCython.py # 主编译调度逻辑
126
126
  │ ├── compile.py # Cython 编译核心(临时目录隔离)
127
- │ ├── obfuscate.py # 七重 AST 混淆引擎
127
+ │ ├── obfuscate.py # 六重 AST 混淆引擎
128
128
  │ ├── run_tasks.py # 并发任务执行 + Rich 实时面板
129
129
  │ └── tools.py # CLI 参数解析、文件扫描、国际化
130
130
  ├── tests/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AutoCython-zhang
3
- Version: 2.3.6
3
+ Version: 2.3.7
4
4
  Summary: 自动Cython,使用Cython批量编译.py文件为.pyd文件!
5
5
  Author-email: zhang_gavin <qq814608@163.com>
6
6
  License-Expression: MIT
@@ -20,7 +20,7 @@ Dynamic: license-file
20
20
 
21
21
  # AutoCython
22
22
 
23
- > 自动将 Python 源码编译为 Cython 二进制扩展(`.so`/`.pyd`),支持七重 AST 混淆、并发编译、跨平台运行。
23
+ > 自动将 Python 源码编译为 Cython 二进制扩展(`.so`/`.pyd`),支持六重 AST 混淆、并发编译、跨平台运行。
24
24
 
25
25
  [![PyPI](https://img.shields.io/pypi/v/AutoCython-zhang)](https://pypi.org/project/AutoCython-zhang/)
26
26
  [![Python](https://img.shields.io/pypi/pyversions/AutoCython-zhang)](https://pypi.org/project/AutoCython-zhang/)
@@ -33,7 +33,7 @@ AutoCython 是一个 Python 源码保护工具,通过 Cython 编译 + AST 混
33
33
  ## 特性
34
34
 
35
35
  - **一键编译** — 单文件 (`-f`) 或整目录 (`-p`) 批量编译为 `.so`/`.pyd`
36
- - **七重 AST 混淆** — docstring 移除、注解清除、局部变量重命名、字符串 XOR 加密、常量折叠、控制流平坦化、虚假分支注入
36
+ - **六重 AST 混淆** — docstring 移除、局部变量重命名、字符串 XOR 加密、常量折叠、控制流平坦化、虚假分支注入;默认完整保留运行时 annotation,兼容 FastAPI / Pydantic / dataclass
37
37
  - **并发编译** — 基于 `ThreadPoolExecutor` 的多线程并行编译,可配置并发数
38
38
  - **实时进度面板** — 基于 Rich 的实时任务状态表格、进度条、耗时统计
39
39
  - **跨平台** — 支持 Linux、macOS、Windows,自动适配 `.so`/`.pyd` 扩展名
@@ -108,7 +108,7 @@ print(f"生成: {output}")
108
108
  |------|------|------|
109
109
  | `compile()` | `AutoCython.AutoCython` | 主入口:解析参数并调度编译任务 |
110
110
  | `compile_to_binary()` | `AutoCython.compile` | 将单个 `.py` 文件编译为二进制扩展 |
111
- | `obfuscate_source()` | `AutoCython.obfuscate` | 对源码执行七重 AST 混淆变换 |
111
+ | `obfuscate_source()` | `AutoCython.obfuscate` | 对源码执行六重 AST 混淆变换,并保留运行时 annotation |
112
112
  | `run_tasks()` | `AutoCython.run_tasks` | 并发任务执行引擎(含实时进度面板) |
113
113
  | `find_python_files()` | `AutoCython.tools` | 递归扫描目录下的可编译 `.py` 文件 |
114
114
  | `parse_arguments()` | `AutoCython.tools` | CLI 参数解析(中英双语) |
@@ -124,7 +124,7 @@ AutoCython/
124
124
  │ ├── _version.py # 单一版本号来源
125
125
  │ ├── AutoCython.py # 主编译调度逻辑
126
126
  │ ├── compile.py # Cython 编译核心(临时目录隔离)
127
- │ ├── obfuscate.py # 七重 AST 混淆引擎
127
+ │ ├── obfuscate.py # 六重 AST 混淆引擎
128
128
  │ ├── run_tasks.py # 并发任务执行 + Rich 实时面板
129
129
  │ └── tools.py # CLI 参数解析、文件扫描、国际化
130
130
  ├── tests/
@@ -1,6 +1,6 @@
1
1
  # AutoCython
2
2
 
3
- > 自动将 Python 源码编译为 Cython 二进制扩展(`.so`/`.pyd`),支持七重 AST 混淆、并发编译、跨平台运行。
3
+ > 自动将 Python 源码编译为 Cython 二进制扩展(`.so`/`.pyd`),支持六重 AST 混淆、并发编译、跨平台运行。
4
4
 
5
5
  [![PyPI](https://img.shields.io/pypi/v/AutoCython-zhang)](https://pypi.org/project/AutoCython-zhang/)
6
6
  [![Python](https://img.shields.io/pypi/pyversions/AutoCython-zhang)](https://pypi.org/project/AutoCython-zhang/)
@@ -13,7 +13,7 @@ AutoCython 是一个 Python 源码保护工具,通过 Cython 编译 + AST 混
13
13
  ## 特性
14
14
 
15
15
  - **一键编译** — 单文件 (`-f`) 或整目录 (`-p`) 批量编译为 `.so`/`.pyd`
16
- - **七重 AST 混淆** — docstring 移除、注解清除、局部变量重命名、字符串 XOR 加密、常量折叠、控制流平坦化、虚假分支注入
16
+ - **六重 AST 混淆** — docstring 移除、局部变量重命名、字符串 XOR 加密、常量折叠、控制流平坦化、虚假分支注入;默认完整保留运行时 annotation,兼容 FastAPI / Pydantic / dataclass
17
17
  - **并发编译** — 基于 `ThreadPoolExecutor` 的多线程并行编译,可配置并发数
18
18
  - **实时进度面板** — 基于 Rich 的实时任务状态表格、进度条、耗时统计
19
19
  - **跨平台** — 支持 Linux、macOS、Windows,自动适配 `.so`/`.pyd` 扩展名
@@ -88,7 +88,7 @@ print(f"生成: {output}")
88
88
  |------|------|------|
89
89
  | `compile()` | `AutoCython.AutoCython` | 主入口:解析参数并调度编译任务 |
90
90
  | `compile_to_binary()` | `AutoCython.compile` | 将单个 `.py` 文件编译为二进制扩展 |
91
- | `obfuscate_source()` | `AutoCython.obfuscate` | 对源码执行七重 AST 混淆变换 |
91
+ | `obfuscate_source()` | `AutoCython.obfuscate` | 对源码执行六重 AST 混淆变换,并保留运行时 annotation |
92
92
  | `run_tasks()` | `AutoCython.run_tasks` | 并发任务执行引擎(含实时进度面板) |
93
93
  | `find_python_files()` | `AutoCython.tools` | 递归扫描目录下的可编译 `.py` 文件 |
94
94
  | `parse_arguments()` | `AutoCython.tools` | CLI 参数解析(中英双语) |
@@ -104,7 +104,7 @@ AutoCython/
104
104
  │ ├── _version.py # 单一版本号来源
105
105
  │ ├── AutoCython.py # 主编译调度逻辑
106
106
  │ ├── compile.py # Cython 编译核心(临时目录隔离)
107
- │ ├── obfuscate.py # 七重 AST 混淆引擎
107
+ │ ├── obfuscate.py # 六重 AST 混淆引擎
108
108
  │ ├── run_tasks.py # 并发任务执行 + Rich 实时面板
109
109
  │ └── tools.py # CLI 参数解析、文件扫描、国际化
110
110
  ├── tests/
@@ -42,7 +42,7 @@ markers = [
42
42
 
43
43
  [tool.setuptools.packages.find]
44
44
  where = ["."]
45
- exclude = ["tests*"]
45
+ exclude = ["tests*", "build*"]
46
46
 
47
47
  [tool.setuptools.dynamic]
48
48
  version = {attr = "AutoCython._version.__version__"}