# `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.

## Signal overview

`Signal`(*[, doc]) | A descriptor which returns per-instance `AdHocSignal` objects on attribute access.
`SyncSignal`(*[, doc]) | 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.

### Utilities

aioxmpp.callbacks.first_signal(*signals)

  Connect to multiple signals and wait for the first to emit.

  Parameters

    signals (`AdHocSignal`) – Signals to connect to.

  Returns

    An awaitable for the first signal to emit.

  The awaitable returns the first argument passed to the signal. If the first argument is an exception, the exception is re-raised from the awaitable.

  A common use-case is a situation where a class exposes a “on_finished” type signal and an “on_failure” type signal. `first_signal()` can be used to combine those nicely:

      # e.g. a aioxmpp.im.conversation.AbstractConversation
      conversation = ...
      await first_signal(
          # emits without arguments when the conversation is successfully
          # entered
          conversation.on_enter,
          # emits with an exception when entering the conversation fails
          conversation.on_failure,
      )
      # await first_signal(...) will either raise an exception (failed) or
      # return None (success)

  Warning

  Only works with signals which emit with zero or one argument. Signals which emit with more than one argument or with keyword arguments are silently ignored! (Thus, if only such signals are connected, the future will never complete.)

  (This is a side-effect of the implementation of `AdHocSignal.AUTO_FUTURE()`).

  Note

  Does not work with coroutine signals (`SyncAdHocSignal`).

### Signal descriptors

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

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

  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)

  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

### 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

  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)

    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)

    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.

  context_connect(f, mode=None)

    This returns a context manager. When entering the context, f is connected to the `AdHocSignal` using mode. 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`.

  future()

    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

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

  WEAK

    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)

    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

    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 positional 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)

    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.ensure_future()` 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.

    If the spawned coroutine returns with an exception or a non-`None` return value, a message is logged, with the following log levels:

    - Return with non-`None` value: `logging.INFO`

    - Raises `asyncio.CancelledError`: `logging.DEBUG`

    - Raises any other exception: `logging.WARNING`

    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

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

  connect(coro)

    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)

    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`.

  async fire(*args, **kwargs)

    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.

## Filters

class aioxmpp.callbacks.Filter

  A filter chain for arbitrary data.

  This is used for example in `StanzaStream` to allow services and applications to filter inbound and outbound stanzas.

  Each function registered with the filter receives at least one argument. This argument is the object which is to be filtered. The function must return the object, a replacement or `None`. If `None` is returned, the filter chain aborts and further functions are not called. Otherwise, the next function is called with the result of the previous function until the filter chain is complete.

  Other arguments passed to `filter()` are passed unmodified to each function called; only the first argument is subject to filtering.

  Changed in version 0.9: This class was formerly available at `aioxmpp.stream.Filter`.

  register(func, order)

    Add a function to the filter chain.

    Parameters

      - func – A callable which is to be added to the filter chain.

      - order – An object indicating the ordering of the function relative to the others.

    Returns

      Token representing the registration.

    Register the function func as a filter into the chain. order must be a value which is used as a sorting key to order the functions registered in the chain.

    The type of order depends on the use of the filter, as does the number of arguments and keyword arguments which func must accept. This will generally be documented at the place where the `Filter` is used.

    Functions with the same order are sorted in the order of their addition, with the function which was added earliest first.

    Remember that all values passed to order which are registered at the same time in the same `Filter` need to be totally orderable with respect to each other.

    The returned token can be used to `unregister()` a filter.

  filter(obj, *args, **kwargs)

    Filter the given object through the filter chain.

    Parameters

      - obj – The object to filter

      - args – Additional arguments to pass to each filter function.

      - kwargs – Additional keyword arguments to pass to each filter function.

    Returns

      The filtered object or `None`

    See the documentation of `Filter` on how filtering operates.

    Returns the object returned by the last function in the filter chain or `None` if any function returned `None`.

  unregister(token_to_remove)

    Unregister a filter function.

    Parameters

      token_to_remove – The token as returned by `register()`.

    Unregister a function from the filter chain using the token returned by `register()`.

  context_register(func[, order])

    Context manager which temporarily registers a filter function.

    Parameters

      - func – The filter function to register.

      - order – The sorting key for the filter function.

    Return type

      context manager

    Returns

      Context manager which temporarily registers the filter function.

    If `register()` does not require order because it has been overridden in a subclass, the order argument can be omitted here, too.

    New in version 0.9.