Hide navigation sidebar
Hide table of contents sidebar
Skip to content
Back to top
Toggle table of contents sidebar

# Source code for betty.event_dispatcher

    """
    Provide the Dispatch API.
    """

    from __future__ import annotations

    from collections import defaultdict
    from typing import (
        Sequence,
        Callable,
        Awaitable,
        TypeVar,
        TypeAlias,
        TYPE_CHECKING,
        final,
    )

    from betty.asyncio import gather

    if TYPE_CHECKING:
        from collections.abc import MutableMapping, MutableSequence, Mapping

    class Event:
        """
        An event that can be dispatched and handled.

        For each unique 'conceptual' event (a thing that happens while using Betty),
        a subclass **MUST** be created for that specific event type. Instances of
        these subclasses are dispatched and handled.
        """

        pass

    _EventT = TypeVar("_EventT", bound=Event)
    EventHandler: TypeAlias = Callable[[_EventT], Awaitable[None]]

    class _EventHandlerRegistry:
        """
        Manage event handlers.
        """

        def __init__(self):
            self._handlers: MutableMapping[
                type[Event], MutableSequence[Sequence[EventHandler[Event]]]
            ] = defaultdict(list)

        def add_handler(self, event_type: type[_EventT], *handlers: EventHandler[_EventT]):
            """
            Add a batch of one or more event handlers.

            All handlers of a batch are invoked concurrently.
            """
            self._handlers[event_type].append(
                handlers  # type: ignore[arg-type]
            )

        def add_registry(self, event_handler_registry: EventHandlerRegistry) -> None:
            """
            Add another registry to this one.
            """
            for (
                event_type,
                event_type_handler_batches,
            ) in event_handler_registry.handlers.items():
                for event_type_handler_batch in event_type_handler_batches:
                    self.add_handler(event_type, *event_type_handler_batch)

    @final
    class EventHandlerRegistry(_EventHandlerRegistry):
        """
        Manage event handlers.
        """

        @property
        def handlers(
            self,
        ) -> Mapping[type[Event], Sequence[Sequence[EventHandler[Event]]]]:
            """
            The registered event handlers.
            """
            return self._handlers

    @final
    class EventDispatcher(_EventHandlerRegistry):
        """
        Dispatch events to event handlers.
        """

        async def dispatch(self, event: Event) -> None:
            """
            Dispatch an event.
            """
            for handler_batch in self._handlers[type(event)]:
                await gather(
                    *(  # type: ignore[arg-type]
                        handler(event) for handler in handler_batch
                    )
                )