initial commit
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
"""Blender headless scene validation (collections and naming).
|
||||
|
||||
Usage (from repo root):
|
||||
blender -b sensors/finger/r01/source/geometry.blend -P blender/scripts/validate.py -- sensors/finger/r01
|
||||
|
||||
Note: this script loads design.yaml with PyYAML. Prefer installing PyYAML into
|
||||
Blender's Python, or run metadata-only checks via: orisys-design validate <revision>
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
yaml = None
|
||||
|
||||
|
||||
def _argv_after_double_dash() -> list[str]:
|
||||
if "--" not in sys.argv:
|
||||
return []
|
||||
return sys.argv[sys.argv.index("--") + 1 :]
|
||||
|
||||
|
||||
def _load_design(revision_dir: Path) -> dict:
|
||||
if yaml is None:
|
||||
raise RuntimeError(
|
||||
"PyYAML is not installed in Blender's Python. "
|
||||
"Run: orisys-design validate <revision> without --blender, "
|
||||
"or pip install PyYAML into Blender's Python."
|
||||
)
|
||||
with (revision_dir / "design.yaml").open(encoding="utf-8") as handle:
|
||||
data = yaml.safe_load(handle)
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError("design.yaml must be a mapping")
|
||||
return data
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = _argv_after_double_dash()
|
||||
if not args:
|
||||
print("validate.py: pass revision directory after --", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
revision_dir = Path(args[0]).resolve()
|
||||
try:
|
||||
design = _load_design(revision_dir)
|
||||
except Exception as exc:
|
||||
print(f"ERROR: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
collections = design.get("collections", {})
|
||||
objects = design.get("objects", {})
|
||||
|
||||
errors: list[str] = []
|
||||
present = {col.name for col in bpy.data.collections}
|
||||
|
||||
for key, expected_name in collections.items():
|
||||
if expected_name not in present:
|
||||
errors.append(f"Missing collection {expected_name!r} (design key: {key})")
|
||||
|
||||
for key, expected_name in objects.items():
|
||||
obj = bpy.data.objects.get(expected_name)
|
||||
if obj is None:
|
||||
errors.append(f"Missing object {expected_name!r} (design key: {key})")
|
||||
|
||||
if errors:
|
||||
for message in errors:
|
||||
print(f"ERROR: {message}", file=sys.stderr)
|
||||
print(f"Present collections: {sorted(present)}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
print(f"OK: Blender scene matches conventions for {revision_dir.name}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user