"""Il server non importa VisionSuite, e questo lo verifica. Senza qualcuno che lo controlli, il confine si perde in silenzio: basta un import comodo perché l'immagine dell'API torni da cinque gigabyte e il motivo per cui il worker esiste svanisca senza che nessuna prova diventi rossa. """ import ast from pathlib import Path BACKEND = Path(__file__).resolve().parents[1] # Full dotted prefixes, not bare roots: "src" alone would forbid every import # the backend legitimately makes of itself (src.backend.*), so the entries # for the vision tree are multi-segment ("src.vision", "src.vision_worker") # while VisionSuite's own top-level packages stay single-segment. # # M2: "Acquire" used to sit here as a stand-in for vs-camera, but it is a # class/SDK name (Balluff's "mvIMPACT Acquire" SDK), never an import root - # it could never match. vs-camera imports under the same `visionsuite` # namespace as vs-core (see # vendor/visionsuite/packages/vs-camera/pyproject.toml: "il codice si importa # come visionsuite.camera...."), which "visionsuite" below already forbids, # so there is nothing separate to add for it. FORBIDDEN = { "visionsuite", "pm2d", "dxf_compare", "torch", "src.vision", "src.vision_worker", } def _imported_modules(source: Path) -> set[str]: """Full dotted module paths this file imports - not just their first segment. `from src.vision.runner import ...` must be checked against "src.vision.runner" (inside the forbidden "src.vision"), not against "src" alone, which is never forbidden since the backend imports itself constantly. """ tree = ast.parse(source.read_text(encoding="utf-8"), filename=str(source)) modules: set[str] = set() for node in ast.walk(tree): if isinstance(node, ast.Import): modules.update(alias.name for alias in node.names) elif isinstance(node, ast.ImportFrom) and node.module and node.level == 0: modules.add(node.module) return modules def _forbidden_hits(modules: set[str]) -> set[str]: """Which FORBIDDEN entries a set of imported module paths triggers. A module counts as forbidden if it equals a forbidden entry exactly, or lives inside it ("src.vision.runner" is inside "src.vision"). """ hits: set[str] = set() for module in modules: for entry in FORBIDDEN: if module == entry or module.startswith(entry + "."): hits.add(entry) return hits def test_the_server_never_imports_visionsuite(): offenders = [] for source in BACKEND.rglob("*.py"): if "tests" in source.parts or "migrations" in source.parts: continue forbidden = _forbidden_hits(_imported_modules(source)) if forbidden: offenders.append(f"{source.relative_to(BACKEND)}: {sorted(forbidden)}") assert offenders == [], ( "il server deve restare cieco alla visione; usa il worker via HTTP:\n" + "\n".join(offenders) ) def test_the_submodule_is_pinned(): """Server e stazione devono montare lo stesso commit: qui c'è la fonte.""" gitmodules = Path(__file__).resolve().parents[3] / ".gitmodules" assert gitmodules.exists(), "vendor/visionsuite non è un sottomodulo" assert "vendor/visionsuite" in gitmodules.read_text(encoding="utf-8") def test_a_full_module_path_import_is_caught_not_just_its_root(tmp_path): """M2: `from src.vision.runner import ...` inside src/backend/** must be caught. Checking only the first dotted segment ("src") would miss it - "src" itself is never forbidden, the backend imports itself all the time. This is the blind spot the reviewer found: same repository, no install needed, the most tempting shortcut of all. """ offender = tmp_path / "sneaky.py" offender.write_text("from src.vision.runner import run_graph\n") assert _forbidden_hits(_imported_modules(offender)) == {"src.vision"} def test_a_vision_worker_import_is_also_caught(tmp_path): offender = tmp_path / "sneaky.py" offender.write_text("import src.vision_worker.main\n") assert _forbidden_hits(_imported_modules(offender)) == {"src.vision_worker"} def test_an_unrelated_src_backend_import_is_not_caught(tmp_path): """The fix must not turn every self-import of the backend into an offender - only the vision subtrees are forbidden.""" innocent = tmp_path / "innocent.py" innocent.write_text("from src.backend.config import settings\n") assert _forbidden_hits(_imported_modules(innocent)) == set()