Source code for rath.memory.registry
"""Memory backend registry: register / lookup / select.
Memory backends register a class under a string name. Public lookup helpers
operate on classes; :func:`get` instantiates a backend on demand. Mirrors
:mod:`rath.backend.registry` while staying independent of it.
"""
from __future__ import annotations
from collections.abc import Callable
from typing import TypeVar
from rath.memory.abc import MemoryBackend
from rath.memory.errors import MemoryBackendNotFound
_REGISTRY: dict[str, type[MemoryBackend]] = {}
_DEFAULT: dict[str, str] = {}
B = TypeVar("B", bound=MemoryBackend)
[docs]
def register(name: str) -> Callable[[type[B]], type[B]]:
"""Decorator: register a :class:`MemoryBackend` subclass under ``name``."""
def decorator(cls: type[B]) -> type[B]:
if name in _REGISTRY:
raise ValueError(f"memory backend {name!r} is already registered")
cls.name = name
_REGISTRY[name] = cls
return cls
return decorator
[docs]
def list_names() -> list[str]:
"""Return the names of all registered memory backends, in registration order."""
return list(_REGISTRY)
[docs]
def get(name: str) -> MemoryBackend:
"""Look up a memory backend by name and return a fresh instance."""
cls = _get_class(name)
return cls()
[docs]
def get_class(name: str) -> type[MemoryBackend]:
"""Look up the registered class for ``name`` without instantiating it."""
return _get_class(name)
[docs]
def is_available(name: str) -> bool:
"""Return ``True`` iff a memory backend named ``name`` is registered and available."""
if name not in _REGISTRY:
return False
return _REGISTRY[name].is_available()
[docs]
def preferred(names: list[str]) -> MemoryBackend:
"""Return an instance of the first available memory backend in ``names``.
Raises :class:`MemoryBackendNotFound` if none of the listed backends are
registered and available.
"""
for n in names:
if n in _REGISTRY and _REGISTRY[n].is_available():
return _REGISTRY[n]()
available = [n for n in _REGISTRY if _REGISTRY[n].is_available()]
raise MemoryBackendNotFound(name=", ".join(names), available=available)
[docs]
def set_default(name: str) -> None:
"""Set the default memory backend used by :func:`current`."""
_get_class(name) # Raises if ``name`` is unknown.
_DEFAULT["name"] = name
[docs]
def current() -> MemoryBackend:
"""Return a fresh instance of the default memory backend.
Raises :class:`MemoryBackendNotFound` if no default has been set.
"""
if "name" not in _DEFAULT:
raise MemoryBackendNotFound(name="<default>", available=list_names())
return get(_DEFAULT["name"])
def _get_class(name: str) -> type[MemoryBackend]:
if name not in _REGISTRY:
raise MemoryBackendNotFound(name=name, available=list_names())
return _REGISTRY[name]
def _reset() -> None:
"""Clear the registry. Test-only helper."""
_REGISTRY.clear()
_DEFAULT.clear()