the-mindscape-of-bro-tsong/scripts/check_card_contract.py

73 lines
2.1 KiB
Python

import re
import sys
from pathlib import Path
def required_headings(root):
contract_path = Path(root) / "docs" / "MODEL_CARD_CONTRACT.md"
if not contract_path.exists():
return [], [f"missing file {contract_path.as_posix()}"]
headings = []
in_required_sections = False
for line in contract_path.read_text(encoding="utf-8").splitlines():
if line.strip() == "## Required Sections":
in_required_sections = True
continue
if in_required_sections and line.startswith("## "):
title = line[3:].strip()
if title and title not in {"Additional Sections"}:
headings.append(title)
if in_required_sections and line.strip() == "## Additional Sections":
break
return headings, []
def card_headings(path):
headings = set()
for line in path.read_text(encoding="utf-8").splitlines():
match = re.match(r"^##\s+(.+?)\s*$", line)
if match:
headings.add(match.group(1).strip())
return headings
def check_cards(root):
root = Path(root)
headings, errors = required_headings(root)
if errors:
return errors
cards_dir = root / "cards"
if not cards_dir.exists():
return ["missing directory cards"]
ignored_names = {"README.md", "card_index.md"}
card_paths = sorted(path for path in cards_dir.glob("*.md") if path.name not in ignored_names)
if not card_paths:
return ["cards/ contains no Markdown model cards"]
results = []
for path in card_paths:
present = card_headings(path)
relative_path = path.relative_to(root).as_posix()
for heading in headings:
if heading not in present:
results.append(f"{relative_path} missing required heading ## {heading}")
return results
def main():
root = Path(__file__).resolve().parents[1]
errors = check_cards(root)
if errors:
for error in errors:
print(f"ERROR: {error}")
return 1
print("card contract check passed")
return 0
if __name__ == "__main__":
sys.exit(main())