# -*- coding: utf-8 -*- r'''检查 landing.py 的 CSS 里所有 % 都正确转义了。 为什么要专门写: SITE_CSS 最后要过 `% {...}` 格式化,所以**所有字面的百分号** 必须写成 %%。漏一个就报 ValueError: unsupported format character ';' 而且报的是"第 N 个字符",不给行号,很难找。 我在这一版里已经踩了两次: * color:%(ACCENT) 少了个 s * left:50%; 忘了写 %% 规则: * 合法占位符:%(NAME)s —— 名字必须在下面的白名单里 * 合法百分号:%% * 其它一律报错并给出行号 ''' from __future__ import annotations import re import sys from pathlib import Path SRC = Path(__file__).resolve().parent.parent / "relay" / "landing.py" PLACEHOLDERS = {"BG", "SURFACE", "SURFACE2", "BORDER", "TEXT", "MUTED", "FAINT", "ACCENT", "ACCENT_DARK"} def main() -> int: text = SRC.read_text(encoding="utf-8") lines = text.splitlines() start = end = None for i, line in enumerate(lines): if line.startswith('SITE_CSS = """'): start = i + 1 elif start is not None and line.startswith('""" %'): end = i break if start is None or end is None: print(" 找不到 SITE_CSS 字面量") return 1 css = "\n".join(lines[start:end]) print(" SITE_CSS: 第 %d~%d 行,%d 字符" % (start + 1, end, len(css))) bad = [] for i, line in enumerate(css.splitlines(), start + 1): # 先把合法占位符抹掉 tmp = line for name in PLACEHOLDERS: tmp = tmp.replace("%(" + name + ")s", "") # 再把 %% 抹掉 tmp = tmp.replace("%%", "") # 剩下任何 % 都是错的 if "%" in tmp: # 找出具体是哪种错 m = re.search(r"%\(([a-zA-Z_]+)\)(.)?", tmp) if m: bad.append((i, line.strip(), "占位符 %%%s 不对:名字是 %r;" "正确写法是 %%%s(记得末尾的 s)" % (m.group(1), m.group(1), m.group(1)))) else: m2 = re.search(r"%(.)", tmp) bad.append((i, line.strip(), "字面的 %% 要写成 %%%%" + ("(后面跟的是 %r)" % m2.group(1) if m2 else ""))) if bad: print() print(" ⚠ 有 %d 处问题:" % len(bad)) for ln, content, why in bad: print(" 第 %d 行: %s" % (ln, content[:88])) print(" %s" % why) print() print(" 这些在渲染时会让 landing.py 直接 import 失败,") print(" 表现成官网打不开(500)。") return 1 print(" ✓ 所有 %% 都正确转义") # 再真的试一遍格式化 import ast tree = ast.parse(text) for node in ast.walk(tree): if (isinstance(node, ast.Assign) and node.targets and getattr(node.targets[0], "id", "") == "SITE_CSS"): first = node.value while isinstance(first, ast.BinOp): first = first.left if isinstance(first, ast.Constant): try: first.value % {k: "#000" for k in PLACEHOLDERS} print(" ✓ 实际格式化也通过") except ValueError as exc: print(" ✗ 格式化失败:%s" % exc) return 1 return 0 if __name__ == "__main__": sys.exit(main())