PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
audit_ingress.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""
3Static ingress audit for PETSc option parsing in setup/io.
4
5This script scans src/setup.c and src/io.c for PetscOptionsGet*/HasName calls,
6extracts option flags, and compares them against a maintained manifest.
7"""
8
9from __future__ import annotations
10
11import argparse
12import json
13import pathlib
14import re
15import sys
16from typing import Iterable, Set
17
18
19OPTION_RE = re.compile(
20 r'PetscOptions(?:Get(?:Int|Real|Bool|String|IntArray|RealArray)|HasName)\s*'
21 r'\‍(\s*NULL\s*,\s*NULL\s*,\s*"(-[^"]+)"'
22)
23
24
25def scan_petsc_options(paths: Iterable[pathlib.Path]) -> Set[str]:
26 """!
27 @brief Perform scan petsc options.
28 @param[in] paths Argument passed to `scan_petsc_options()`.
29 @return Value returned by `scan_petsc_options()`.
30 """
31 flags: Set[str] = set()
32 for path in paths:
33 text = path.read_text(encoding="utf-8")
34 for match in OPTION_RE.finditer(text):
35 flags.add(match.group(1))
36 return flags
37
38
39def load_manifest(path: pathlib.Path) -> Set[str]:
40 """!
41 @brief Load manifest.
42 @param[in] path Filesystem path argument passed to `load_manifest()`.
43 @return Value returned by `load_manifest()`.
44 """
45 data = json.loads(path.read_text(encoding="utf-8"))
46 options = data.get("known_petsc_options")
47 if not isinstance(options, list):
48 raise ValueError("Manifest key 'known_petsc_options' must be a list.")
49 bad = [opt for opt in options if not isinstance(opt, str) or not opt.startswith("-")]
50 if bad:
51 raise ValueError(f"Manifest has invalid option entries: {bad}")
52 return set(options)
53
54
55def main() -> int:
56 """!
57 @brief Entry point for this script.
58 @return Value returned by `main()`.
59 """
60 parser = argparse.ArgumentParser(
61 description=(
62 "Scan PETSc option ingress in src/setup.c and src/io.c, then compare "
63 "against scripts/audit_ingress_manifest.json."
64 ),
65 formatter_class=argparse.RawDescriptionHelpFormatter,
66 epilog=(
67 "Examples:\n"
68 " python3 scripts/audit_ingress.py\n"
69 " python3 scripts/audit_ingress.py --show-scanned\n"
70 " python3 scripts/audit_ingress.py --manifest scripts/audit_ingress_manifest.json\n"
71 ),
72 )
73 parser.add_argument(
74 "--manifest",
75 default="scripts/audit_ingress_manifest.json",
76 help=(
77 "Manifest JSON path, relative to repository root unless absolute "
78 "(default: scripts/audit_ingress_manifest.json)."
79 ),
80 )
81 parser.add_argument(
82 "--show-scanned",
83 action="store_true",
84 help="Print discovered PETSc options before drift comparison.",
85 )
86 args = parser.parse_args()
87
88 repo_root = pathlib.Path(__file__).resolve().parents[1]
89 manifest_path = (repo_root / args.manifest).resolve()
90 scan_paths = [repo_root / "src/setup.c", repo_root / "src/io.c"]
91
92 if not manifest_path.exists():
93 print(f"[ERROR] Manifest not found: {manifest_path}", file=sys.stderr)
94 return 2
95
96 scanned = scan_petsc_options(scan_paths)
97 expected = load_manifest(manifest_path)
98
99 missing_in_manifest = sorted(scanned - expected)
100 stale_in_manifest = sorted(expected - scanned)
101
102 if args.show_scanned:
103 print("[INFO] Scanned PETSc options:")
104 for flag in sorted(scanned):
105 print(flag)
106 print("")
107
108 print(f"[INFO] Scanned options: {len(scanned)}")
109 print(f"[INFO] Manifest options: {len(expected)}")
110
111 if missing_in_manifest:
112 print("[ERROR] New PETSc ingress options missing in manifest:")
113 for flag in missing_in_manifest:
114 print(f" - {flag}")
115
116 if stale_in_manifest:
117 print("[ERROR] Manifest options no longer present in setup/io scan:")
118 for flag in stale_in_manifest:
119 print(f" - {flag}")
120
121 if missing_in_manifest or stale_in_manifest:
122 print(
123 "[FAIL] Ingress drift detected. Update scripts/audit_ingress_manifest.json and docs mapping.",
124 file=sys.stderr,
125 )
126 return 1
127
128 print("[OK] Ingress manifest matches setup/io PETSc option scan.")
129 return 0
130
131
132if __name__ == "__main__":
133 raise SystemExit(main())
Set[str] scan_petsc_options(Iterable[pathlib.Path] paths)
Perform scan petsc options.
Set[str] load_manifest(pathlib.Path path)
Load manifest.
int main()
Entry point for this script.