# -*- coding: utf-8 -*- r'''收尾:把「去密码硬编码」那一步留下的机械替换伤全部治好。 == 病灶 == fix_password_round2.py 里对 `os.environ` 的替换太粗: t = t.replace(OLD, "(从 RELAY_ADMIN_PASSWORD 环境变量读)") 于是凡是 `"@<老密码>"` 这种形状(@ 在引号里)的地方, 都变成了一个**字符串字面量**: PASS = "@(从 RELAY_ADMIN_PASSWORD 环境变量读)" ADMIN_PASS = "@(从 RELAY_ADMIN_PASSWORD 环境变量读)" 它们本该是: PASS = (os.environ.get("RELAY_ADMIN_PASSWORD") or "").strip() 另外我在给文件插 `import os` 时,插到了 `from __future__ import` 前面 —— Python 要求 __future__ 必须是第一个语句,于是整个文件语法错。 == 这个脚本做三件事 == 1. 把那两个被污染的赋值改成正确的 os.environ 读法 2. 确保每个用到 os.environ 的文件都**正确位置**导入了 os 3. 全仓复查:有没有「字符串里含中文提示但本该是表达式」的残留 ''' from __future__ import annotations import ast import re import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent TARGETS = ["_build/test_response_headers.py", "_build/split_site_admin2.py"] POLLUTED = re.compile( r'(\b\w*PASS\w*)\s*=\s*"@?(从 RELAY_ADMIN_PASSWORD[^"]*"') def ensure_import_os(text: str) -> str: """确保有 import os,且 __future__ 在最前面。""" has_os = re.search(r"^import os\b", text, re.M) is not None m = re.search(r"^from __future__ import .*$", text, re.M) if m: # 把 __future__ 提到所有 import 之前(如果它前面还有 import) before = text[:m.start()] # 前面的 import os 先摘掉 if re.search(r"^import os\s*$", before, re.M): before = re.sub(r"^import os\s*\n", "", before, flags=re.M) text = before + text[m.start():] has_os = False # 在 __future__ 之后插 import os if not has_os: m2 = re.search(r"^from __future__ import .*$", text, re.M) insert_at = m2.end() text = (text[:insert_at] + "\nimport os" + text[insert_at:]) return text if not has_os: # 没有 __future__:插在第一个 import 之前 m3 = re.search(r"^(?:import |from )", text, re.M) if m3: text = text[:m3.start()] + "import os\n" + text[m3.start():] else: text = "import os\n" + text return text def main() -> int: changed = 0 for rel in TARGETS: p = ROOT / rel if not p.is_file(): print(" ✗ %s 不存在" % rel) continue text = p.read_text(encoding="utf-8") before = text # 1) 修被污染的赋值 text = POLLUTED.sub( r'\1 = (os.environ.get("RELAY_ADMIN_PASSWORD") or "").strip()', text) # 2) 保证 import os 在正确位置 text = ensure_import_os(text) if text == before: print(" • %s 不需要改" % rel) continue # 3) 写完先自己 parse 一遍,语法错就别写 try: ast.parse(text) except SyntaxError as exc: print(" ✗ %s 改完语法错(不写回):第 %s 行 %s" % (rel, exc.lineno, exc.msg)) continue p.write_text(text, encoding="utf-8", newline="\n") print(" ✓ %s" % rel) changed += 1 print() print(" 改了 %d 个" % changed) # 全仓复查 print() print(" ── 全仓复查 ──") problems = [] for pat in ("_tests/*.py", "_build/*.py", "relay/*.py"): for f in ROOT.glob(pat): try: text = f.read_text(encoding="utf-8") except Exception: # noqa: BLE001 continue rel = str(f.relative_to(ROOT)) # 被污染的赋值 if POLLUTED.search(text): problems.append("%s: 还有被污染的 PASS 赋值" % rel) # 语法 try: ast.parse(text) except SyntaxError as exc: if "__future__" in (exc.msg or ""): problems.append("%s: __future__ 位置错" % rel) else: problems.append("%s: 第 %s 行 %s" % (rel, exc.lineno, exc.msg)) if problems: for x in problems: print(" ⚠ %s" % x) return 1 print(" ✓ 没有残留") return 0 if __name__ == "__main__": sys.exit(main())