face-intel / scripts /check_imports.py
Marwan
Restructure + add reverse face search (PimEyes-style)
f5eeb1c
Raw
History Blame Contribute Delete
3.69 kB
#!/usr/bin/env python3
"""
Import-graph checker — verifies the one-way dependency direction.
Run: python scripts/check_imports.py
Exits 0 if every layer only imports from layers below it.
Exits 1 with a violation report otherwise.
"""
from __future__ import annotations
import importlib
import pkgutil
import sys
from pathlib import Path
# Add project root to path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
# Layer rank — lower number = lower in the stack
LAYER_RANK = {
"models": 0,
"utils": 1,
"config": 1, # config is foundational
"storage": 2,
"metrics": 2, # metrics depends on models only
"providers": 3,
"pipeline": 4,
"orchestrator": 5,
"normalization": 5,
"confidence": 5,
"services": 6,
"api": 7,
}
# Allowed cross-layer imports (lower → higher is forbidden;
# higher → lower is allowed; same-layer is allowed)
# metrics is allowed to import from models only (not storage/providers)
# but we treat it as layer 2 (same as storage) so it can't import storage
def get_layer(module_name: str) -> int | None:
for prefix, rank in LAYER_RANK.items():
if module_name == prefix or module_name.startswith(prefix + "."):
return rank
return None
def check_package(package_name: str) -> list[str]:
"""Import every module in a package and check its imports."""
violations: list[str] = []
try:
pkg = importlib.import_module(package_name)
except ImportError as e:
print(f" SKIP {package_name}: {e}")
return violations
pkg_layer = get_layer(package_name)
if pkg_layer is None:
return violations
# Walk all submodules
if hasattr(pkg, "__path__"):
for _, modname, _ in pkgutil.walk_packages(pkg.__path__, prefix=f"{package_name}."):
try:
mod = importlib.import_module(modname)
except Exception as e:
print(f" SKIP {modname}: {e}")
continue
mod_layer = get_layer(modname)
if mod_layer is None:
continue
# Check every imported module
for imported_name in list(vars(mod).keys()):
# crude: check sys.modules for the actual imported packages
pass
# Better: check mod.__dict__ for module objects
for attr_name, attr_val in vars(mod).items():
if not hasattr(attr_val, "__name__"):
continue
if not isinstance(attr_val, type(sys)): # not a module
continue
imported_module = getattr(attr_val, "__name__", "")
imported_layer = get_layer(imported_module)
if imported_layer is None:
continue
if imported_layer > mod_layer:
violations.append(
f" VIOLATION: {modname} (layer {mod_layer}) imports "
f"{imported_module} (layer {imported_layer})"
)
return violations
def main() -> int:
print("Checking import graph for one-way dependency violations...")
print()
all_violations: list[str] = []
for pkg in LAYER_RANK:
print(f"Checking {pkg}/...")
violations = check_package(pkg)
all_violations.extend(violations)
print()
if all_violations:
print(f"FOUND {len(all_violations)} violation(s):")
for v in all_violations:
print(v)
return 1
else:
print("OK — no dependency-direction violations found.")
return 0
if __name__ == "__main__":
sys.exit(main())