callbacks – Synchronous and asynchronous callbacks

This module provides facilities for objects to provide signals to which other objects can connect.

Descriptor vs. ad-hoc

Descriptors can be used as class attributes and will create ad-hoc signals dynamically for each instance. They are the most commonly used:

class Emitter:
    on_event = callbacks.Signal()

def handler():
    pass

emitter1 = Emitter()
emitter2 = Emitter()
emitter1.on_event.connect(handler)

emitter1.on_event()  # calls `handler`
emitter2.on_event()  # does not call `handler`

# the actual signals are distinct
assert emitter1.on_event is not emitter2.on_event

Ad-hoc signals are useful for testing and are the type of which the actual fields are.

Class overview

Signal A descriptor which returns per-instance AdHocSignal objects on attribute access.
SyncSignal A descriptor which returns per-instance SyncAdHocSignal objects on attribute access.
AdHocSignal() An ad-hoc signal is a single emitter.
SyncAdHocSignal() A synchronous ad-hoc signal is like AdHocSignal, but for coroutines instead of ordinary callables.

Signal implementations (ad-hoc signals)

Whenever accessing an attribute using the Signal or SyncSignal descriptors, an object of one of the following classes is returned. This is where the behaviour of the signals is specified.

class aioxmpp.callbacks.AdHocSignal[source]

An ad-hoc signal is a single emitter. This is where callables are connected to, using the connect() method of the AdHocSignal.

fire(*args, **kwargs)[source]

Emit the signal, calling all connected objects in-line with the given arguments and in the order they were registered.

AdHocSignal provides full isolation with respect to exceptions. If a connected listener raises an exception, the other listeners are executed as normal, but the raising listener is removed from the signal. The exception is logged to logger and not re-raised, so that the caller of the signal is also not affected.

Instead of calling fire() explicitly, the ad-hoc signal object itself can be called, too.

connect(f, mode=None)[source]

Connect an object f to the signal. The type the object needs to have depends on mode, but usually it needs to be a callable.

connect() returns an opaque token which can be used with disconnect() to disconnect the object from the signal.

The default value for mode is STRONG. Any decorator can be used as argument for mode and it is applied to f. The result is stored internally and is what will be called when the signal is being emitted.

If the result of mode returns a false value during emission, the connection is removed.

Note

The return values required by the callable returned by mode and the one required by a callable passed to f using the predefined modes are complementary!

A callable f needs to return true to be removed from the connections, while a callable returned by the mode decorator needs to return false.

Existing modes are listed below.

future()[source]

Return a asyncio.Future which has been connect()-ed using AUTO_FUTURE.

The token returned by connect() is not returned; to remove the future from the signal, just cancel it.

logger

This may be a logging.Logger instance to allow the signal to log errors and debug events to a specific logger instead of the default logger (aioxmpp.callbacks).

This attribute must not be None, and it is initialised to the default logger on creation of the AdHocSignal.

The different ways callables can be connected to an ad-hoc signal are shown below:

STRONG[source]

Connections using this mode keep a strong reference to the callable. The callable is called directly, thus blocking the emission of the signal.

WEAK[source]

Connections using this mode keep a weak reference to the callable. The callable is executed directly, thus blocking the emission of the signal.

If the weak reference is dead, it is automatically removed from the signals connection list. If the callable is a bound method, weakref.WeakMethod is used automatically.

For both STRONG and WEAK holds: if the callable returns a true value, it is disconnected from the signal.

classmethod ASYNC_WITH_LOOP(loop)[source]

This mode requires an asyncio event loop as argument. When the signal is emitted, the callable is not called directly. Instead, it is enqueued for calling with the event loop using asyncio.BaseEventLoop.call_soon(). If None is passed as loop, the loop is obtained from asyncio.get_event_loop() at connect time.

A strong reference is held to the callable.

Connections using this mode are never removed automatically from the signals connection list. You have to use disconnect() explicitly.

AUTO_FUTURE[source]

Instead of a callable, a asyncio.Future must be passed when using this mode.

This mode can only be used for signals which send at most one argument. If no argument is sent, the set_result() method is called with None.

If one argument is sent and it is an instance of Exception, it is passed to set_exception(). Otherwise, if one argument is sent, it is passed to set_exception().

In any case, the future is removed after the next emission of the signal.

classmethod SPAWN_WITH_LOOP(loop)[source]

This mode requires an asyncio event loop as argument and a coroutine to be passed to connect(). If None is passed as loop, the loop is obtained from asyncio.get_event_loop() at connect time.

When the signal is emitted, the coroutine is spawned using asyncio.async() in the given loop, with the arguments passed to the signal.

A strong reference is held to the coroutine.

Connections using this mode are never removed automatically from the signals connection list. You have to use disconnect() explicitly.

New in version 0.6.

disconnect(token)

Disconnect the connection identified by token. This never raises, even if an invalid token is passed.

class aioxmpp.callbacks.SyncAdHocSignal[source]

A synchronous ad-hoc signal is like AdHocSignal, but for coroutines instead of ordinary callables.

connect(coro)[source]

The coroutine coro is connected to the signal. The coroutine must return a true value, unless it wants to be disconnected from the signal.

Note

This is different from the return value convention with AdHocSignal.STRONG and AdHocSignal.WEAK.

connect() returns a token which can be used with disconnect() to disconnect the coroutine.

context_connect(coro)[source]

This returns a context manager. When entering the context, coro is connected to the SyncAdHocSignal. When leaving the context (no matter whether with or without exception), the connection is disconnected.

See also

The returned object is an instance of SignalConnectionContext.

coroutine fire(*args, **kwargs)[source]

Emit the signal, calling all coroutines in-line with the given arguments and in the order they were registered.

This is obviously a coroutine.

Instead of calling fire() explicitly, the ad-hoc signal object itself can be called, too.

disconnect(token)

Disconnect the connection identified by token. This never raises, even if an invalid token is passed.

Signal descriptors

These descriptors can be used on classes to have attributes which are signals:

class aioxmpp.callbacks.Signal(*, doc=None)[source]

A descriptor which returns per-instance AdHocSignal objects on attribute access.

Example use:

class Foo:
    on_event = Signal()

f = Foo()
assert isinstance(f.on_event, AdHocSignal)
assert f.on_event is f.on_event
assert Foo().on_event is not f.on_event
class aioxmpp.callbacks.SyncSignal(*, doc=None)[source]

A descriptor which returns per-instance SyncAdHocSignal objects on attribute access.

Example use:

class Foo:
    on_event = SyncSignal()

f = Foo()
assert isinstance(f.on_event, SyncAdHocSignal)
assert f.on_event is f.on_event
assert Foo().on_event is not f.on_event