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={})[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 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 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.

client[source]

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

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

This is the preferred way to obtain dependencies specified via ORDER_BEFORE.

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.

coroutine 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

Inheritance from classes which have any of these decorators on any of its methods is forbidden currently, because of the ambiguities which arise.

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.

@aioxmpp.service.iq_handler(type_, payload_cls)[source]

Register the decorated 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 TypeError:
 

if the decorated object is not a coroutine function

See also

register_iq_request_coro()
for more details on the type_ and payload_cls arguments
@aioxmpp.service.message_handler(type_, from_)[source]

Register the decorated function as message handler.

Parameters:
Raises TypeError:
 

if the decorated object is a coroutine function

See also

register_message_callback()
for more details on the type_ and from_ arguments
@aioxmpp.service.presence_handler(type_, from_)[source]

Register the decorated function as presence stanza handler.

Parameters:
Raises TypeError:
 

if the decorated object is a coroutine function

See also

register_presence_callback()
for more details on the type_ and from_ arguments
@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) – A service class which is listed in the ORDERED_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_.

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.

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.

Test functions

aioxmpp.service.is_iq_handler(type_, payload_cls, coro)[source]

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

Return true if cb has been decorated with message_handler() for the given type_ and from_.

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

Return true if cb has been decorated with presence_handler() for the given type_ and from_.

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.

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 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)[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.

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)[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:

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.

Subclasses may override the following to modify the default behaviour:

required_dependencies[source]

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([inherit_dependencies=True])[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.

Services have dependencies. A Meta instance (i.e. a service class) can declare dependencies using the following two 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.

The dependencies are inherited from bases unless the inherit_dependencies keyword argument is set to false.

After a class has been instanciated, the full set of dependencies is provided in the attributes, including all transitive relationships. These attributes are updated when new classes are declared.

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

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.

Inheritance works too:

class Foo(metaclass=service.Meta):
    pass

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

class Baz(Bar):
    # has ORDER_BEFORE == {Foo}
    pass

class Fourth(Bar, inherit_dependencies=False):
    # has empty ORDER_BEFORE
    pass