Source code for camacq.event

"""Hold events."""

from __future__ import annotations

from collections.abc import Callable
import logging
from typing import TYPE_CHECKING, Any, ClassVar

from camacq.const import BASE_EVENT

if TYPE_CHECKING:
    from camacq.control import Center

_LOGGER = logging.getLogger(__name__)

EventHandler = Callable[["Center", "Event"], Any]


[docs] class Event: """A base event. Parameters ---------- data : dict, optional The data of the event. """ __slots__ = {"data": "Return the data of the event."} event_type: ClassVar[str] = BASE_EVENT def __init__(self, data: dict[str, Any] | None = None) -> None: """Set up event.""" self.data: dict[str, Any] = data or {} def __repr__(self) -> str: """Return the representation.""" return f"{type(self).__name__}(data={self.data})"
[docs] class EventBus: """Representation of an eventbus. Parameters ---------- center : Center instance The Center instance. """ def __init__(self, center: Center) -> None: """Set up instance.""" self._center = center self._registry: dict[str, list[EventHandler]] = {} @property def event_types(self) -> list[str]: """:list: Return all registered event types.""" return list(self._registry.keys()) def _register_handler(self, event_type: str, handler: EventHandler) -> None: """Register handler to fire for events of type event_class.""" handlers = self._registry.setdefault(event_type, []) handlers.append(handler)
[docs] def register(self, event_type: str, handler: EventHandler) -> Callable[[], None]: """Register event handler and return a function to remove it. An event can be a message from the microscope API or an internal event. Parameters ---------- event_type : str A string representing the type of event. handler : callable A coroutine function that should accept two parameters, center and event. The first parameter is the Center instance, the second parameter is the Event instance that has fired. Returns ------- callable Return a function to remove the registered handler. """ _LOGGER.debug("Registering event handler for event type %s", event_type) self._register_handler(event_type, handler) def remove() -> None: """Remove registered event handler.""" handlers = self._registry[event_type] try: handlers.remove(handler) except ValueError: _LOGGER.warning("Handler %s already removed from bus", handler) return remove
[docs] async def notify(self, event: Event) -> None: """Notify handlers that an event has fired. Parameters ---------- event : Event instance An instance of Event or an instance of subclass of Event. """ _LOGGER.debug("Notifying event %s", event) # Inspired by https://goo.gl/VEPG3n registry = self._registry for event_class in event.__class__.__mro__: # Handle base objects for Python 3. if event_class.__name__ == "object": continue event_type = getattr(event_class, "event_type", None) if event_type is None: continue for handler in registry.get(event_type, []): await handler(self._center, event) # await in sequential order
[docs] def match_event(event: Event, **event_data: Any) -> bool: """Return True if event attributes match event_data.""" if not event_data or all( val == getattr(event, key, None) for key, val in event_data.items() ): return True return False