"""Op dispatch registry. The cursor core defines the op contract; backends (in studio/backends/) register concrete implementations via register_backend. Keeps pixel_cursor/ free of model imports — torch/diffusers/MotionClone never enter the core package. """ from __future__ import annotations from typing import Callable, Any _REGISTRY: dict[tuple[str, str], Callable] = {} def register_backend(op_name: str, backend_name: str, fn: Callable) -> None: _REGISTRY[(op_name, backend_name)] = fn def list_backends(op_name: str | None = None) -> list[tuple[str, str]]: if op_name is None: return sorted(_REGISTRY.keys()) return sorted(k for k in _REGISTRY if k[0] == op_name) def _dispatch(op_name: str, backend: str, cursor, *args, **kwargs) -> Any: if backend == "auto": for (op, _name), fn in _REGISTRY.items(): if op == op_name: return fn(cursor, *args, **kwargs) raise NotImplementedError( f"No backend registered for op {op_name!r}. " f"Register one via pixel_cursor.ops.register_backend()." ) fn = _REGISTRY.get((op_name, backend)) if fn is None: available = sorted({n for (o, n) in _REGISTRY if o == op_name}) raise NotImplementedError( f"No backend {backend!r} for op {op_name!r}; available: {available}" ) return fn(cursor, *args, **kwargs)