# -*- coding: utf-8 -*- r'''全仓语法检查 —— 必须用 compile(),不能用 ast.parse()。 == 为什么这个文件存在 == 我这一轮反复被同一个问题咬: Python 要求 `from __future__ import ...` 是文件里第一条语句。 我的补丁脚本为了加 `import os` / `import re`,把它插到了 `from __future__` **前面**,整个文件就废了。 而更糟的是**我的检查工具查不出来**: ast.parse(src) -> 通过(它只解析,不做编译期检查) compile(src, ...) -> SyntaxError: from __future__ imports must occur at the beginning of the file 我前面几次"全仓语法检查 0 个问题"就是这么来的假绿。 真正的症状是: * 源码直接跑 -> 启动就崩 * 打包成 exe -> 走到那个模块才崩,用户看到"双击没反应" 所以这个脚本用 **compile()** 扫全仓,并且专门把 "只被 ast.parse 放过、但 compile 会拦" 的文件标出来。 进回归套件之后,任何脚本再犯一次,全量回归立刻红。 ''' from __future__ import annotations import ast import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent PATTERNS = ["_tests/*.py", "_build/*.py", "relay/*.py", "windows/*.py", "windows/lanremote/*.py", "installer/*.py"] def main() -> int: files = [] for pat in PATTERNS: files += sorted(ROOT.glob(pat)) bad = [] sneaky = [] # ast.parse 放过、compile 拦下的(就是这次的坑) n = 0 for f in files: if not f.is_file(): continue n += 1 try: src = f.read_text(encoding="utf-8") except Exception: # noqa: BLE001 continue try: compile(src, str(f), "exec") except SyntaxError as exc: bad.append((f.relative_to(ROOT), exc.lineno, exc.msg)) # 看看 ast.parse 是不是会放过它 try: ast.parse(src) sneaky.append(f.relative_to(ROOT)) except SyntaxError: pass print(" 用 compile() 检查 %d 个文件" % n) if not bad: print(" ✓ 全部通过") return 0 print(" ✗ %d 个文件有语法错:" % len(bad)) for rel, ln, msg in bad: print(" %s: 第 %s 行 %s" % (rel, ln, msg)) if sneaky: print() print(" ⚠ 下面这些 **ast.parse 会放过**(所以以前的检查是假绿):") for rel in sneaky: print(" %s" % rel) print(" -> 以后一律用 compile(),别再用 ast.parse() 当语法检查。") return 1 if __name__ == "__main__": sys.exit(main())