yksa-orbital/yksa_orbital/registry.py
2026-08-18 22:01:53 +08:00

52 lines
1.8 KiB
Python

"""Which propagator answers, and one instance of it per process.
``get_backend()`` with no argument returns the deployment's configured default;
with a name it returns that one specifically. Both matter. The default is what
the request path should use, and the explicit name is what a caller needing
physics the default cannot do -- space weather, drag fitting, decay -- must pass.
That second case is not hypothetical. The default is ``sgp4``, which raises on
any of those; a view that forgot to name ``"orekit"`` and swallowed the error
rendered an empty chart for weeks without anything logging a complaint. If your
call needs the sidecar, say so.
"""
from __future__ import annotations
from django.conf import settings
from odm import PropagatorBackend
from .backends.orekit import OrekitBackend
from .backends.sgp4 import Sgp4Backend
BACKENDS: dict[str, type[PropagatorBackend]] = {
"sgp4": Sgp4Backend,
"orekit": OrekitBackend,
}
#: One instance per name. Backends are stateless apart from an HTTP client, so
#: sharing them saves a connection pool rather than risking shared state.
_INSTANCES: dict[str, PropagatorBackend] = {}
DEFAULT_BACKEND = "sgp4"
def register(name: str, backend: type[PropagatorBackend]) -> None:
"""Add a backend under ``name``. For a service with its own propagator."""
BACKENDS[name] = backend
_INSTANCES.pop(name, None)
def get_backend(name: str | None = None) -> PropagatorBackend:
key = name or getattr(settings, "ORBITAL_PROPAGATOR_BACKEND", DEFAULT_BACKEND)
try:
cls = BACKENDS[key]
except KeyError as exc:
raise ValueError(
f"unknown propagation backend {key!r}; "
f"expected one of {', '.join(sorted(BACKENDS))}"
) from exc
if key not in _INSTANCES:
_INSTANCES[key] = cls()
return _INSTANCES[key]