#!/usr/bin/env python3 """Absence — Appendix E, specification 6. Establishes that something is NOT present. A harder claim than presence, and it needs the search space stated explicitly. This script refuses to assert absence unless coverage is complete: if any item could not be searched, the finding is reported as INCONCLUSIVE rather than as absence. python3 absence.py --dir ./production --term "indemnification" --out absence.json Exit codes: 0 absence established, 2 term present, 3 inconclusive (incomplete coverage). """ import argparse, json, os, platform, re, sys from datetime import datetime, timezone VERSION = "1.0" TEXTLIKE = {".txt", ".csv", ".tsv", ".md", ".json", ".htm", ".html", ".xml", ".log", ".eml"} def main(): ap = argparse.ArgumentParser(description="Establish absence, with the search space stated.") ap.add_argument("--dir", required=True) ap.add_argument("--term", action="append", required=True) ap.add_argument("--regex", action="store_true") ap.add_argument("--case-sensitive", action="store_true") ap.add_argument("--out", required=True) a = ap.parse_args() if not os.path.isdir(a.dir): sys.exit(f"not a directory: {a.dir}") flags = 0 if a.case_sensitive else re.IGNORECASE pats = [(t, re.compile(t if a.regex else re.escape(t), flags)) for t in a.term] seen = searched = 0 blocked, found = [], [] for dp, dns, fns in os.walk(a.dir): dns.sort() for fn in sorted(fns): p = os.path.join(dp, fn); rel = os.path.relpath(p, a.dir).replace(os.sep, "/") seen += 1 ext = os.path.splitext(fn)[1].lower() if ext not in TEXTLIKE: blocked.append({"item": rel, "reason": f"unsupported format {ext or '(none)'}"}); continue try: with open(p, encoding="utf-8", errors="strict") as f: text = f.read() except (UnicodeDecodeError, OSError) as e: blocked.append({"item": rel, "reason": type(e).__name__}); continue if ext in {".htm", ".html", ".xml"}: text = re.sub(r"<[^>]+>", " ", text) searched += 1 for term, rx in pats: if rx.search(text): found.append({"item": rel, "term": term}) complete = (searched == seen) if found: finding, code = "PRESENT", 2 elif complete: finding, code = "ABSENT", 0 else: finding, code = "INCONCLUSIVE", 3 report = { "specification": "Absence (Appendix E, 6)", "script": os.path.basename(__file__), "script_version": VERSION, "run_utc": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), "python": platform.python_version(), "finding": finding, "search_space": {"root": os.path.abspath(a.dir), "items_encountered": seen, "items_searched": searched, "items_not_searched": len(blocked), "coverage_complete": complete, "formats_searchable": sorted(TEXTLIKE)}, "terms": a.term, "occurrences": sorted(found, key=lambda h: (h["item"], h["term"])), "items_not_searched": blocked, "statement": { "ABSENT": "No occurrence was found, and every item in the stated search space was searched.", "PRESENT": "The term occurs. Absence cannot be asserted.", "INCONCLUSIVE": ("No occurrence was found in the items that could be searched, but some items " "could not be. Absence is NOT established over the whole search space."), }[finding], } with open(a.out, "w", encoding="utf-8") as f: json.dump(report, f, indent=1); f.write("\n") print(f"{finding}: searched {searched}/{seen} -> {a.out}") print(report["statement"]) sys.exit(code) if __name__ == "__main__": main()