Source code for synthpriv.core.registry

"""Generator registry.

Generators are registered with the ``@register_generator`` decorator and
instantiated by name (key). This lets the pipeline, the CLI or a REST API
resolve generators without importing each class directly.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import TYPE_CHECKING, TypeVar

if TYPE_CHECKING:  # pragma: no cover
    from synthpriv.core.base import BaseSynthesizer

SynthesizerT = TypeVar("SynthesizerT", bound="BaseSynthesizer")


[docs] class GeneratorNotFoundError(KeyError): """A generator that is not registered was requested."""
[docs] @dataclass class GeneratorSpec: """Registry entry.""" key: str cls: type description: str = field(default="") supports: tuple[str, ...] = field(default=("tabular",))
_REGISTRY: dict[str, GeneratorSpec] = {}
[docs] def register_generator(key: str, description: str = "", supports: tuple[str, ...] = ("tabular",)): """Decorator to register a class as a generator under ``key``.""" def decorate(cls: type) -> type: if key in _REGISTRY: raise ValueError(f"Generator {key!r} is already registered") doc = description or getattr(cls, "description", "") or cls.__doc__ or "" _REGISTRY[key] = GeneratorSpec(key=key, cls=cls, description=doc.strip(), supports=supports) return cls return decorate
[docs] def list_generators() -> list[str]: """Keys of all registered generators.""" return sorted(_REGISTRY)
[docs] def get_generator(key: str) -> GeneratorSpec: """Return the specification of a registered generator.""" try: return _REGISTRY[key] except KeyError as exc: raise GeneratorNotFoundError( f"Generator {key!r} not registered. Available: {list_generators()}" ) from exc
[docs] def build_generator(key: str, *args, **kwargs) -> "BaseSynthesizer": """Instantiate a generator from its registered key.""" spec = get_generator(key) return spec.cls(*args, **kwargs)