# `service` — Utilities for implementing `Client` services

Protocol extensions or in general support for parts of the XMPP protocol are implemented using `Service` classes, or rather, classes which use the `Meta` metaclass.

Both of these are provided in this module. To reduce the boilerplate required to develop services, decorators are provided which can be used to easily register coroutines and functions as stanza handlers, filters and others.

class `aioxmpp.service.``Service`(client, *, logger_base=None, dependencies={})

  A `Service` is used to implement XMPP or XEP protocol parts, on top of the more or less fixed stanza handling implemented in `aioxmpp.node` and `aioxmpp.stream`.

  `Service` is a base class which can be used by extension developers to implement support for custom or standardized protocol extensions. Some of the features for which `aioxmpp` has support are also implemented using `Service` subclasses.

  client must be a `Client` to which the service will be attached. The client cannot be changed later, for the sake of simplicity.

  logger_base may be a `logging.Logger` instance or `None`. If it is `None`, a logger is automatically created, by taking the fully qualified name of the `Service` subclass which is being instanciated. Otherwise, the logger is passed to `derive_logger()` and the result is used as value for the `logger` attribute.

  To implement your own service, derive from `Service`. If your service depends on other services (such as `aioxmpp.pubsub` or `aioxmpp.disco`), these dependencies must be declared as documented in the service meta class `Meta`.

  To stay forward compatible, accept arbitrary keyword arguments and pass them down to `Service`. As it is not possible to directly pass arguments to `Service`s on construction (due to the way `aioxmpp.Client.summon()` works), there is no need for you to introduce custom arguments, and thus there should be no conflicts.

  Note

  Inheritance from classes which subclass `Service` is forbidden.

  Changed in version 0.9.

  `client`

    The client to which the `Service` is bound. This attribute is read-only.

    If the service has been shut down using `shutdown()`, this reads as `None`.

  `dependencies`

    When the service is instantiated through `summon()`, this attribute holds a mapping which maps the service classes contained in the `ORDER_AFTER` attribute to the respective instances related to the `client`.

    This is the preferred way to obtain dependencies specified via `ORDER_AFTER`.

  `derive_logger`(logger)

    Return a child of logger specific for this instance. This is called after `client` has been set, from the constructor.

    The child name is calculated by the default implementation in a way specific for aioxmpp services; it is not meant to be used by non-`aioxmpp` classes; do not rely on the way how the child name is calculated.

  coroutine `shutdown`()

    Close the service and wait for it to completely shut down.

    Some services which are still running may depend on this service. In that case, the service may refuse to shut down instead of shutting down, by raising a `RuntimeError` exception.

    Note

    Developers creating subclasses of `Service` to implement services should not override this method. Instead, they should override the `_shutdown()` method.

## Decorators and Descriptors

These decorators provide special functionality when used on methods of `Service` subclasses.

Note

These decorators work only on methods declared on `Service` subclasses, as their functionality are implemented in cooperation with the `Meta` metaclass and `Service` itself.

Note

> These decorators and the descriptors (see below) are initialised in the order in which they are declared at the class. In many cases, this does not matter, but there are some corner cases.

> For example: Suppose you have a class like this:

>     class FooService(aioxmpp.service.Service):
>         feature = aioxmpp.disco.register_feature(
>             "some:namespace"
>         )

>         @aioxmpp.servie.depsignal(aioxmpp.DiscoServer, "on_info_changed")
>         def handle_on_info_changed(self):
>             pass

> In this case, the `handle_on_info_changed` method is not invoked during startup of the `FooService`. In this case however:

>     class FooService(aioxmpp.service.Service):
>         @aioxmpp.servie.depsignal(aioxmpp.DiscoServer, "on_info_changed")
>         def handle_on_info_changed(self):
>             pass

>         feature = aioxmpp.disco.register_feature(
>             "some:namespace"
>         )

> The `handle_on_info_changed` is invoked during startup of the `FooService` because the `some:namespace` feature is registered after the signal is connected.

> Changed in version 0.9: This behaviour was introduced in version 0.9.

When using a descriptor and a `depsignal()` connected to `DiscoServer.on_info_changed()`: if the `disco.register_feature` is declared before the `depsignal()`, the signal handler will not be invoked for that specific feature because it is registered before the signal handler is connected).

`@``aioxmpp.service.``iq_handler`(type_, payload_cls)

  Register the decorated function or coroutine function as IQ request handler.

  Parameters:

  - type (`IQType`) – IQ type to listen for
  - payload_cls (`XSO` subclass) – Payload XSO class to listen for

  Raises: | ValueError – if payload_cls is not a registered IQ payload

  If the decorated function is not a coroutine function, it must return an awaitable instead.

  See also

  `register_iq_request_handler()`
    for more details on the type_ and payload_cls arguments, as well as behaviour expected from the decorated function.
  `aioxmpp.IQ.as_payload_class()`
    for a way to register a XSO as IQ payload

  Changed in version 0.10: The decorator now checks if payload_cls is a valid, registered IQ payload and raises `ValueError` if not.

`@``aioxmpp.service.``message_handler`(type_, from_)

  Deprecated alias of `dispatcher.message_handler()`.

  Deprecated since version 0.9.

`@``aioxmpp.service.``presence_handler`(type_, from_)

  Deprecated alias of `dispatcher.presence_handler()`.

  Deprecated since version 0.9.

`@``aioxmpp.service.``inbound_message_filter`

  Register the decorated function as a service-level inbound message filter.

  Raises: | TypeError – if the decorated object is a coroutine function

  See also

  `StanzaStream`
    for important remarks regarding the use of stanza filters.

`@``aioxmpp.service.``inbound_presence_filter`

  Register the decorated function as a service-level inbound presence filter.

  Raises: | TypeError – if the decorated object is a coroutine function

  See also

  `StanzaStream`
    for important remarks regarding the use of stanza filters.

`@``aioxmpp.service.``outbound_message_filter`

  Register the decorated function as a service-level outbound message filter.

  Raises: | TypeError – if the decorated object is a coroutine function

  See also

  `StanzaStream`
    for important remarks regarding the use of stanza filters.

`@``aioxmpp.service.``outbound_presence_filter`

  Register the decorated function as a service-level outbound presence filter.

  Raises: | TypeError – if the decorated object is a coroutine function

  See also

  `StanzaStream`
    for important remarks regarding the use of stanza filters.

`@``aioxmpp.service.``depsignal`(class_, signal_name, *, defer=False)

  Connect the decorated method or coroutine method to the addressed signal on a class on which the service depends.

  Parameters:

  - class (`Service` class or one of the special cases below) – A service class which is listed in the `ORDER_AFTER` relationship.
  - signal_name (`str`) – Attribute name of the signal to connect to
  - defer (`bool`) – Flag indicating whether deferred execution of the decorated method is desired; see below for details.

  The signal is discovered by accessing the attribute with the name signal_name on the given class_. In addition, the following arguments are supported for class_:

  1. `aioxmpp.stream.StanzaStream`: the corresponding signal of the stream of the client running the service is used.
  2. `aioxmpp.Client`: the corresponding signal of the client running the service is used.

  If the signal is a `callbacks.Signal` and defer is false, the decorated object is connected using the default `STRONG` mode.

  If the signal is a `callbacks.Signal` and defer is true and the decorated object is a coroutine function, the `SPAWN_WITH_LOOP` mode with the default asyncio event loop is used. If the decorated object is not a coroutine function, `ASYNC_WITH_LOOP` is used instead.

  If the signal is a `callbacks.SyncSignal`, defer must be false and the decorated object must be a coroutine function.

  Changed in version 0.9: Support for `aioxmpp.stream.StanzaStream` and `aioxmpp.Client` as class_ argument was added.

`@``aioxmpp.service.``depfilter`(class_, filter_name)

  Register the decorated method at the addressed `Filter` on a class on which the service depends.

  Parameters:

  - class (`Service` class or `aioxmpp.stream.StanzaStream`) – A service class which is listed in the `ORDER_AFTER` relationship.
  - filter_name (`str`) – Attribute name of the filter to register at

  The filter at which the decorated method is registered is discovered by accessing the attribute with the name filter_name on the instance of the dependent class class_. If class_ is `aioxmpp.stream.StanzaStream`, the filter is searched for on the stream (and no dependendency needs to be declared).

  New in version 0.9.

`@``aioxmpp.service.``attrsignal`(descriptor, signal_name, *, defer=False)

  Connect the decorated method or coroutine method to the addressed signal on a descriptor.

  Parameters:

  - descriptor (`Descriptor` subclass.) – The descriptor to connect to.
  - signal_name (`str`) – Attribute name of the signal to connect to
  - defer (`bool`) – Flag indicating whether deferred execution of the decorated method is desired; see below for details.

  The signal is discovered by accessing the attribute with the name signal_name on the `value_type` of the descriptor.

  During instantiation of the service, the value of the descriptor is used to obtain the signal and then the decorated method is connected to the signal.

  If the signal is a `callbacks.Signal` and defer is false, the decorated object is connected using the default `STRONG` mode.

  If the signal is a `callbacks.Signal` and defer is true and the decorated object is a coroutine function, the `SPAWN_WITH_LOOP` mode with the default asyncio event loop is used. If the decorated object is not a coroutine function, `ASYNC_WITH_LOOP` is used instead.

  If the signal is a `callbacks.SyncSignal`, defer must be false and the decorated object must be a coroutine function.

  New in version 0.9.

See also

`register_feature`
  For a descriptor (see below) which allows to register a Service Discovery feature when the service is instantiated.
`mount_as_node`
  For a descriptor (see below) which allows to register a Service Discovery node when the service is instantiated.
`register_pep_node`
  For a descriptor (see below) which allows to register a PEP node including notification features.

### Test functions

`aioxmpp.service.``is_iq_handler`(type_, payload_cls, coro)

  Return true if coro has been decorated with `iq_handler()` for the given type_ and payload_cls.

`aioxmpp.service.``is_message_handler`(type_, from_, cb)

  Deprecated alias of `dispatcher.is_message_handler()`.

  Deprecated since version 0.9.

`aioxmpp.service.``is_presence_handler`(type_, from_, cb)

  Deprecated alias of `dispatcher.is_presence_handler()`.

  Deprecated since version 0.9.

`aioxmpp.service.``is_inbound_message_filter`(cb)

  Return true if cb has been decorated with `inbound_message_filter()`.

`aioxmpp.service.``is_inbound_presence_filter`(cb)

  Return true if cb has been decorated with `inbound_presence_filter()`.

`aioxmpp.service.``is_outbound_message_filter`(cb)

  Return true if cb has been decorated with `outbound_message_filter()`.

`aioxmpp.service.``is_outbound_presence_filter`(cb)

  Return true if cb has been decorated with `outbound_presence_filter()`.

`aioxmpp.service.``is_depsignal_handler`(class_, signal_name, cb, *, defer=False)

  Return true if cb has been decorated with `depsignal()` for the given signal, class and connection mode.

`aioxmpp.service.``is_depfilter_handler`(class_, filter_name, filter_)

  Return true if filter_ has been decorated with `depfilter()` for the given filter and class.

`aioxmpp.service.``is_attrsignal_handler`(descriptor, signal_name, cb, *, defer=False)

  Return true if cb has been decorated with `attrsignal()` for the given signal, descriptor and connection mode.

### Creating your own decorators

Sometimes, when you create your own service, it makes sense to create own decorators which depending services can use to make easy use of some features of your service.

Note

Remember that it isn’t necessary to create custom decorators to simply connect a method to a signal exposed by another service. Users of that service should be using `depsignal()` instead.

The key part is the `HandlerSpec` object. It specifies the effect the decorator has on initialisation and shutdown of the service. To add a `HandlerSpec` to a decorated method, use `add_handler_spec()` in the implementation of your decorator.

class `aioxmpp.service.``HandlerSpec`(key, is_unique=True, require_deps=[])

  Specification of the effects of the decorator at initalisation and shutdown time.

  Parameters:

  - key (pair) – Context manager and arguments pair.
  - is_unique (`bool`) – Whether multiple identical key values are allowed on a single class.
  - require_deps (iterable of `Service` classes) – Dependent services which are required for the decorator to work.

  During initialisation of the `Service` which has a method using a given handler spec, the first part of the key pair is called with the service instance as first, the client `StanzaStream` as second and the bound method as third argument. The second part of the key is unpacked as additional positional arguments.

  The result of the call must be a context manager, which is immediately entered. On shutdown, the context manager is exited.

  An example use would be the following handler spec:

      HandlerSpec(
          (func, (IQType.GET, some_payload_class)),
          is_unique=True,
      )

  where `func` is a context manager which takes a service instance, a stanza stream, a bound method as well as an IQ type and a payload class. On enter, the context manager would register the method it received as third argument on the stanza stream (second argument) as handler for the given IQ type and payload class (fourth and fifth arguments).

  If is_unique is true and several methods have `HandlerSpec` objects with the same key, `TypeError` is raised at class definition time.

  If at class definition time any of the dependent classes in require_deps are not declared using the order attributes (see `Meta`), a `TypeError` is raised.

`aioxmpp.service.``add_handler_spec`(f, handler_spec)

  Attach a handler specification (see `HandlerSpec`) to a function.

  Parameters:

  - f – Function to attach the handler specification to.
  - handler_spec (`HandlerSpec`) – Handler specification to attach to the function.

  This uses a private attribute, whose exact name is an implementation detail. The handler_spec is stored in a `set` bound to the attribute.

### Creating your own descriptors

Sometimes a decorator is not the right tool for the job, because with what you attempt to achieve, there’s simply no relationship to a method.

In this case, subclassing `Descriptor` is the way to go. It provides an abstract base class implementing a descriptor. Using a `Descriptor` subclass, you can create objects for each individual service instance using the descriptor, including cleanup.

class `aioxmpp.service.``Descriptor`(*args, **kwargs)

  Abstract base class for resource managing descriptors on `Service` classes.

  While resources such as callback slots can easily be managed with decorators (see above), because they are inherently related to the method they use, others cannot. A `Descriptor` provides a method to initialise a context manager. The context manager is entered when the service is initialised and left when the service is shut down, thus providing a way for the `Descriptor` to manage the resource associated with it.

  The result from entering the context manager is accessible by reading the attribute the descriptor is bound to.

  Subclasses must implement the following:

  `init_cm`(instance)

    Create and return a context manager.

    Parameters: | instance – The service instance for which the CM is used.
    Returns: | A context manager managing the resource.

    The context manager is responsible for acquiring, initialising, destructing and releasing the resource managed by this descriptor.

    The returned context manager is not stored anywhere in the descriptor, it is the responsibility of the caller to register it appropriately.

  `value_type`

    The type of the value of the descriptor, once it is being accessed as an object attribute.

    New in version 0.9.

  Subclasses may override the following to modify the default behaviour:

  `required_dependencies`

    Iterable of services which must be declared as dependencies on a class using this descriptor.

    The default implementation returns an empty list.

  `add_to_stack`(instance, stack)

    Get the context manager for the service instance and push it to the context manager stack.

    Parameters:

    - instance (`Service`) – The service to get the context manager for.
    - stack (`contextlib.ExitStack`) – The context manager stack to push the CM onto.

    Returns: | The object returned by the context manager on enter.

    If a context manager has already been created for instance, it is re-used.

    On subsequent calls to `__get__()` for the given instance, the return value of this method will be returned, that is, the value obtained from entering the context.

## Metaclass

class `aioxmpp.service.``Meta`

  The metaclass for services. The `Service` class uses it and in general you should just inherit from `Service` and define the dependency attributes as needed.

  Only use `Meta` explicitely if you know what you are doing, and you most likely do not. `Meta` is internal API and may change at any point.

  Services have dependencies. A `Meta` instance (i.e. a service class) can declare dependencies using the following attributes.

  `ORDER_BEFORE`

    An iterable of `Service` classes before which the class which is currently being declared needs to be instanciated.

    Thus, any service which occurs in `ORDER_BEFORE` will be instanciated after this class (if at all). Think of it as “this class is ordered before the classes in this attribute”.

    New in version 0.3.

  `SERVICE_BEFORE`

    Before 0.3, this was the name of the `ORDER_BEFORE` attribute. It is still supported, but use emits a `DeprecationWarning`. It must not be mixed with `ORDER_BEFORE` or `ORDER_AFTER` on a class declaration, or the declaration will raise `ValueError`.

    Deprecated since version 0.3: Support for this attribute will be removed in 1.0; starting with 1.0, using this attribute will raise a `TypeError` on class declaration and a `AttributeError` when accessing it on a class or instance.

  `ORDER_AFTER`

    An iterable of `Service` classes which will be instanciated before the class which is being declraed.

    Classes which are declared in this attribute are always instanciated before this class is instantiated. Think of it as “this class is ordered after the classes in this attribute”.

    New in version 0.3.

  `SERVICE_AFTER`

    Before 0.3, this was the name of the `ORDER_AFTER` attribute. It is still supported, but use emits a `DeprecationWarning`. It must not be mixed with `ORDER_BEFORE` or `ORDER_AFTER` on a class declaration, or the declaration will raise `ValueError`.

    Deprecated since version 0.3: See `SERVICE_BEFORE` for details on the deprecation cycle.

  Further, the following attributes are generated:

  `PATCHED_ORDER_AFTER`

    An iterable of `Service` classes. This includes all classes in `ORDER_AFTER` and all classes which specify the class in `ORDER_BEFORE`.

    This is primarily used internally to handle `ORDER_BEFORE` when summoning services.

    It is an error to manually define `PATCHED_ORDER_AFTER` in a class definition, doing so will raise a `TypeError`.

    New in version 0.9.

  `_DEPGRAPH_NODE`

    An internal token used for topological ordering. Consider this name reserved by the metaclass.

    It is an error to manually define `_DEPGRAPH_NODE` in a class definition, doing so will raise a `TypeError`.

    New in version 0.9.

  Changed in version 0.9: The `ORDER_AFTER` and `ORDER_BEFORE` attribtes do not change after class creation. In earlier versions they contained the transitive completion of the dependency relation.

  Dependency relationships must not have cycles; a cycle results in a `ValueError` when the class causing the cycle is declared.

  Note

  Subclassing instances of `Meta` is forbidden. Trying to do so will raise a `TypeError`

  Changed in version 0.9.

  Example:

      class Foo(metaclass=service.Meta):
          pass

      class Bar(metaclass=service.Meta):
          ORDER_BEFORE = [Foo]

      class Baz(metaclass=service.Meta):
          ORDER_BEFORE = [Bar]

      class Fourth(metaclass=service.Meta):
          ORDER_BEFORE = [Bar]

  `Baz` and `Fourth` will be instanciated before `Bar` and `Bar` will be instanciated before `Foo`. There is no dependency relationship between `Baz` and `Fourth`.