"""Loader: resolve a hermaguard tool script and return its module. Resolution order: 1. Source checkout: walk up from this package to the repo root (tools/). 2. pip install: sys.prefix/share/hermaguard/tools (setuptools data-files). """ import importlib.util import inspect import sys from pathlib import Path _HERE = Path(__file__).resolve().parent # Console-script name -> (tool dir name, preferred file stem) TOOL_LAYOUT = { "coverage": ("hermaguard-coverage", "hermaguard-coverage"), "locate": ("hermaguard-locate", "hermaguard-locate"), "role_patterns": ("hermaguard-role-patterns", "hermaguard-role-patterns"), "sanitize": ("hermaguard-sanitize", "hermaguard-sanitize"), "verify": ("hermaguard-verify", "hermaguard-verify"), "grader": ("hermaguard-grader", "hermaguard_grader"), "prescan": ("hermaguard-prescan", "hermaguard-prescan"), "compile_rules": ("hermaguard-compile-rules", "hermaguard-compile-rules"), "benchmark": ("hermaguard-benchmark", "benchmark"), } def _find_tools_root() -> Path: """Locate the tools/ directory (source checkout or pip data-files).""" # Source checkout: hermaguard_tools/ sits at repo root. for parent in _HERE.parents: cand = parent / "tools" if (cand / "hermaguard-coverage" / "hermaguard-coverage.py").is_file(): return cand # pip install: data-files land under sys.prefix/share/hermaguard/tools. # NOTE: setuptools data-files FLATTEN the tree, so the tool scripts sit # directly in that dir (no per-tool subdirectories). cand = Path(sys.prefix) / "share" / "hermaguard" / "tools" if cand.is_dir() and any(cand.glob("*.py")): return cand raise ImportError( "hermaguard tools/ not found — run from the repo checkout, or install " "with `pip install .` (data-files include the tools)." ) def load_tool(name: str): """Import and return the module for the named tool (cached).""" mod_name = f"hermaguard_tools._tool_{name}" if mod_name in sys.modules: return sys.modules[mod_name] dir_name, stem = TOOL_LAYOUT[name] root = _find_tools_root() # Source checkout layout: tools/