Source code for regstack.backends.factory
"""Pick the right backend implementation from ``config.database_url``.
URL scheme decides which package is loaded. SQL backends are imported
lazily so a Mongo-only deployment doesn't pay the SQLAlchemy import
cost (and vice versa).
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from regstack.backends.base import Backend, BackendKind
if TYPE_CHECKING:
from regstack.auth.clock import Clock
from regstack.config.schema import RegStackConfig
def detect_backend_kind(database_url: str) -> BackendKind:
"""Map a ``database_url`` to a backend kind."""
lowered = database_url.lower()
if lowered.startswith(("mongodb://", "mongodb+srv://")):
return BackendKind.MONGO
if lowered.startswith(("postgresql://", "postgresql+asyncpg://", "postgres://")):
return BackendKind.POSTGRES
if lowered.startswith(("sqlite://", "sqlite+aiosqlite://")):
return BackendKind.SQLITE
raise ValueError(
f"Unrecognised database_url scheme: {database_url!r}. "
"Expected one of: sqlite+aiosqlite://, postgresql+asyncpg://, mongodb://."
)
[docs]
def build_backend(config: RegStackConfig, *, clock: Clock | None = None) -> Backend:
"""Construct the configured backend without opening any sockets yet
(pools are lazy in every implementation).
"""
from regstack.auth.clock import SystemClock
clock_obj = clock or SystemClock()
kind = detect_backend_kind(config.database_url.get_secret_value())
if kind is BackendKind.MONGO:
from regstack.backends.mongo.backend import MongoBackend
return MongoBackend(config=config, clock=clock_obj)
if kind in (BackendKind.SQLITE, BackendKind.POSTGRES):
from regstack.backends.sql.backend import SqlBackend
return SqlBackend(config=config, clock=clock_obj, kind=kind)
raise AssertionError(f"unhandled backend kind {kind}") # pragma: no cover