Source code for embedl_deploy._internal.core.backend

# Copyright (C) 2026 Embedl AB

"""Backend discovery and selection."""

import importlib
from collections.abc import Sequence
from dataclasses import dataclass
from pathlib import Path

from embedl_deploy._internal.core.patterns.main import Pattern, Phase

_INTERNAL_DIR = Path(__file__).resolve().parent.parent


def _check_phase(
    patterns: Sequence[type[Pattern]],
    expected: Phase,
) -> None:
    for p in patterns:
        if p.phase != expected:
            raise ValueError(
                f"{p.__name__} has phase {p.phase.value!r}, "
                f"expected {expected.value!r} "
                f"(in {expected.value}_patterns)"
            )


[docs] @dataclass(frozen=True) class Backend: """A collection of patterns for a specific hardware target. Each field holds patterns for exactly one :class:`~embedl_deploy._internal.core.patterns.main.Phase`. ``__post_init__`` validates that every pattern's phase matches the field it is assigned to. """ #: Short unique identifier for the backend. name: str #: Structural rewrite patterns, applied iteratively. conversion_patterns: Sequence[type[Pattern]] = () #: Fusion patterns, applied in a single pass after conversions. fusion_patterns: Sequence[type[Pattern]] = () #: State-prep patterns, applied sequentially once each. state_prep_patterns: Sequence[type[Pattern]] = () #: Q/DQ stub surgery patterns, looped to a fixpoint. stub_prep_patterns: Sequence[type[Pattern]] = () def __post_init__(self) -> None: _check_phase(self.conversion_patterns, Phase.CONVERSION) _check_phase(self.fusion_patterns, Phase.FUSION) _check_phase(self.state_prep_patterns, Phase.STATE_PREP) _check_phase(self.stub_prep_patterns, Phase.STUB_PREP)
@dataclass class _BackendState: """Mutable state for backend discovery and selection.""" #: The currently selected backend. selected: Backend | None = None #: Cached discovery result. discovered: list[Backend] | None = None _backend_state = _BackendState()
[docs] def reset_backends() -> None: """Clear cached discovery results and the active backend.""" _backend_state.selected = None _backend_state.discovered = None
[docs] def discover_backends() -> list[Backend]: """Return all installed backends. Results are cached after the first call. :returns: List of discovered :class:`~embedl_deploy._internal.core.backend.Backend` instances. """ backends = _backend_state.discovered if backends is None: backends = [] for entry in sorted(_INTERNAL_DIR.iterdir()): if ( not entry.is_dir() or entry.name.startswith("_") or entry.name == "core" ): continue module_path = f"embedl_deploy._internal.{entry.name}.backend" try: mod = importlib.import_module(module_path) except ModuleNotFoundError as e: if e.name == module_path: continue raise for attr in vars(mod).values(): if isinstance(attr, Backend): backends.append(attr) seen: dict[str, Backend] = {} for b in backends: if b.name in seen: raise ValueError( "Multiple backend definitions with the same name" f" {b.name!r}." ) seen[b.name] = b _backend_state.discovered = backends return list(backends)
[docs] def get_backend() -> Backend: """Return the active backend, discovering it if necessary. If no backend has been set via :func:`~embedl_deploy._internal.core.backend.set_backend`, the installed backends are discovered automatically. When exactly one is found it becomes the active backend. :returns: The active :class:`~embedl_deploy._internal.core.backend.Backend`. :raises RuntimeError: If no backends are installed, or if multiple backends are installed and none has been explicitly selected. """ backend = _backend_state.selected if backend is None: backends = discover_backends() if len(backends) == 0: raise RuntimeError( "No backends found — install at least one backend" ) if len(backends) > 1: names = ", ".join(sorted(b.name for b in backends)) raise RuntimeError( f"Multiple backends found ({names}). " "Call set_backend() to select one." ) backend = backends[0] _backend_state.selected = backend return backend
[docs] def set_backend(name: str) -> None: """Select the active backend by name. :param name: The name of a discovered backend (e.g. ``"tensorrt"``, ``"lattice_advanced"``). :raises ValueError: If `name` does not match any installed backend. """ backend_map = {b.name: b for b in discover_backends()} if name not in backend_map: available = ", ".join(sorted(backend_map)) or "(none)" raise ValueError( f"Backend {name!r} not found. Available backends: {available}" ) _backend_state.selected = backend_map[name]