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 inaioxmpp.node
andaioxmpp.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 whichaioxmpp
has support are also implemented usingService
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 orNone
. If it isNone
, a logger is automatically created, by taking the fully qualified name of theService
subclass which is being instantiated. Otherwise, the logger is passed toderive_logger()
and the result is used as value for thelogger
attribute.To implement your own service, derive from
Service
. If your service depends on other services (such asaioxmpp.pubsub
oraioxmpp.disco
), these dependencies must be declared as documented in the service meta classMeta
.To stay forward compatible, accept arbitrary keyword arguments and pass them down to
Service
. As it is not possible to directly pass arguments toService
s on construction (due to the wayaioxmpp.Client.summon()
works), there is no need for you to introduce custom arguments, and thus there should be no conflicts.-
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 asNone
.
-
dependencies
¶ When the service is instantiated through
summon()
, this attribute holds a mapping which maps the service classes contained in theORDER_AFTER
attribute to the respective instances related to theclient
.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): passIn this case, the
handle_on_info_changed
method is not invoked during startup of theFooService
. 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 theFooService
because thesome: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
- 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 moredetails 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 theORDER_AFTER
relationship.signal_name (
str
) – Attribute name of the signal to connect todefer (
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_:
aioxmpp.stream.StanzaStream
: the corresponding signal of the stream of the client running the service is used.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 defaultSTRONG
mode.If the signal is a
callbacks.Signal
and defer is true and the decorated object is a coroutine function, theSPAWN_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
andaioxmpp.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
class (
Service
class oraioxmpp.stream.StanzaStream
) – A service class which is listed in theORDER_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)[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 todefer (
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 defaultSTRONG
mode.If the signal is a
callbacks.Signal
and defer is true and the decorated object is a coroutine function, theSPAWN_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
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 clientStanzaStream
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
), aTypeError
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 theDescriptor
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.
-
abstract
Metaclass¶
-
class
aioxmpp.service.
Meta
[source]¶ The metaclass for services. The
Service
class uses it and in general you should just inherit fromService
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 aDeprecationWarning
. It must not be mixed withORDER_BEFORE
orORDER_AFTER
on a class declaration, or the declaration will raiseValueError
.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 aAttributeError
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 aDeprecationWarning
. It must not be mixed withORDER_BEFORE
orORDER_AFTER
on a class declaration, or the declaration will raiseValueError
.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 inORDER_AFTER
and all classes which specify the class inORDER_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 aTypeError
.New in version 0.9.
Changed in version 0.9: The
ORDER_AFTER
andORDER_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 aTypeError
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
andFourth
will be instantiated beforeBar
andBar
will be instantiated beforeFoo
. There is no dependency relationship betweenBaz
andFourth
.-