| """Protocol registry and contracts for syndrome-net execution backends.""" |
|
|
| from __future__ import annotations |
|
|
| import logging |
| from importlib.metadata import EntryPoint, entry_points |
| from typing import Any, Iterable, Iterator |
|
|
| from surface_code_in_stem.protocols.base import ProtocolContract, ProtocolRegistry |
| from surface_code_in_stem.protocols.nisq_protocol import NISQProtocol |
| from surface_code_in_stem.protocols.sqkd_protocol import SQKDProtocol |
| from surface_code_in_stem.protocols.surface_protocol import SurfaceProtocol |
|
|
| _LOGGER = logging.getLogger(__name__) |
| _PROTOCOL_ENTRYPOINT_GROUP = "syndrome_net.protocol_backends" |
|
|
|
|
| def _iter_entry_points(group: str) -> Iterable[EntryPoint]: |
| """Return entry-point definitions for discovery while handling import failures.""" |
| try: |
| points = entry_points() |
| except Exception as exc: |
| _LOGGER.warning("Unable to enumerate protocol entry points for %s: %s", group, exc) |
| if __debug__: |
| _LOGGER.debug("Error enumerating protocol entry points for %s", group, exc_info=True) |
| return () |
|
|
| if hasattr(points, "select"): |
| try: |
| discovered = tuple(points.select(group=group)) |
| except Exception as exc: |
| _LOGGER.warning("Failed to select protocol entry points for %s: %s", group, exc) |
| if __debug__: |
| _LOGGER.debug("Error selecting protocol entry points for %s", group, exc_info=True) |
| return () |
| try: |
| return tuple(sorted(discovered, key=lambda point: point.name)) |
| except Exception as exc: |
| _LOGGER.warning("Unable to sort protocol entry points for %s: %s", group, exc) |
| if __debug__: |
| _LOGGER.debug("Error sorting protocol entry points for %s", group, exc_info=True) |
| return discovered |
|
|
| try: |
| discovered = tuple(points.get(group, ())) |
| except Exception as exc: |
| _LOGGER.warning("Unable to read protocol entry points for %s: %s", group, exc) |
| if __debug__: |
| _LOGGER.debug("Error reading protocol entry points for %s", group, exc_info=True) |
| return () |
|
|
| try: |
| return tuple(sorted(discovered, key=lambda point: point.name)) |
| except Exception as exc: |
| _LOGGER.warning("Unable to sort protocol entry points for %s: %s", group, exc) |
| if __debug__: |
| _LOGGER.debug("Error sorting protocol entry points for %s", group, exc_info=True) |
| return discovered |
|
|
|
|
| def _materialize_component(component_factory: object) -> Any: |
| if isinstance(component_factory, type): |
| return component_factory() |
|
|
| if callable(component_factory): |
| return component_factory() |
|
|
| return component_factory |
|
|
|
|
| def _is_valid_protocol(protocol: object) -> bool: |
| return ( |
| hasattr(protocol, "contract") |
| and hasattr(protocol, "supports") |
| and hasattr(protocol, "normalize_context") |
| and hasattr(protocol, "validate_context") |
| ) |
|
|
|
|
| def _iter_discovered_protocols() -> Iterator[tuple[str, object]]: |
| """Yield (name, protocol_instance) pairs from discovered protocol backends.""" |
| try: |
| discovered_points = tuple(_iter_entry_points(_PROTOCOL_ENTRYPOINT_GROUP)) |
| except Exception as exc: |
| _LOGGER.warning( |
| "Unable to iterate protocol entry points for %s: %s", |
| _PROTOCOL_ENTRYPOINT_GROUP, |
| exc, |
| ) |
| if __debug__: |
| _LOGGER.debug("Error iterating protocol entry points", exc_info=True) |
| return |
|
|
| for point in discovered_points: |
| try: |
| loaded = point.load() |
| protocol = _materialize_component(loaded) |
| except Exception as exc: |
| _LOGGER.warning( |
| "Skipping protocol entry point '%s' (%s): %s", |
| point.name, |
| _PROTOCOL_ENTRYPOINT_GROUP, |
| exc, |
| ) |
| if __debug__: |
| _LOGGER.debug("Protocol entry point load failure", exc_info=True) |
| continue |
|
|
| if not _is_valid_protocol(protocol): |
| _LOGGER.warning( |
| "Skipping protocol entry point '%s': protocol interface missing required attributes", |
| point.name, |
| ) |
| if __debug__: |
| _LOGGER.debug("Invalid protocol candidate: %r", protocol) |
| continue |
|
|
| yield point.name, protocol |
|
|
|
|
| def create_default_protocol_registry() -> ProtocolRegistry: |
| registry = ProtocolRegistry() |
| registry.register(SurfaceProtocol()) |
| registry.register(NISQProtocol()) |
| registry.register(SQKDProtocol()) |
|
|
| for protocol_name, protocol in sorted( |
| _iter_discovered_protocols(), |
| key=lambda item: item[0], |
| ): |
| if protocol.contract.name != protocol_name: |
| _LOGGER.warning( |
| "Overriding protocol entry point name '%s' with contract name '%s'", |
| protocol_name, |
| protocol.contract.name, |
| ) |
|
|
| try: |
| registry.register(protocol) |
| except Exception as exc: |
| _LOGGER.warning("Skipping protocol '%s': %s", protocol_name, exc) |
| if __debug__: |
| _LOGGER.debug("Error registering dynamic protocol backend", exc_info=True) |
|
|
| return registry |
|
|
|
|
| DEFAULT_PROTOCOL_REGISTRY = create_default_protocol_registry() |
|
|
| __all__ = [ |
| "ProtocolRegistry", |
| "ProtocolContract", |
| "SurfaceProtocol", |
| "NISQProtocol", |
| "SQKDProtocol", |
| "create_default_protocol_registry", |
| "DEFAULT_PROTOCOL_REGISTRY", |
| ] |
|
|