Source code for app.adapters.registry
"""Backend registry + ``get_backend()`` resolver.
sfmapi ships **no concrete SfM backend**. The wire surface
(REST routes, request/response schemas, error envelope, capability
discovery) is engine-independent; backend implementations live in
separate repositories. To run a real workload, install one such
implementation and register it.
Worker code never imports a specific backend module — it calls
:func:`get_backend` and uses the returned backend object. Which
backend is returned is controlled by the ``SFMAPI_BACKEND``
environment variable (or the explicit ``name`` arg).
Adding a backend (separate package or app-startup hook):
.. code-block:: python
from app.adapters.registry import register_backend
class MyBackend:
name = "my_backend"
... # implement Backend and any optional stage/action surfaces
register_backend("my_backend", MyBackend)
Then set ``SFMAPI_BACKEND=my_backend`` and the worker picks it up
without any other change.
"""
from __future__ import annotations
import os
from collections.abc import Callable
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from app.adapters.backend import Backend
_REGISTRY: dict[str, Callable[[], Backend]] = {}
[docs]
def register_backend(name: str, factory: Callable[[], Backend]) -> None:
"""Register a backend factory. Re-registering an existing name
overwrites it (useful for tests)."""
_REGISTRY[name] = factory
[docs]
def list_backends() -> list[str]:
return sorted(_REGISTRY)
[docs]
def get_backend(name: str | None = None) -> Backend:
"""Resolve and instantiate the configured backend.
``name`` overrides the env var when set (mostly for tests).
Raises :class:`KeyError` if no backend is registered under the
resolved name — sfmapi ships no default implementation, so the
caller (or test fixture, or app-startup hook) must register one
before workers can run.
"""
chosen = name or os.environ.get("SFMAPI_BACKEND")
if not chosen:
raise KeyError(
"no sfmapi backend selected: set SFMAPI_BACKEND or pass `name=` "
f"explicitly. Registered backends: {list_backends()}"
)
if chosen not in _REGISTRY:
raise KeyError(
f"unknown sfmapi backend {chosen!r}; registered: {list_backends()}. "
"Install + register a backend implementation in app startup."
)
return _REGISTRY[chosen]()
__all__ = ["get_backend", "list_backends", "register_backend"]