Source code for gcode_reader.emulate.registry

"""Dynamic registry for Machine subclasses.

Machines self-register via the :func:`register` decorator. Third-party packages
can add new machine types by applying the same decorator and advertising the
class through the ``gcode_reader.machines`` entry-point group in their
``pyproject.toml``::

    [project.entry-points."gcode_reader.machines"]
    acme = "acme_gcode:AcmePrinter"

The registry is populated at import time; call :func:`load_entry_points` once
(e.g. in ``gcode_reader/__init__.py``) to pull in installed third-party machines.
"""

from __future__ import annotations

from importlib.metadata import entry_points
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from .machine import Machine

_registry: dict[str, type[Machine]] = {}


[docs] def register(name: str, aliases: list[str] | None = None): """Class decorator that registers a Machine subclass by name. Args: name: Primary key used to look up the machine. aliases: Optional additional keys that resolve to the same class. Example:: @register("acme", aliases=["acme_v2"]) class AcmePrinter(Machine): ... """ def decorator(cls: type[Machine]) -> type[Machine]: _registry[name] = cls for alias in aliases or []: _registry[alias] = cls return cls return decorator
[docs] def get(name: str) -> type[Machine]: """Return the Machine subclass registered under *name*. Args: name: Key or alias (case-insensitive, spaces and underscores ignored). Raises: ValueError: If *name* is not found in the registry. """ key = name.lower().replace(" ", "").replace("_", "") if key not in _registry: available = sorted(set(_registry)) raise ValueError( f"Machine '{name}' not recognised. Available machines:\n\t{available}" ) return _registry[key]
[docs] def list_machines() -> dict[str, type[Machine]]: """Return a copy of the full registry mapping (names + aliases -> classes).""" return dict(_registry)
[docs] def load_entry_points() -> None: """Discover and import third-party machines registered via entry points. Call this once at package initialisation to pick up machines shipped by external packages. Safe to call multiple times. """ for ep in entry_points(group="gcode_reader.machines"): ep.load()