# -*- coding: utf-8 -*- r'''找出 SITE_CSS 里没转义的 %(% 格式化会把它们当占位符)。 规则:既然 SITE_CSS 最后要过 `% {...}`,那么**所有字面的百分号** 都必须写成 %%。CSS 里最常见的漏网之鱼是: width:50% 渐变里的 60% 60% rgba(...) 不会 ''' from __future__ import annotations import ast import sys from pathlib import Path SRC = Path(__file__).resolve().parent.parent / "relay" / "landing.py" text = SRC.read_text(encoding="utf-8") tree = ast.parse(text) target = None for node in ast.walk(tree): if isinstance(node, ast.Assign): for t in node.targets: if isinstance(t, ast.Name) and t.id == "SITE_CSS": target = node.value break if target is None: print(" 找不到 SITE_CSS 赋值") sys.exit(1) # 第一段是那个大三引号字符串(后面还接了 showcase) first = target while isinstance(first, ast.BinOp): first = first.left if isinstance(first, ast.Constant): css = first.value offset = first.col_offset print(" 找到 SITE_CSS 的字面量部分:%d 字符,起始行 %d" % (len(css), first.lineno)) else: print(" 结构不认识:%s" % type(first).__name__) sys.exit(1) # 逐行找单个 % (不是 %%) print() print(" ════ 可疑的单 % ════") bad = 0 for i, line in enumerate(css.splitlines(), 1): # 去掉 %(name)s 这类合法占位符,再看还有没有单 % stripped = line import re stripped = re.sub(r"%\([a-zA-Z_]+\)[sd]", "", stripped) # 现在剩下的 % 都必须是 %% for m in re.finditer(r"%(.)", stripped): if m.group(1) != "%": print(" 第 %d 行: ...%s..." % (i, line.strip()[:90])) print(" 这里有个单 %% 后面跟了 %r" % m.group(1)) bad += 1 break if not bad: print(" (没找到)") # 直接试一下格式化,把出错位置算出来 print() print(" ════ 试格式化 ════") try: css % {"BG": "#fff", "SURFACE": "#fafafa", "SURFACE2": "#f5f5f5", "BORDER": "#eee", "TEXT": "#111", "MUTED": "#666", "FAINT": "#999", "ACCENT": "#07e", "ACCENT_DARK": "#06d"} print(" ✓ 能格式化") except ValueError as exc: print(" ✗ %s" % exc) # 把出错位置附近打出来 import re m = re.search(r"index (\d+)", str(exc)) if m: pos = int(m.group(1)) lo = max(0, pos - 90) print(" 位置 %d 附近:" % pos) print(" ...%s..." % css[lo:pos + 30].replace("\n", "\\n"))