initial commit for public eval
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate values.tex from plugins under analysis/values/.
|
||||
|
||||
Each plugin exposes compute(derived) -> (keys, sources). Keys are merged into
|
||||
one \\pgfkeys block; sources are recorded in the header for provenance.
|
||||
"""
|
||||
import argparse
|
||||
import datetime as dt
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
from types import ModuleType
|
||||
|
||||
def load_plugins(plugin_dir: Path, skip: set[str]) -> list[tuple[str, ModuleType]]:
|
||||
plugins: list[tuple[str, ModuleType]] = []
|
||||
for p in sorted(plugin_dir.glob("*.py")):
|
||||
if p.name.startswith("_") or p.stem in skip:
|
||||
continue
|
||||
spec = importlib.util.spec_from_file_location(f"values.{p.stem}", p)
|
||||
if spec is None or spec.loader is None:
|
||||
raise SystemExit(f"could not load plugin {p}")
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
if hasattr(mod, "compute"):
|
||||
plugins.append((p.stem, mod))
|
||||
return plugins
|
||||
|
||||
|
||||
def file_mtime_iso(p: Path) -> str:
|
||||
return dt.datetime.fromtimestamp(p.stat().st_mtime).isoformat(timespec="seconds")
|
||||
|
||||
|
||||
# Map key-name suffix to siunitx unit.
|
||||
UNIT_SUFFIXES = {
|
||||
"-us": r"\micro\second",
|
||||
"-ms": r"\milli\second",
|
||||
"-gbps": r"\giga\bit\per\second",
|
||||
"-pct": r"\percent",
|
||||
"-kb": r"\kilo\byte",
|
||||
}
|
||||
|
||||
|
||||
NUM_SUFFIXES = ("-p", "-alpha", "-n", "-uncorrected")
|
||||
NUM_PREFIXES = ("n-",)
|
||||
|
||||
|
||||
def value_to_latex(key: str, value: str) -> str:
|
||||
leaf = key.rsplit("/", 1)[-1]
|
||||
for suffix, unit in UNIT_SUFFIXES.items():
|
||||
if leaf.endswith(suffix):
|
||||
return f"\\qty{{{value}}}{{{unit}}}"
|
||||
if any(leaf.startswith(p) for p in NUM_PREFIXES) or any(leaf.endswith(s) for s in NUM_SUFFIXES):
|
||||
return f"\\num{{{value}}}"
|
||||
return value
|
||||
|
||||
|
||||
def format_pgfkeys(all_keys: dict[str, str], provenance: list[str]) -> str:
|
||||
lines: list[str] = []
|
||||
lines.append("% Auto-generated by analysis/gen_values.py. Do not edit by hand.")
|
||||
lines.append(f"% Generated: {dt.datetime.now().isoformat(timespec='seconds')}")
|
||||
lines.extend(provenance)
|
||||
for k, v in sorted(all_keys.items()):
|
||||
lines.append(f"\\pgfkeyssetvalue{{/{k}}}{{{value_to_latex(k, v)}}}")
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def main() -> None:
|
||||
p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
p.add_argument("--derived", required=True, type=Path, help="derived/ root with per-experiment subdirs")
|
||||
p.add_argument("--out", required=True, type=Path, help="output values.tex path")
|
||||
p.add_argument("--plugins", type=Path, default=Path(__file__).parent / "values",
|
||||
help="plugin directory (default: analysis/values)")
|
||||
p.add_argument("--skip", nargs="*", default=[],
|
||||
help="plugin names to skip (stem only, e.g. --skip cpu rtt)")
|
||||
args = p.parse_args()
|
||||
|
||||
plugins = load_plugins(args.plugins, set(args.skip))
|
||||
if not plugins:
|
||||
raise SystemExit(f"no plugins found in {args.plugins} (after --skip)")
|
||||
|
||||
all_keys: dict[str, str] = {}
|
||||
provenance: list[str] = ["% Plugins:"]
|
||||
for name, mod in plugins:
|
||||
keys, sources = mod.compute(args.derived)
|
||||
dup = set(keys) & set(all_keys)
|
||||
if dup:
|
||||
raise SystemExit(f"plugin {name} duplicates keys: {sorted(dup)}")
|
||||
all_keys.update(keys)
|
||||
provenance.append(f"% {name}: {len(keys)} keys")
|
||||
for src in sources:
|
||||
provenance.append(f"% {src} (mtime {file_mtime_iso(src)})")
|
||||
print(f" {name}: {len(keys)} keys from {len(sources)} sources")
|
||||
|
||||
args.out.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.out.write_text(format_pgfkeys(all_keys, provenance))
|
||||
print(f"wrote {args.out} ({len(all_keys)} keys total)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user