#!/usr/bin/env python3 """Every section a WAI specification text cites in another WAI text exists. Reads wai/SPEC.md, wai/extensions/*.md and wai/SECURITY.md. In each, a reference to a section of SPEC.md or of an extension is checked against the headings of the file it names. Three spellings are recognised, including where the reference breaks across lines: [text](X.md) §N a link, then the section [text §N](X.md) the section inside the link text X §N, X.md §N, `X` §N an extension's file name (or SPEC) in prose, then the section where §N is a section number of one or more parts (§7, §2.4.3), and a range written §A–§B checks both ends. A heading is numbered when it begins `#`…, then the number, as in `## 7.`, `### 6.4 ` or `## 7.1.`. Files under wai/research/ are dated records, kept as written, and are not read. Exit status 1, with one line per reference that names a missing section; 0 otherwise. python3 scripts/check-wai-section-refs.py [repo-root] """ import os, re, sys ROOT = os.path.abspath(sys.argv[1] if len(sys.argv) > 1 else os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) WAI = os.path.join(ROOT, "wai") EXT = os.path.join(WAI, "extensions") HEADING = re.compile(r"^#{1,6}[ \t]+([0-9]+(?:\.[0-9]+)*)\.?(?=[ \t])", re.M) SECTION = r"§[ \t]*([0-9]+(?:\.[0-9]+)*)" RANGE = re.compile(r"[ \t]*[–-][ \t]*§[ \t]*([0-9]+(?:\.[0-9]+)*)") def sections(path): with open(path, encoding="utf-8") as f: return set(HEADING.findall(f.read())) def texts(): out = [os.path.join(WAI, "SPEC.md")] out += sorted(os.path.join(EXT, n) for n in os.listdir(EXT) if n.endswith(".md")) out.append(os.path.join(WAI, "SECURITY.md")) # it cites the texts it points readers to return out def main(): files = texts() known = {os.path.normpath(p): sections(p) for p in files} stems = sorted((os.path.splitext(os.path.basename(p))[0] for p in files if os.path.dirname(p) == EXT), key=len, reverse=True) by_stem = {os.path.splitext(os.path.basename(p))[0]: os.path.normpath(p) for p in files if os.path.dirname(p) == EXT} by_stem["SPEC"] = os.path.normpath(os.path.join(WAI, "SPEC.md")) # A file name in prose, not inside a link target or a longer word or path. bare = re.compile(r"(?