#!/usr/bin/env python3 """Generate a minimal native Blender compositor fixture for one WBV2 batch.""" from __future__ import annotations import argparse import json import subprocess from pathlib import Path ROOT = Path(__file__).resolve().parents[1] NATIVE_BLENDER = ROOT / "build_blender_native/bin/blender" def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--output", required=True, help="repository-relative .blend output under tests/files/web") parser.add_argument("--node", action="append", required=True, help="Blender CompositorNode type identifier") args = parser.parse_args() output = Path(args.output) if output.is_absolute() or ".." in output.parts or output.suffix != ".blend" or not str(output).startswith("tests/files/web/"): parser.error("--output must be a repository-relative .blend path under tests/files/web/") nodes = list(dict.fromkeys(args.node)) if not nodes or any(not node.startswith("CompositorNode") or not node.replace("_", "").isalnum() for node in nodes): parser.error("every --node must be a Blender CompositorNode identifier") if not NATIVE_BLENDER.is_file(): raise SystemExit(f"native Blender fixture generator is unavailable: {NATIVE_BLENDER.relative_to(ROOT)}") destination = ROOT / output destination.parent.mkdir(parents=True, exist_ok=True) script = ( "import bpy\n" f"destination = {str(destination)!r}\n" f"node_types = {nodes!r}\n" "scene = bpy.context.scene\n" "tree = bpy.data.node_groups.new('WBV2 Compositor Native Batch', 'CompositorNodeTree')\n" "scene.compositing_node_group = tree\n" "tree.interface.new_socket(name='Image', in_out='OUTPUT', socket_type='NodeSocketColor')\n" "source = tree.nodes.new('CompositorNodeRGB')\n" "source.name = 'WBV2 Batch Source'\n" "output = tree.nodes.new('NodeGroupOutput')\n" "output.name = 'WBV2 Batch Output'\n" "tree.links.new(source.outputs[0], output.inputs[0])\n" "for node_type in node_types:\n" " node = tree.nodes.new(node_type)\n" " node.name = 'WBV2 ' + node_type\n" "bpy.ops.wm.save_as_mainfile(filepath=destination)\n" ) result = subprocess.run( [str(NATIVE_BLENDER), "--background", "--factory-startup", "--python-expr", script], cwd=ROOT, text=True, capture_output=True, check=False, ) if result.returncode or not destination.is_file(): raise SystemExit(f"native compositor fixture generation failed ({result.returncode}):\n{result.stdout}{result.stderr}") print(json.dumps({"status": "PASS", "fixture": output.as_posix(), "nodes": nodes, "bytes": destination.stat().st_size})) return 0 if __name__ == "__main__": raise SystemExit(main())