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={}, service_order_index=0)[source]

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 instantiated. 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 Services 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.

service_order_index

Return the index of this service in the toposort of summoned services. This is primarily used to order filter chain registrations consistently with the dependency relationship of the services.

New in version 0.11.

derive_logger(logger)[source]

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.

async shutdown()[source]

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.service.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.service.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, *, with_send_reply=False)[source]

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

  • with_send_reply (bool) – Whether to pass a function to send a reply to the decorated callable as second argument.

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_, payload_cls and with_send_reply 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

New in version 0.11: The with_send_reply argument.

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_)[source]

Deprecated alias of dispatcher.message_handler().

Deprecated since version 0.9.

@aioxmpp.service.presence_handler(type_, from_)[source]

Deprecated alias of dispatcher.presence_handler().

Deprecated since version 0.9.

@aioxmpp.service.inbound_message_filter[source]

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[source]

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[source]

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[source]

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)[source]

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)[source]

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

Parameters

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)[source]

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, *, with_send_reply=False)[source]

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

aioxmpp.service.is_message_handler(type_, from_, cb)[source]

Deprecated alias of dispatcher.is_message_handler().

Deprecated since version 0.9.

aioxmpp.service.is_presence_handler(type_, from_, cb)[source]

Deprecated alias of dispatcher.is_presence_handler().

Deprecated since version 0.9.

aioxmpp.service.is_inbound_message_filter(cb)[source]

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

aioxmpp.service.is_inbound_presence_filter(cb)[source]

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

aioxmpp.service.is_outbound_message_filter(cb)[source]

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

aioxmpp.service.is_outbound_presence_filter(cb)[source]

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

aioxmpp.service.is_depsignal_handler(class_, signal_name, cb, *, defer=False)[source]

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_)[source]

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)[source]

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=[])[source]

Specification of the effects of the decorator at initialisation 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.

There is a property to extract the function directly:

func

The factory of the context manager for this handler.

New in version 0.11.

aioxmpp.service.add_handler_spec(f, handler_spec, *, kwargs=None)[source]

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.

  • kwargs (dict) – additional keyword arguments passed to the function carried in the handler spec.

Raises

ValueError – if the handler was registered with different kwargs before

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

New in version 0.11: The kwargs argument. If two handlers with the same spec, but different arguments are registered for one function, an error will be raised. So you should always include all possible arguments, this is the responsibility of the calling decorator.

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)[source]

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:

abstract init_cm(instance)[source]

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)[source]

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[source]

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 explicitly 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 instantiated.

Thus, any service which occurs in ORDER_BEFORE will be instantiated 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 instantiated before the class which is being declraed.

Classes which are declared in this attribute are always instantiated 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.

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

The following attribute was generated in earlier version of aioxmpp:

_DEPGRAPH_NODE

For compatibility with earlier versions, a warning is issued when _DEPGRAPH_NODE is defined in a service class definition.

This behaviour will be removed in aioxmpp 1.0.

Deprecated since version 0.11.

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 instantiated before Bar and Bar will be instantiated before Foo. There is no dependency relationship between Baz and Fourth.