#!/usr/bin/env python3 """Remove from a registry fragment every sensor that the validator reported as FAIL (and, with --drop-warn, WARN). Works on the YAML text line by line so the compact flow style is preserved. Usage: python3 scripts/prune-fragment.py [--drop-warn] [--keep-empty] Sources left without any sensor are removed unless --keep-empty (they may still be useful with discover: flags — decide case by case). """ import json import re import sys import yaml def main() -> None: frag, report = sys.argv[1], sys.argv[2] drop_warn = "--drop-warn" in sys.argv keep_empty = "--keep-empty" in sys.argv rep = json.load(open(report)) bad = {r["url"] for r in rep["results"] if r["status"] == "FAIL" or (drop_warn and r["status"] == "WARN")} lines = open(frag).read().split("\n") out = [] removed = 0 for ln in lines: m = re.search(r'url:\s*"([^"]+)"', ln) or re.search(r"url:\s*'([^']+)'", ln) or re.search(r"url:\s*(\S+?)[,}]", ln) if ln.lstrip().startswith("- {") and m and m.group(1) in bad: removed += 1 continue out.append(ln) text = "\n".join(out) # Drop sources with no sensors left (flow-style sensors → check the parsed structure). if not keep_empty: data = yaml.safe_load(text) empty = {s["id"] for s in data["sources"] if not s.get("sensors") and not s.get("discover")} if empty: blocks = re.split(r"(?m)^(?= - id: )", text) kept = [b for b in blocks if not any(b.startswith(f" - id: {e}\n") for e in empty)] text = "".join(kept) print(f"removed {len(empty)} empty sources: {', '.join(sorted(empty))}", file=sys.stderr) open(frag, "w").write(text) print(f"removed {removed} sensors from {frag}", file=sys.stderr) if __name__ == "__main__": main()