Events

Events are a handy way to make your code react to changes in another part of the application. To dispatch and listen to events, you first need to have one or more Signal instances as attributes of some class. Each signal needs to be associated with some Event class. Then, when you dispatch a new event by calling dispatch(), a new instance of this event class will be constructed and passed to all listener callbacks.

To listen to events dispatched from a signal, you need to have a function or any other callable that accepts a single positional argument. You then pass this callable to connect(). That’s it!

To disconnect the callback, simply call disconnect() with whatever you passed to connect() as argument.

Here’s how it works:

from asphalt.core import Event, Signal


class CustomEvent(Event):
    def __init__(source, topic, extra_argument):
        super().__init__(source, topic)
        self.extra_argument = extra_argument


class MyEventSource:
    somesignal = Signal(Event)
    customsignal = Signal(CustomEvent)


def plain_listener(event):
    print('received event: %s' % event)


async def coro_listener(event):
    print('coroutine listeners are fine too: %s' % event)


async def some_handler():
    source = MyEventSource()
    source.somesignal.connect(plain_listener)
    source.customsignal.connect(coro_listener)

    # Dispatches an Event instance
    source.somesignal.dispatch()

    # Dispatches a CustomEvent instance (the extra argument is passed to its constructor)
    source.customsignal.dispatch('extra argument here')

Exception handling

By default, all exceptions raised by listener callbacks are just sent to the logger (asphalt.core.event). If the dispatcher needs to know about any exceptions raised by listeners, it can call dispatch() with return_future=True. This will cause a Future to be returned and, when awaited, will raise a EventDispatchError if any listener raised an exception. This exception will contain every exception that was raised, along with the information regarding which callback raised which exception.

Waiting for a single event

To wait for the next event dispatched from a given signal, you can use the wait_event() method:

async def print_next_event(source):
    event = await source.somesignal.wait_event()
    print(event)

You can even wait for the next event dispatched from any of several signals using the wait_event() function:

from asphalt.core import wait_event


async def print_next_event(source1, source2, source3):
    event = await wait_event(source1.some_signal, source2.another_signal, source3.some_signal)
    print(event)

Receiving events iteratively

With stream_events(), you can even asynchronously iterate over events dispatched from a signal:

async def listen_to_events(source):
    async for event in source.somesignal.stream_events():
        print(event)

Using stream_events(), you can stream events from multiple signals:

from asphalt.core import stream_events


async def listen_to_events(source1, source2, source3):
    async for event in stream_events(source1.some_signal, source2.another_signal,
                                     source3.some_signal):
        print(event)