xso — Working with XML stream contents

This subpackage deals with XML Stream Objects. XSOs can be stanzas, but in general any XML.

The facilities in this subpackage are supposed to help developers of XEP plugins, as well as the main development of aioxmpp. The subpackage is split in two parts, aioxmpp.xso.model, which provides facilities to allow declarative-style parsing and un-parsing of XML subtrees into XSOs and the aioxmpp.xso.types module, which provides classes which implement validators and type parsers for content represented as strings in XML.

Introduction

See also

For a more in-depth introduction into aioxmpp.xso, please refer to the An Introduction to XSO chapter in the user guide. This document here is a reference manual.

The aioxmpp.xso subpackage provides declarative-style parsing of XML document fragments. The declarations are similar to what you might know from declarative Object-Relational-Mappers such as sqlalchemy. Due to the different data model of XML and relational databases, they are not identical of course.

An abstract class describing the common properties of an XMPP stanza might look like this:

class Stanza(xso.XSO):
    from_ = xso.Attr(tag="from", type_=xso.JID(), default=None)
    to = xso.Attr(tag="to", type_=xso.JID(), default=None)
    lang = xso.LangAttr(tag=(namespaces.xml, "lang"))

Instances of classes deriving from aioxmpp.xso.XSO are called XML stream objects, or XSOs for short. Each XSO maps to an XML element node.

The declaration of an XSO class typically has one or more descriptors describing the mapping of XML child nodes of the element. XML nodes which can be mapped include attributes, text and elements (processing instructions and comments are not supported; CDATA sections are treated like text).

XSO-specific Terminology

Definition of an XSO

An XSO is an object whose class inherits from aioxmpp.xso.XSO.

A word on tags

Tags, as used by etree, are used throughout this module. Note that we are representing tags as tuples of (namespace_uri, localname), where namespace_uri may be None.

See also

The functions normalize_tag() and tag_to_str() are useful to convert from and to ElementTree compatible strings.

XML stream events

XSOs are parsed using SAX-like events. This allows them to be built one-by-one in memory (and discarded) even while the XML stream is in progress.

The XSO module uses a subset of the original SAX event list, and it uses a custom format. The reason for that is that instead of using an interface with methods, the parsing parts are implemented using suspendable functions (see below).

Suspendable functions

This module uses suspendable functions, implemented as generators, at several points. These may also be called coroutines, but have nothing to do with coroutines as used by asyncio, which is why we will call them suspendable functions here.

Suspendable functions possibly take arguments and then operate on input which is fed to them in a push-manner step by step (using the send() method). The main usage in this module is to process XML stream events: The SAX events are processed step-by-step by the functions, and when the event is fully processed, it suspends itself (using yield) until the next event is sent into it.

General functions

aioxmpp.xso.normalize_tag(tag)[source]

Normalize an XML element tree tag into the tuple format. The following input formats are accepted:

  • ElementTree namespaced string, e.g. {uri:bar}foo

  • Unnamespaced tags, e.g. foo

  • Two-tuples consisting of namespace_uri and localpart; namespace_uri may be None if the tag is supposed to be namespaceless. Otherwise it must be, like localpart, a str.

Return a two-tuple consisting the (namespace_uri, localpart) format.

aioxmpp.xso.tag_to_str(tag)[source]

tag must be a tuple (namespace_uri, localname). Return a tag string conforming to the ElementTree specification. Example:

tag_to_str(("jabber:client", "iq")) == "{jabber:client}iq"

Object declaration with aioxmpp.xso.model

This module provides facilities to create classes which map to full XML stream subtrees (for example stanzas including payload).

To create such a class, derive from XSO and provide attributes using the Attr, Text, Child and ChildList descriptors.

Descriptors for XML-sourced attributes

Attr(tag, *[, type_, missing])

A single XML attribute.

LangAttr([default])

Special handler for the xml:lang attribute.

Text(*[, type_, erroneous_as_absent])

Character data contents of an XSO.

Child(classes[, required, strict])

A single child element of any of the given XSO types.

ChildTag(tags, *[, default_ns, text_policy, …])

Tag of a single child element with one of the given tags.

ChildFlag(tag[, text_policy, child_policy, …])

Presence of a child element with the given tag, as boolean.

ChildText(tag, *[, child_policy, …])

Character data of a single child element matching the given tag.

ChildTextMap(xso_type)

Dictionary of character data in child elements keyed by the language attribute.

ChildValue(type_)

Child element parsed using an Element Type.

ChildList(classes)

List of child elements of any of the given XSO classes.

ChildMap(classes, *[, key])

Dictionary holding child elements of one or more XSO classes.

ChildLangMap(classes, **kwargs)

Shorthand for a dictionary of child elements keyed by the language attribute.

ChildValueList(type_, *[, container_type])

List of child elements parsed using the given Element Type.

ChildValueMap(type_, *[, mapping_type])

Dictiorary of child elements parsed using the given Element Type.

ChildValueMultiMap(type_, *[, mapping_type])

Multi-dict of child elements parsed using the given Element Type.

Collector()

Catch-all descriptor collecting unhandled elements in an lxml element tree.

The following descriptors can be used to load XSO attributes from XML. There are two fundamentally different descriptor types: scalar and non-scalar (e.g. list) descriptors. Assignment to the descriptor attribute is strictly type-checked for scalar descriptors.

Scalar descriptors

Many of the arguments and attributes used for the scalar descriptors are similar. They are described in detail on the Attr class and not repeated that detailed on the other classes. Refer to the documentation of the Attr class in those cases.

class aioxmpp.xso.Attr(name, *[, type_=xso.String()][, validator=None][, validate=ValidateMode.FROM_RECV][, missing=None][, default][, erroneous_as_absent=False])[source]

A single XML attribute.

Parameters

tag (str or tuple of Namespace URI and Local Name.) – The tag identifying the attribute.

If the tag is a str, it is converted using (None, tag), thus representing an unnamespaced attribute. Note that most attributes are unnamespaced; namespaced attributes always have a namespace prefix on them. Attributes without a namespace prefix, in XML, are unnamespaced (not part of the current prefixless namespace).

Note

The following arguments occur at several of the descriptor classes, and are all available at Attr. Their semantics are identical on other classes, transferred to the respective use there.

(For example, the ChildText descriptor is obviously not working with attributes, so the phrase “if the attribute is absent” should be mentally translated to “if the child element is absent”.)

Parameters
  • type (AbstractCDataType) – A character data type to interpret the XML character data.

  • validator (AbstractValidator) – Optional validator object

  • validate (ValidateMode) – Control when the validator is enforced.

  • default – The value which the attribute has if no value has been assigned.

  • missing (None or unary function) – Callback function to handle a missing attribute in the input.

  • erroneous_as_absent (bool) – Treat an erroneous value (= the type_ raises ValueError or TypeError while parsing) as if the attribute was not present.

The type_ must be a Character Data Type, i.e. an instance of a subclass of AbstractCDataType. By default, it is aioxmpp.xso.String. The type_ is used to parse the XML character data into python types as appropriate. Errors during this parsing count as parsing errors of the whole XSO (subtree), unless erroneous_as_absent is set to true. In that case, the attribute is simply treated as absent if parsing the value fails.

If the XML attribute has no default assigned, the descriptor will appear to have the default value. If no default is given (the default) and an attempt is made to access the described attribute, AttributeError is raised as you would expect from any normal attribute.

If a default is given, the default is also returned after a del operation; otherwise, del behaves as for any normal attribute.

Another peculiar property of the default is that it does not need to conform to the validator or type_. If the descriptor is set to the default value, it is not emitted on the output.

In addition to the default, it is possible to inject attribute values at parse time using the missing callback. missing must be None or a function taking a single argument. If it is not None, it will be called with the parsing Context as its only argument when a missing attribute is encountered. The return value, unless None, is used as value for the descriptor. If the return value is None, the attribute is treated like any normal missing attribute.

It is possible to add validation to values received over the wire or assigned to the descriptor. The validator object controls what validation occurs and validate controls when validation occurs.

validate must be a member of the ValidateMode enumeration (see there for the semantics of the values). validator must be an object implementing the interface defined by AbstractValidator.

Note that validation is independent of the conversions applied by type_. Validation always happens on the parsed type and happens before serialisation. Thus, if type_ is not aioxmpp.xso.String, the validator will not receive a str object to operate on.

See also

LangAttr, which is a subclass of Attr specialized for describing xml:lang attributes.

Note

The default argument does not need to comply with either type_ or validator. This can be used to convey meaning with the absence of the attribute. Note that assigning the default value is not possible if it does not comply with type_ or validator and the del operator must be used instead.

from_value(instance, value)

Convert the given value using the set type_ and store it into instance’ attribute.

handle_missing(instance, ctx)[source]

Handle a missing attribute on instance. This is called whenever no value for the attribute is found during parsing. The call to missing() is independent of the value of required.

If the missing callback is not None, it is called with the instance and the ctx as arguments. If the returned value is not None, it is used as the value of the attribute (validation takes place as if the value had been set from the code, not as if the value had been received from XML) and the handler returns.

If the missing callback is None or returns None, the handling continues as normal: if required is true, a ValueError is raised.

to_dict(instance, d)[source]

Override the implementation from Text by storing the formatted value in the XML attribute instead of the character data.

If the value is None, no element is generated.

class aioxmpp.xso.LangAttr(*[, validator=None][, validate=ValidateMode.FROM_RECV][, default=None])[source]

Special handler for the xml:lang attribute.

An attribute representing the xml:lang attribute, including inheritance semantics.

This is a subclass of Attr which takes care of inheriting the xml:lang value of the parent. The tag is set to the (namespaces.xml, "lang") value to match xml:lang attributes. type_ is a xso.LanguageTag instance and missing is set to lang_attr().

Note that LangAttr overrides default to be None by default.

class aioxmpp.xso.Child(classes, *[, required=False][, strict=False])[source]

A single child element of any of the given XSO types.

Parameters
  • classes (iterable of aioxmpp.xso.XSO subclasses) – XSO types to support in this attribute

  • required (bool) – If true, parsing fails if the element is missing.

  • strict (bool) – Enable strict type checking on assigned values.

The tags among the classes must be unique, otherwise ValueError is raised on construction.

Instead of the default argument like supplied by Attr, Child only supports required: if required is a false value (the default), a missing child is tolerated and None is a valid value for the described attribute. Otherwise, a missing matching child is an error and the attribute cannot be set to None.

If strict is true, only instances of the exact classes registered with the descriptor can be assigned to it. Subclasses of the registered classes also need to be registered explicitly to be allowed as types for values.

This comes with a performance impact on every write to the descriptor, so it is disabled by default. It is recommended to enable this for descriptors where applications may register additional classes, to protect them from forgetting such a registration (which would cause issues with reception).

If during parsing, more than one child element with a tag matching one of the XSO.TAG values of the registered classes is encountered, it is unspecified which child is taken.

get_tag_map()

Return a dictionary mapping the tags of the supported classes to the classes themselves. Can be used to obtain a set of supported tags.

from_events(instance, ev_args, ctx)[source]

Detect the object to instantiate from the arguments ev_args of the "start" event. The new object is stored at the corresponding descriptor attribute on instance.

This method is suspendable.

to_sax(instance, dest)[source]

Take the object associated with this descriptor on instance and serialize it as child into the given lxml.etree.Element parent.

If the object is None, no content is generated.

class aioxmpp.xso.ChildTag(tags, *[, text_policy=UnknownTextPolicy.FAIL][, child_policy=UnknownChildPolicy.FAIL][, attr_policy=UnknownAttrPolicy.FAIL][, default_ns=None][, allow_none=False])[source]

Tag of a single child element with one of the given tags.

Parameters
  • tags (iterable of valid arguments to normalize_tags() or a enum.Enum subclass) – The tags to match on.

  • text_policy (UnknownTextPolicy) – Determine how text content on the child elements is handled.

  • child_policy (UnknownChildPolicy) – Determine how elements nested in the child elements are handled.

  • attr_policy (UnknownAttrPolicy) – Determine how attributes on the child elements are handled.

  • allow_none (bool) – If true, None is used as the default if no child matching the tags is found and represents the absence of the child for serialisation.

  • declare_prefix (False, None or str) – Which namespace prefix, if any, to declare on the child element for its namespace.

When assigned to a class’ attribute, this descriptor represents the presence or absence of a single child with a tag from a given set of valid tags.

tags must be an iterable of valid arguments to normalize_tag() or an enum.Enum whose values are valid arguments to normalize_tag(). If normalize_tag() returns a false value (such as None) as namespace_uri, it is replaced with default_ns (defaulting to None, which makes this sentence a no-op). This allows a benefit to readability if you have many tags which share the same namespace. This is, however, not allowed for tags given as enumeration.

text_policy, child_policy and attr_policy describe the behaviour if the child element unexpectedly has text, children or attributes, respectively. The default for each is to fail with a ValueError.

If allow_none is True, assignment of None to the attribute to which this descriptor belongs is allowed and represents the absence of the child element.

If declare_prefix is not False (note that None is a valid, non-False value in this context!), the namespace is explicitly declared using the given prefix when serializing to SAX.

from_events(instance, ev_args, ctx)[source]
to_sax(instance, dest)[source]
class aioxmpp.xso.ChildFlag(tag, *[, text_policy=UnknownTextPolicy.FAIL][, child_policy=UnknownChildPolicy.FAIL][, attr_policy=UnknownAttrPolicy.FAIL])[source]

Presence of a child element with the given tag, as boolean.

Parameters
  • tag (str or tuple) – The tag of the child element to use as flag.

  • text_policy (UnknownTextPolicy) – Determine how text content on the child elements is handled.

  • child_policy (UnknownChildPolicy) – Determine how elements nested in the child elements are handled.

  • attr_policy (UnknownAttrPolicy) – Determine how attributes on the child elements are handled.

When used as a XSO descriptor, it represents the presence or absence of a single child with the given tag. The presence or absence is represented by the values True and False respectively.

tag must be a valid tag.

The default value for attributes using this descriptor is False.

text_policy, child_policy and attr_policy describe the behaviour if the child element unexpectedly has text, children or attributes, respectively. The default for each is to fail with a ValueError.

If declare_prefix is not False (note that None is a valid, non-False value in this context!), the namespace is explicitly declared using the given prefix when serializing to SAX.

class aioxmpp.xso.ChildText(tag, *[, child_policy=UnknownChildPolicy.FAIL][, attr_policy=UnknownAttrPolicy.FAIL][, type_=xso.String()][, validator=None][, validate=ValidateMode.FROM_RECV][, default][, erroneous_as_absent=False])[source]

Character data of a single child element matching the given tag.

When assigned to a class’ attribute, it binds that attribute to the XML character data of a child element with the given tag. tag must be a valid input to normalize_tag().

Parameters
  • child_policy (UnknownChildPolicy) – The policy to apply when children are found in the child element whose text this descriptor represents.

  • attr_policy (UnknownAttrPolicy) – The policy to apply when attributes are found at the child element whose text this descriptor represents.

The type_, validate, validator, default and erroneous_as_absent arguments behave like in Attr.

declare_prefix works as for ChildTag.

child_policy and attr_policy describe how the parser behaves when an unknown child or attribute (respectively) is encountered on the child element whose text this descriptor represents. See UnknownChildPolicy and UnknownAttrPolicy for the possible behaviours.

get_tag_map()[source]

Return an iterable yielding tag.

This is for compatibility with the Child interface.

from_events(instance, ev_args, ctx)[source]

Starting with the element to which the start event information in ev_args belongs, parse text data. If any children are encountered, child_policy is enforced (see UnknownChildPolicy). Likewise, if the start event contains attributes, attr_policy is enforced (c.f. UnknownAttrPolicy).

The extracted text is passed through type_ and validator and if it passes, stored in the attribute on the instance with which the property is associated.

This method is suspendable.

to_sax(instance, dest)[source]

Create a child node at parent with the tag tag. Set the text contents to the value of the attribute which this descriptor represents at instance.

If the value is None, no element is generated.

class aioxmpp.xso.ChildValue(type_)[source]

Child element parsed using an Element Type.

Descriptor represeting a child element as parsed using an element type.

Parameters

type (aioxmpp.xso.AbstractElementType) – The element type to use to parse the child element.

The descriptor value will be the unpacked child element value. Upon serialisation, the descriptor

class aioxmpp.xso.Text(*[, type_=xso.String()][, validator=None][, validate=ValidateMode.FROM_RECV][, default][, erroneous_as_absent=False])[source]

Character data contents of an XSO.

Note that this does not preserve the relative ordering of child elements and character data pieces. This is known and a WONTFIX, as it is not required in XMPP to keep that relative order: Elements either have character data or other elements as children.

The type_, validator, validate, default and erroneous_as_absent arguments behave like in Attr.

from_value(instance, value)[source]

Convert the given value using the set type_ and store it into instance’ attribute.

to_sax(instance, dest)[source]

Assign the formatted value stored at instance’ attribute to the text of el.

If the value is None, no text is generated.

Non-scalar descriptors

class aioxmpp.xso.ChildList(classes)[source]

List of child elements of any of the given XSO classes.

The ChildList works like Child, with two key differences:

  • multiple children which are matched by this descriptor get collected into an XSOList.

  • the default is fixed at an empty list.

  • required is not supported

from_events(instance, ev_args, ctx)[source]

Like Child.from_events(), but instead of replacing the attribute value, the new object is appended to the list.

to_sax(instance, dest)[source]

Like Child.to_node(), but instead of serializing a single object, all objects in the list are serialized.

class aioxmpp.xso.ChildMap(classes[, key=None])[source]

Dictionary holding child elements of one or more XSO classes.

The ChildMap class works like ChildList, but instead of storing the child objects in a list, they are stored in a map which contains an XSOList of objects for each tag.

key may be callable. If it is given, it is used while parsing to determine the dictionary key under which a newly parsed XSO will be put. For that, the key callable is called with the newly parsed XSO as the only argument and is expected to return the key.

from_events(instance, ev_args, ctx)[source]

Like ChildList.from_events(), but the object is appended to the list associated with its tag in the dict.

to_sax(instance, dest)[source]

Serialize all objects in the dict associated with the descriptor at instance to the given parent.

The order of elements within a tag is preserved; the order of the tags relative to each other is undefined.

The following utility function is useful when filling data into descriptors using this class:

fill_into_dict(items, dest)[source]

Take an iterable of items and group it into the given dest dict, using the key function.

The dest dict must either already contain the keys which are generated by the key function for the items in items, or must default them suitably. The values of the affected keys must be sequences or objects with an append() method which does what you want it to do.

class aioxmpp.xso.ChildLangMap(classes)[source]

Shorthand for a dictionary of child elements keyed by the language attribute.

The ChildLangMap class is a specialized version of the ChildMap, which uses a key function to group the children by their XML language tag.

It is expected that the language tag is available as lang attribute on the objects stored in this map.

class aioxmpp.xso.ChildValueList(type_)[source]

List of child elements parsed using the given Element Type.

Parameters

This descriptor parses the XSO classes advertised by the type_ (via get_xso_types()) and exposes the unpacked values in a container.

The optional container_type argument must, if given, be a callable which returns a mutable container supporting either add() or append() of the values used with the type_ and iteration. It will be used instead of list to create the values for the descriptor.

New in version 0.5.

class aioxmpp.xso.ChildValueMap(type_, *, mapping_type=dict)[source]

Dictiorary of child elements parsed using the given Element Type.

Parameters
  • type (AbstractElementType) – Type describing the subtree to convert to pairs of key and value.

  • mapping_type (Subclass of MutableMapping) – Type of the mapping to use.

This works very similar to ChildValueList, but instead of a mutable sequence, the value of the descriptor is a mutable mapping.

The type_ must return key-value pairs from xso.AbstractElementType.unpack() and must accept such key-value pairs in xso.AbstractElementType.pack().

The optional mapping_type argument must, if given, be a callable which returns a collections.abc.MutableMapping supporting the keys and values used by the type_. It will be used instead of dict to create the values for the descriptor. A possible use-case is using structs.LanguageMap together with TextChildMap.

See also

ChildTextMap for a specialised version to deal with AbstractTextChild subclasses.

New in version 0.5.

class aioxmpp.xso.ChildValueMultiMap(type_, *, mapping_type=multidict.MultiDict)[source]

Multi-dict of child elements parsed using the given Element Type.

Parameters

type (AbstractElementType) – Type describing the subtree to convert to pairs of key and value.

This is very similar to ChildValueMap, but it uses a multidict.MultiDict as storage. Interface-compatible classes can be substituted by passing them to mapping_type. Candidate for that are multidict.CIMultiDict.

New in version 0.6.

class aioxmpp.xso.ChildTextMap(xso_type)[source]

Dictionary of character data in child elements keyed by the language attribute.

A specialised version of ChildValueMap which uses TextChildMap together with structs.LanguageMap to convert the AbstractTextChild subclass xso_type to and from a language-text mapping.

If instead of an XSO a tag is passed (that is, a valid argument to normalize_tag()) an AbstractTextChild instance is created on demand.

For an example, see Message.

class aioxmpp.xso.Collector[source]

Catch-all descriptor collecting unhandled elements in an lxml element tree.

When assigned to a class’ attribute, it collects all children which are not known to any other descriptor into an XML tree. The root node has the tag of the XSO class it pertains to.

The default is fixed to the empty root node.

Changed in version 0.10: Before the subtrees were collected in a list. This was changed to an ElementTree to allow using XPath over all collected elements. Most code should not be affected by this, since the interface is very similar. Assignment is now forbidden. Use [:] = instead.

from_events(instance, ev_args, ctx)[source]

Collect the events and convert them to a single XML subtree, which then gets appended to the list at instance. ev_args must be the arguments of the "start" event of the new child.

This method is suspendable.

to_sax(instance, dest)[source]

Container for child lists

The child lists in ChildList, ChildMap and ChildLangMap descriptors use a specialized list-subclass which provides advanced capabilities for filtering XSO objects.

class aioxmpp.xso.model.XSOList(iterable=(), /)[source]

A list subclass; it provides the complete list interface with the addition of the following methods:

filter(*, type_=None, lang=None, attrs={})[source]

Return an iterable which produces a sequence of the elements inside this XSOList, filtered by the criteria given as arguments. The function starts with a working sequence consisting of the whole list.

If type_ is not None, elements which are not an instance of the given type are excluded from the working sequence.

If lang is not None, it must be either a LanguageRange or an iterable of language ranges. The set of languages present among the working sequence is determined and used for a call to lookup_language. If the lookup returns a language, all elements whose lang is different from that value are excluded from the working sequence.

Note

If an iterable of language ranges is given, it is evaluated into a list. This may be of concern if a huge iterable is about to be used for language ranges, but it is an requirement of the lookup_language function which is used under the hood.

Note

Filtering by language assumes that the elements have a LangAttr descriptor named lang.

If attrs is not empty, the filter iterates over each key-value pair. For each iteration, all elements which do not have an attribute of the name in key or where that attribute has a value not equal to value are excluded from the working sequence.

In general, the iterable returned from filter() can only be used once. It is dynamic in the sense that changes to elements which are in the list behind the last element returned from the iterator will still be picked up when the iterator is resumed.

filtered(*, type_=None, lang=None, attrs={})[source]

This method is a convencience wrapper around filter() which evaluates the result into a list and returns that list.

In the future, methods to add indices to XSOList instances may be added; right now, there is no need for the huge complexity which would arise from keeping the indices up-to-date with changes in the elements attributes.

Parsing XSOs

To parse XSOs, an asynchronous approach which uses SAX-like events is followed. For this, the suspendable functions explained earlier are used. The main class to parse a XSO from events is XSOParser. To drive that suspendable callable from SAX events, use a SAXDriver.

class aioxmpp.xso.XSOParser[source]

A generic XSO parser which supports a dynamic set of XSOs to parse. XSOParser objects are callable and they are suspendable methods (i.e. calling a XSOParser returns a generator which parses stanzas from sax-ish events. Use with SAXDriver).

Example use:

# let Message be a XSO class, like in the XSO example
result = None
def catch_result(value):
    global result
    result = value

parser = aioxmpp.xso.XSOParser()
parser.add_class(Message, catch_result)
sd = aioxmpp.xso.SAXDriver(parser)
lxml.sax.saxify(lxml.etree.fromstring(
    "<jc:message id='foo' from='bar' to='baz' type='chat' "
    "xmlns:jc='jabber:client'/>"
), sd)

The following methods can be used to dynamically add and remove top-level XSO classes.

add_class(cls, callback)[source]

Add a class cls for parsing as root level element. When an object of cls type has been completely parsed, callback is called with the object as argument.

remove_class(cls)[source]

Remove a XSO class cls from parsing. This method raises KeyError with the classes TAG attribute as argument if removing fails because the class is not registered.

get_tag_map()[source]

Return the internal mapping which maps tags to tuples of (cls, callback).

Warning

The results of modifying this dict are undefined. Make a copy if you need to modify the result of this function.

class aioxmpp.xso.SAXDriver(dest_generator_factory, on_emit=None)[source]

This is a xml.sax.handler.ContentHandler subclass which only supports namespace-conforming SAX event sources.

dest_generator_factory must be a function which returns a new suspendable method supporting the interface of XSOParser. The SAX events are converted to an internal event format and sent to the suspendable function in order.

on_emit may be a callable. Whenever a suspendable function returned by dest_generator_factory returns, with the return value as sole argument.

When you are done with a SAXDriver, you should call close() to clean up internal parser state.

close()[source]

Clean up all internal state.

Base and meta class

The XSO base class makes use of the model.XMLStreamClass metaclass and provides implementations for utility methods. For an object to work with this module, it must derive from XSO or provide an identical interface.

class aioxmpp.xso.XSO[source]

XSO is short for XML Stream Object and means an object which represents a subtree of an XML stream. These objects can also be created and validated on-the-fly from SAX-like events using XSOParser.

The constructor does not require any arguments and forwards them directly the next class in the resolution order. Note that during deserialization, __init__ is not called. It is assumed that all data is loaded from the XML stream and thus no initialization is required.

This is beneficial to applications, as it allows them to define mandatory arguments for __init__. This would not be possible if __init__ was called during deserialization. A way to execute code after successful deserialization is provided through xso_after_load().

XSO objects support copying. Like with deserialisation, __init__ is not called during copy. The default implementation only copies the XSO descriptors’ values (with deepcopy, they are copied deeply). If you have more attributes to copy, you need to override __copy__ and __deepcopy__ methods.

Changed in version 0.4: Copy and deepcopy support has been added. Previously, copy copied not enough data, while deepcopy copied too much data (including descriptor objects).

To declare an XSO, inherit from XSO and provide the following attributes on your class:

  • A TAG attribute, which is a tuple (namespace_uri, localname) representing the tag of the XML element you want to match.

  • An arbitrary number of Text, Collector, Child, ChildList and Attr-based attributes.

See also

xso.model.XMLStreamClass

is the metaclass of XSO. The documentation of the metaclass holds valuable information with respect to modifying XSO classes and subclassing.

Note

Attributes whose name starts with xso_ or _xso_ are reserved for use by the aioxmpp.xso implementation. Do not use these in your code if you can possibly avoid it.

XSO subclasses automatically declare a __slots__ attribute which does not include the __dict__ value. This effectively prevents any attributes not declared on the class as descriptors from being written. The rationale is detailed on in the linked documentation. To prevent this from happening in your subclass, inherit with protect set to false:

class MyXSO(xso.XSO, protect=False):
    pass

New in version 0.6: The handling of the __slots__ attribute was added.

To further influence the parsing behaviour of a class, two attributes are provided which give policies for unexpected elements in the XML tree:

UNKNOWN_CHILD_POLICY = UnknownChildPolicy.DROP

A value from the UnknownChildPolicy enum which defines the behaviour if a child is encountered for which no matching attribute is found.

Note that this policy has no effect if a Collector descriptor is present, as it takes all children for which no other descriptor exists, thus all children are known.

UNKNOWN_ATTR_POLICY = UnknownAttrPolicy.DROP

A value from the UnknownAttrPolicy enum which defines the behaviour if an attribute is encountered for which no matching descriptor is found.

Example:

class Body(aioxmpp.xso.XSO):
    TAG = ("jabber:client", "body")

    text = aioxmpp.xso.Text()

class Message(aioxmpp.xso.XSO):
    TAG = ("jabber:client", "message")
    UNKNOWN_CHILD_POLICY = aioxmpp.xso.UnknownChildPolicy.DROP

    type_ = aioxmpp.xso.Attr(tag="type")
    from_ = aioxmpp.xso.Attr(tag="from")
    to = aioxmpp.xso.Attr(tag="to")
    id_ = aioxmpp.xso.Attr(tag="id")

    body = aioxmpp.xso.Child([Body])

Beyond the validation of the individual descriptor values, it is possible to implement more complex validation steps by overriding the validate() method:

validate()[source]

Validate the objects structure beyond the values of individual fields (which have their own validators).

This first calls _PropBase.validate_contents() recursively on the values of all child descriptors. These may raise (or re-raise) errors which occur during validation of the child elements.

To implement your own validation logic in a subclass of XSO, override this method and call it via super() before doing your own validation.

Validate is called by the parsing stack after an object has been fully deserialized from the SAX event stream. If the deserialization fails due to invalid values of descriptors or due to validation failures in child objects, this method is obviously not called.

The following methods are available on instances of XSO:

xso_serialise_to_sax(dest)[source]

Serialise the XSO to a SAX handler.

Parameters

dest – SAX handler to send the events to

Changed in version 0.11: The method was renamed from unparse_to_sax to xso_serialise_to_sax.

The following class methods are provided by the metaclass:

classmethod parse_events(ev_args)

Create an instance of this class, using the events sent into this function. ev_args must be the event arguments of the "start" event.

See also

You probably should not call this method directly, but instead use XSOParser with a SAXDriver.

Note

While this method creates an instance of the class, __init__ is not called. See the documentation of xso.XSO() for details.

This method is suspendable.

classmethod register_child(prop, child_cls)

Register a new XMLStreamClass instance child_cls for a given Child descriptor prop.

Warning

This method cannot be used after a class has been derived from this class. This is for consistency: the method modifies the bookkeeping attributes of the class. There would be two ways to deal with the situation:

  1. Updating all the attributes at all the subclasses and re-evaluate the constraints of inheritance. This is simply not implemented, although it would be the preferred way.

  2. Only update the bookkeeping attributes on this class, hiding the change from any existing subclasses. New subclasses would pick the change up, however, which is inconsistent. This is the way which was previously documented here and is not supported anymore.

Obviously, (2) is bad, which is why it is not supported anymore. (1) might be supported at some point in the future.

Attempting to use register_child() on a class which already has subclasses results in a TypeError.

Note that first using register_child() and only then deriving clasess is a valid use: it will still lead to a consistent inheritance hierarchy and is a convenient way to break reference cycles (e.g. if an XSO may be its own child).

To customize behaviour of deserialization, these methods are provided which can be re-implemented by subclasses:

xso_after_load()[source]

After an object has been successfully deserialized, this method is called. Note that __init__ is never called on objects during deserialization.

xso_error_handler(descriptor, ev_args, exc_info)[source]

This method is called whenever an error occurs while parsing.

If an exception is raised by the parsing function of a descriptor attribute, such as Attr, the descriptor is passed as first argument, the exc_info tuple as third argument and the arguments which led to the descriptor being invoked as second argument.

If an unknown child is encountered and the UNKNOWN_CHILD_POLICY is set to UnknownChildPolicy.FAIL, descriptor and exc_info are passed as None and ev_args are the arguments to the "start" event of the child (i.e. a triple (namespace_uri, localname, attributes)).

If the error handler wishes to suppress the exception, it must return a true value. Otherwise, the exception is propagated (or a new exception is raised, if the error was not caused by an exception). The error handler may also raise its own exception.

Warning

Suppressing exceptions can cause invalid input to reside in the object or the object in general being in a state which violates the schema.

For example, suppressing exceptions about missing attributes will cause the attribute to remain uninitialized (i.e. left at its default value).

Even if the error handler suppresses an exception caused by a broken child, that child will not be added to the object.

class aioxmpp.xso.CapturingXSO[source]

The following class methods is provided by the metaclass (which is not publicly available, but a subclass of XMLStreamClass):

classmethod parse_events(ev_args, parent_ctx)

Capture the events sent to XSO.parse_events(), including the initial ev_args to a list and call _set_captured_events() on the result of XSO.parse_events().

Like the method it overrides, parse_events() is suspendable.

The _set_captured_events() method can be overridden by subclasses to make use of the captured events:

abstract _set_captured_events(events)[source]

This method is called by parse_events() after parsing the object. events is the list of event tuples which this object was deserialised from.

Subclasses must override this method.

An example use case for this class is disco.InfoQuery, combined with aioxmpp.entitycaps. We want to be able to store hashes and the generating XML data for use with future versions, including XML data which cannot be parsed by an XSO in the current process (for example, due to an unknown namespace or a plugin which is available but not loaded). With the captured events, it is possible to re-create XML semantically equivalent to the XML originally received.

New in version 0.5.

The metaclass takes care of collecting the special descriptors in attributes where they can be used by the SAX event interpreter to fill the class with data. It also provides a class method for late registration of child classes.

class aioxmpp.xso.model.XMLStreamClass(name, bases, namespace, protect=True)[source]

This metaclass is used to implement the fancy features of XSO classes and instances. Its documentation details on some of the restrictions and features of XML Stream Classes.

Note

There should be no need to use this metaclass directly when implementing your own XSO classes. Instead, derive from XSO.

The following restrictions apply when a class uses the XMLStreamClass metaclass:

  1. At no point in the inheritance tree there must exist more than one distinct Text descriptor. It is possible to inherit two identical text descriptors from several base classes though.

  2. The above applies equivalently for Collector descriptors.

  3. At no point in the inheritance tree there must exist more than one Attr descriptor which handles a given attribute tag. Like with Text, it is allowed that the same Attr descriptor is inherited through multiple paths from parent classes.

  4. The above applies likewise for element tags and Child (or similar) descriptors.

Objects of this metaclass (i.e. classes) have some useful attributes. The following attributes are gathered from the namespace of the class, by collecting the different XSO-related descriptors:

TEXT_PROPERTY

The Text descriptor object associated with this class. This is None if no attribute using that descriptor is declared on the class.

COLLECTOR_PROPERTY

The Collector descriptor object associated with this class. This is None if no attribute using that descriptor is declared on the class.

ATTR_MAP

A dictionary mapping attribute tags to the Attr descriptor objects for these attributes.

CHILD_MAP

A dictionary mapping element tags to the Child (or similar) descriptor objects which accept these child elements.

CHILD_PROPS

A set of all Child (or ChildList) descriptor objects of this class.

DECLARE_NS

A dictionary which defines the namespace mappings which shall be declared when serializing this element. It must map namespace prefixes (such as None or "foo") to namespace URIs.

For maximum compatibility with legacy XMPP implementations (I’m looking at you, ejabberd!), DECLARE_NS is set by this metaclass unless it is provided explicitly when declaring the class:

  • If no TAG is set, DECLARE_NS is also not set. The attribute does not exist on the class in that case, unless it is inherited from a base class.

  • If TAG is set and at least one base class has a DECLARE_NS, DECLARE_NS is not auto generated, so that inheritance can take place.

  • If TAG is set and has a namespace (and no base class has a DECLARE_NS), DECLARE_NS is set to { None: namespace }, where namespace is the namespace of the TAG.

  • If TAG is set and does not have a namespace, DECLARE_NS is set to the empty dict. This should not occur outside testing, and support for tags without namespace might be removed in future versions.

Warning

It is discouraged to use namespace prefixes of the format "ns{:d}".format(n), for any given number n. These prefixes are reserved for ad-hoc namespace declarations, and attempting to use them may have unwanted side-effects.

Changed in version 0.4: The automatic generation of the DECLARE_NS attribute was added in 0.4.

__slots__

The metaclass automatically sets this attribute to the empty tuple, unless a different value is set in the class or protect is passed as false to the metaclass.

Thus, to disable the automatic setting of __slots__, inherit for example like this:

class MyXSO(xso.XSO, protect=False):
    pass

The rationale for this is that attributes on XSO instances are magic. Having a typo in an attribute may fail non-obviously, if it causes an entirely different semantic to be invoked at the peer (for example the Message.type_ attribute).

Setting __slots__ to empty by default prevents assigning any attribute not bound to an descriptor.

See also

__slots__

The official Python documentation describes the semantics of the __slots__ attribute in more detail.

XSO automatically sets a sensible __slots__ (including __weakref__, but not __dict__).

New in version 0.6.

Note

If you need to stay compatible with versions before 0.6 and have arbitrary attributes writable, the correct way of doing things is to explicitly set __slots__ to ("__dict__",) in your class. You cannot use protect because it is not known in pre-0.6 versions.

Note

XSO defines defaults for more attributes which also must be present on objects which are used as XSOs.

When inheriting from XMLStreamClass objects, the properties are merged sensibly.

Rebinding attributes of XMLStreamClass instances (i.e. classes using this metaclass) is somewhat restricted. The following rules cannot be broken, attempting to do so will result in TypeError being raised when setting the attribute:

  1. Existing descriptors for XSO purposes (such as xso.Attr) cannot be removed (either by assigning a new value to the name they are bound to or deleting the name).

  2. New descriptors can only be added if they do not violate the rules stated at the beginning of the XMLStreamClass documentation.

  3. New descriptors can only be added if no subclasses exist (see xso.XSO.register_child() for reasons why).

To create an enumeration of XSO classes, the following mixin can be used:

class aioxmpp.xso.XSOEnumMixin(namespace, localname)[source]

Mix-in to create enumerations of XSOs.

New in version 0.10.

The enumeration member values must be pairs of namespace, localpart strings. Each enumeration member is equipped with an xso_class attribute at definition time.

to_xso()[source]

A new instance of the xso_class.

This method exists to make it easier to use the XSO objects and the enumeration members interchangeably. The XSO objects also have the to_xso() method which just returns the XSO unmodified.

Code which needs an XSO, but does not care about the data, can thus use the to_xso() method to “coerce” either (enumeration members and instances of their XSO classes) into XSOs.

enum_member

The object (enum member) itself.

This property exists to make it easier to use the XSO objects and the enumeration members interchangeably. The XSO objects also have the enum_member property to obtain the enumeration member to which they belong. Code which is only interested in the enumeration member can thus access the enum_member attribute to “coerce” both (enumeration members and instances of their XSO classes) into enumeration members.

xso_class

A aioxmpp.xso.XSO subclass which has the enumeration members value as TAG. So the subclass matches elements which have the qualified tag in the enumeration member value.

The class does not have any XSO descriptors assigned. They can be added after class definition.

enum_member

The enumeration member to which the xso_class belongs.

This allows to use XSOs and enumeration members more interchangeably; see enum_member for details.

to_xso()[source]

Return the XSO itself.

This allows to use XSOs and enumeration members more interchangeably; see to_xso() for details.

Example usage:

class TagEnum(aioxmpp.xso.XSOEnumMixin, enum.Enum):
    X = ("uri:foo", "x")
    Y = ("uri:foo", "y")

TagEnum.X.xso_class.enabled = aioxmpp.xso.Attr(
    "enabled",
    type_=aioxmpp.xso.Bool()
)

The TagEnum members then have a xso_class attribute which is a subclass of XSO (not an instance of a subclass of XSO).

The xso_class for TagEnum.X also supports the enabled attribute (due to it being monkey-patched onto it), while the xso_class for TagEnum.Y does not. Thus, monkey-patching can be used to customize the individual XSO classes of the members.

To use such an enum on a descriptor, the following syntax can be used:

class Element(aioxmpp.xso.XSO):
    TAG = ("uri:foo", "parent")

    child = aioxmpp.xso.Child([
        member.xso_class
        for member in TagEnum
    ])

Functions, enumerations and exceptions

The values of the following enumerations are used on “magic” attributes of XMLStreamClass instances (i.e. classes).

class aioxmpp.xso.UnknownChildPolicy(value)[source]

Describe the event which shall take place whenever a child element is encountered for which no descriptor can be found to parse it.

FAIL

Raise a ValueError

DROP

Drop and ignore the element and all of its children

class aioxmpp.xso.UnknownAttrPolicy(value)[source]

Describe the event which shall take place whenever a XML attribute is encountered for which no descriptor can be found to parse it.

FAIL

Raise a ValueError

DROP

Drop and ignore the attribute

class aioxmpp.xso.UnknownTextPolicy(value)[source]

Describe the event which shall take place whenever XML character data is encountered on an object which does not support it.

FAIL

Raise a ValueError

DROP

Drop and ignore the text

class aioxmpp.xso.ValidateMode(value)[source]

Control which ways to set a value in a descriptor are passed through a validator.

FROM_RECV

Values which are obtained from XML source are validated.

FROM_CODE

Values which are set through attribute access are validated.

ALWAYS

All values, whether set by attribute or obtained from XML source, are validated.

The following exceptions are generated at some places in this module:

class aioxmpp.xso.UnknownTopLevelTag(msg, ev_args)[source]

Subclass of ValueError. ev_args must be the arguments of the "start" event and are stored as the ev_args attribute for inspection.

ev_args

The ev_args passed to the constructor.

The following special value is used to indicate that no default is used with a descriptor:

aioxmpp.xso.NO_DEFAULT

This is a special value which is used to indicate that no defaulting should take place. It can be passed to the default arguments of descriptors, and usually is the default value of these arguments.

It compares unequal to everything but itself, does not support ordering, conversion to bool, float or integer.

aioxmpp.xso.capture_events(receiver, dest)[source]

Capture all events sent to receiver in the sequence dest. This is a generator, and it is best used with yield from. The observable effect of using this generator with yield from is identical to the effect of using receiver with yield from directly (including the return value), but in addition, the values which are sent to the receiver are captured in dest.

If receiver raises an exception or the generator is closed prematurely using its close(), dest is cleared.

This is used to implement CapturingXSO. See the documentation there for use cases.

New in version 0.5.

aioxmpp.xso.events_to_sax(events, dest)[source]

Convert an iterable events of XSO events to SAX events by calling the matching SAX methods on dest

Handlers for missing attributes

aioxmpp.xso.lang_attr(instance, ctx)[source]

A missing handler for Attr descriptors. If any parent object has a xml:lang attribute set, its value is used.

Pass as missing argument to Attr constructors to use this behaviour for a given attribute.

Types and validators from types

This module provides classes whose objects can be used as types and validators in model.

Character Data types

String([prepfunc])

String Character Data Type, optionally with string preparation.

Float()

Floating point or decimal Character Data Type.

Integer()

Integer Character Data Type, to the base 10.

Bool()

XML boolean Character Data Type.

DateTime(*[, legacy])

ISO datetime Character Data Type.

Date()

ISO date Character Data Type.

Time()

ISO time Character Data Type.

Base64Binary(*[, empty_as_equal])

Character Data Type for bytes encoded as base64.

HexBinary()

Character Data Type for bytes encoded as hexadecimal.

JID(*[, strict])

Character Data Type for aioxmpp.JID objects.

ConnectionLocation()

Character Data Type for a hostname-port pair.

LanguageTag()

Character Data Type for language tags.

JSON()

Character Data Type for JSON formatted data.

EnumCDataType(enum_class[, nested_type, …])

Use an enum.Enum as type for an XSO descriptor.

These types describe character data, i.e. text in XML. Thus, they can be used with Attr, Text and similar descriptors. They are used to deserialise XML character data to python values, such as integers or dates and vice versa. These types inherit from AbstractCDataType.

class aioxmpp.xso.String(prepfunc=None)[source]

String Character Data Type, optionally with string preparation.

Optionally, a stringprep function prepfunc can be applied on the string. A stringprep function must take the string and prepare it accordingly; if it is invalid input, it must raise ValueError. Otherwise, it shall return the prepared string.

If no prepfunc is given, this type is the identity operation.

class aioxmpp.xso.Float[source]

Floating point or decimal Character Data Type.

class aioxmpp.xso.Integer[source]

Integer Character Data Type, to the base 10.

class aioxmpp.xso.Bool[source]

XML boolean Character Data Type.

Parse the value as boolean:

  • "true" and "1" are taken as True,

  • "false" and "0" are taken as False,

  • everything else results in a ValueError exception.

class aioxmpp.xso.DateTime(*, legacy=False)[source]

ISO datetime Character Data Type.

Parse the value as ISO datetime, possibly including microseconds and timezone information.

Timezones are handled as constant offsets from UTC, and are converted to UTC before the datetime object is returned (which is correctly tagged with UTC tzinfo). Values without timezone specification are not tagged.

If legacy is true, the formatted dates use the legacy date/time format (CCYYMMDDThh:mm:ss), as used for example in XEP-0082 or XEP-0009 (whereas in the latter it is not legacy, but defined by XML RPC). In any case, parsing of the legacy format is transparently supported. Timestamps in the legacy format are assumed to be in UTC, and datetime objects are converted to UTC before emitting the legacy format. The timezone designator is never emitted with the legacy format, and ignored if given.

This class makes use of pytz.

New in version 0.5: The legacy argument was added.

class aioxmpp.xso.Date[source]

ISO date Character Data Type.

Implement the Date type from XEP-0082.

Values must have the date type, datetime is forbidden to avoid silent loss of information.

New in version 0.5.

class aioxmpp.xso.Time[source]

ISO time Character Data Type.

Implement the Time type from XEP-0082.

Values must have the time type, datetime is forbidden to avoid silent loss of information. Assignment of time values in time zones which are not UTC is not allowed either. The reason is that the translation to UTC on formatting is not properly defined without an accompanying date (think daylight saving time transitions, redefinitions of time zones, …).

New in version 0.5.

class aioxmpp.xso.Base64Binary(*, empty_as_equal=False)[source]

Character Data Type for bytes encoded as base64.

Parse the value as base64 and return the bytes object obtained from decoding.

If empty_as_equal is True, an empty value is represented using a single equal sign. This is used in the SASL protocol.

class aioxmpp.xso.HexBinary[source]

Character Data Type for bytes encoded as hexadecimal.

Parse the value as hexadecimal blob and return the bytes object obtained from decoding.

class aioxmpp.xso.JID(*, strict=False)[source]

Character Data Type for aioxmpp.JID objects.

Parse the value as Jabber ID using fromstr() and return the aioxmpp.JID object.

strict is passed to fromstr() and defaults to false. See the fromstr() method for a rationale and consider that parse() is only called for input coming from the outside.

class aioxmpp.xso.ConnectionLocation[source]

Character Data Type for a hostname-port pair.

Parse the value as a host-port pair, as for example used for Stream Management reconnection location advisories.

class aioxmpp.xso.LanguageTag[source]

Character Data Type for language tags.

Parses the value as Language Tag using fromstr().

Type coercion requires that any value assigned to a descriptor using this type is an instance of LanguageTag.

class aioxmpp.xso.JSON[source]

Character Data Type for JSON formatted data.

New in version 0.11.

Upon deserialisation, character data is parsed as JSON using json. On serialisation, the value is serialised as JSON. This implies that the data must be JSON serialisable, but there is no check for that in coerce(), as this check would be (a) expensive to do for nested data structures and (b) impossible to do for mutable data structures.

Example:

class JSONContainer(aioxmpp.xso.XSO):
    TAG = ("urn:xmpp:json:0", "json")

    data = aioxmpp.xso.Text(
        type_=aioxmpp.xso.JSON()
    )
class aioxmpp.xso.EnumCDataType(enum_class, nested_type=xso.String(), *, allow_coerce=False, deprecate_coerce=False, allow_unknown=True, accept_unknown=True)[source]

Use an enum.Enum as type for an XSO descriptor.

Parameters
  • enum_class – The Enum to use as type.

  • nested_type (AbstractCDataType) – A type which can handle the values of the enumeration members.

  • allow_coerce (bool) – Allow coercion of different types to enumeration values.

  • deprecate_coerce (int or bool) – Emit DeprecationWarning when coercion occurs. Requires (but does not imply) allow_coerce.

  • allow_unknown (bool) – If true, unknown values are converted to Unknown instances when parsing values from the XML stream.

  • accept_unknown (bool) – If true, Unknown instances are passed through coerce() and can thus be assigned to descriptors using this type.

  • pass_unknown (bool) – If true, unknown values are accepted unmodified (both on the receiving and on the sending side). It is useful for some enum.IntEnum use cases.

A descriptor using this type will accept elements from the given enum_class as values. Upon serialisiation, the value of the enumeration element is taken and formatted through the given nested_type.

Normally, coerce() will raise TypeError for any value which is not an instance of enum_class. However, if allow_coerce is true, the value is passed to the enum_class constructor and the result is returned; the ValueError raised from the enum_class constructor if an invalid value is passed propagates unmodified.

Note

When using allow_coerce, keep in mind that this may have surprising effects for users. Coercion means that the value assigned to an attribute and the value subsequently read from that attribute may not be the same; this may be very surprising to users:

class E(enum.Enum):
    X = "foo"

class SomeXSO(xso.XSO):
    attr = xso.Attr("foo", xso.EnumCDataType(E, allow_coerce=True))

x = SomeXSO()
x.attr = "foo"
assert x.attr == "foo"  # assertion fails!

To allow coercion transitionally while moving from e.g. string-based values to a proper enum, deprecate_coerce can be used. In that case, a DeprecationWarning (see warnings) is emitted when coercion takes place, to warn users about future removal of the coercion capability. If deprecate_coerce is an integer, it is used as the stacklevel argument for the warnings.warn() call. If it is True, the stacklevel is 4, which leads to the warning pointing to a descriptor assignment when used with XSO descriptors.

Handling of Unknown values: Using allow_unknown and accept_unknown is advisable to stay compatible with future protocols, which is why both are enabled by default. Considering that constructing an Unknown value needs to be done explicitly in code, it is unlikely that a user will accidentally assign an unspecified value to a descriptor using this type with accept_unknown.

pass_unknown requires allow_unknown and accept_unknown. When set to true, values which are not a member of enum_class are used without modification (but they are validated against the nested_type). This applies to both the sending and the receiving side. The intended use case is with enum.IntEnum classes. If a Unknown value is passed, it is unwrapped and treated as if the original value had been passed.

Example:

class SomeEnum(enum.Enum):
    X = 1
    Y = 2
    Z = 3

class SomeXSO(xso.XSO):
    attr = xso.Attr(
        "foo",
        type_=xso.EnumCDataType(
            SomeEnum,
            # have to use integer, because the value of e.g. SomeEnum.X
            # is integer!
            xso.Integer()
        ),
    )

Changed in version 0.10: Support for pass_unknown was added.

aioxmpp.xso.EnumType(enum_class, [nested_type, ]*, allow_coerce=False, deprecate_coerce=False, allow_unknown=True, accept_unknown=True)[source]

Create and return a EnumCDataType or EnumElementType, depending on the type of nested_type.

If nested_type is a AbstractCDataType or omitted, a EnumCDataType is constructed. Otherwise, EnumElementType is used.

The arguments are forwarded to the respective class’ constructor.

New in version 0.10.

Deprecated since version 0.10: This function was introduced to ease the transition in 0.10 from a unified EnumType to split EnumCDataType and EnumElementType.

It will be removed in 1.0.

class aioxmpp.xso.Unknown(value)[source]

A wrapper for an unknown enumeration value.

Parameters

value (arbitrary) – The raw value of the “enumeration” “member”.

Instances of this class may be emitted from and accepted by EnumCDataType and EnumElementType, see the documentation there for details.

Unknown instances compare equal when they hold an equal value. Unknown objects are hashable if their values are hashable. The value they refer to cannot be changed during the lifetime of an Unknown object.

Element types

EnumElementType(enum_class, nested_type, *)

Use an enum.Enum as type for an XSO descriptor.

TextChildMap(xso_type)

A type for use with xso.ChildValueMap and descendants of xso.AbstractTextChild.

These types describe structured XML data, i.e. subtrees. Thus, they can be used with the ChildValueList and ChildValueMap family of descriptors (which represent XSOs as python values). These types inherit from AbstractElementType.

class aioxmpp.xso.EnumElementType(enum_class, nested_type, *, allow_coerce=False, deprecate_coerce=False, allow_unknown=True, accept_unknown=True)[source]

Use an enum.Enum as type for an XSO descriptor.

Parameters
  • enum_class – The Enum to use as type.

  • nested_type (AbstractElementType) – Type which describes the value type of the enum_class.

  • allow_coerce (bool) – Allow coercion of different types to enumeration values.

  • deprecate_coerce (int or bool) – Emit DeprecationWarning when coercion occurs. Requires (but does not imply) allow_coerce.

  • allow_unknown (bool) – If true, unknown values are converted to Unknown instances when parsing values from the XML stream.

  • accept_unknown – If true, Unknown instances are passed through coerce() and can thus be assigned to descriptors using this type.

A descriptor using this type will accept elements from the given enum_class as values. Upon serialisiation, the value of the enumeration element is taken and packed through the given nested_type.

Normally, coerce() will raise TypeError for any value which is not an instance of enum_class. However, if allow_coerce is true, the value is passed to the enum_class constructor and the result is returned; the ValueError raised from the enum_class constructor if an invalid value is passed propagates unmodified.

See also

EnumCDataType

for a detailed discussion on the implications of coercion.

Handling of Unknown values: Using allow_unknown and accept_unknown is advisable to stay compatible with future protocols, which is why both are enabled by default. Considering that constructing an Unknown value needs to be done explicitly in code, it is unlikely that a user will accidentally assign an unspecified value to a descriptor using this type with accept_unknown.

class aioxmpp.xso.TextChildMap(xso_type)[source]

A type for use with xso.ChildValueMap and descendants of xso.AbstractTextChild.

This type performs the packing and unpacking of language-text-pairs to and from the xso_type. xso_type must have an interface compatible with xso.AbstractTextChild, which means that it must have the language and text at lang and text, respectively and support the same-named keyword arguments for those attributes at the constructor.

For an example see the source of aioxmpp.Message.

New in version 0.5.

Defining custom types

class aioxmpp.xso.AbstractCDataType[source]

Subclasses of this class describe character data types.

They are used to convert python values from (parse()) and to (format()) XML character data as well as enforce basic type restrictions (coerce()) when values are assigned to descriptors using this type.

This type can be used by the character data descriptors, like Attr and Text.

coerce(v)[source]

Force the given value v to be of the type represented by this AbstractCDataType.

coerce() is called when user code assigns values to descriptors which use the type; it is notably not called when values are extracted from SAX events, as these go through parse() and that is expected to return correctly typed values.

If v cannot be sensibly coerced, TypeError is raised (in some rare occasions, ValueError may be ok too).

Return a coerced version of v or v itself if it matches the required type.

Note

For the sake of usability, coercion should only take place rarely; in most of the cases, throwing TypeError is the preferred method.

Otherwise, a user might be surprised why the int they assigned to an attribute suddenly became a str.

abstract parse(v)[source]

Convert the given string v into a value of the appropriate type this class implements and return the result.

If conversion fails, ValueError is raised.

The result of parse() must pass through coerce() unchanged.

format(v)[source]

Convert the value v of the type this class implements to a str.

This conversion does not fail.

The returned value can be passed to parse() to obtain v.

class aioxmpp.xso.AbstractElementType[source]

Subclasses of this class describe XML subtree types.

They are used to convert python values from (unpack()) and to (pack()) XML subtrees represented as XSO instances as well as enforce basic type restrictions (coerce()) when values are assigned to descriptors using this type.

This type can be used by the element descriptors, like ChildValueList and ChildValueMap.

abstract get_xso_types()[source]

Return the XSO subclasses supported by this type.

Return type

Iterable of XMLStreamClass

Returns

The XSO subclasses which can be passed to unpack().

coerce(v)[source]

Force the given value v to be compatible to pack().

coerce() is called when user code assigns values to descriptors which use the type; it is notably not called when values are extracted from SAX events, as these go through unpack() and that is expected to return correctly typed values.

If v cannot be sensibly coerced, TypeError is raised (in some rare occasions, ValueError may be ok too).

Return a coerced version of v or v itself if it matches the required type.

Note

For the sake of usability, coercion should only take place rarely; in most of the cases, throwing TypeError is the preferred method.

Otherwise, a user might be surprised why the int they assigned to an attribute suddenly became a str.

abstract unpack(obj)[source]

Convert a XSO instance to another object, usually a scalar value or a tuple.

Parameters

obj (One of the types returned by get_xso_types().) – The object to unpack.

Raises

ValueError – if the conversaion fails.

Returns

The unpacked value.

Think of unpack like a high-level struct.unpack(): it converts wire-format data (XML subtrees represented as XSO instances) to python values.

abstract pack(v)[source]

Convert the value v of the type this class implements to an XSO instance.

Parameters

v (as returned by unpack()) – Value to pack

Return type

One of the types returned by get_xso_types().

Returns

The packed value.

The returned value can be passed through unpack() to obtain a value equal to v.

Think of pack like a high-level struct.pack(): it converts python values to wire-format (XML subtrees represented as XSO instances).

Validators

Validators validate the python values after they have been parsed from XML-sourced strings or even when being assigned to a descriptor attribute (depending on the choice in the validate argument).

They can be useful both for defending and rejecting incorrect input and to avoid producing incorrect output.

The basic validator interface

class aioxmpp.xso.AbstractValidator[source]

This is the interface all validators must implement. In addition, a validators documentation should clearly state on which types it operates.

validate(value)[source]

Return True if the value adheres to the restrictions imposed by this validator and False otherwise.

By default, this method calls validate_detailed() and returns True if validate_detailed() returned an empty result.

abstract validate_detailed(value)[source]

Return an empty list if the value adheres to the restrictions imposed by this validator.

If the value does not comply, return a list of UserValueError instances which each represent a condition which was violated in a human-readable way.

Implementations

class aioxmpp.xso.RestrictToSet(values)[source]

Restrict the possible values to the values from values. Operates on any types.

class aioxmpp.xso.Nmtoken[source]

Restrict the possible strings to the NMTOKEN specification of XML Schema Definitions. The validator only works with strings.

Warning

This validator is probably incorrect. It is a good first line of defense to avoid creating obvious incorrect output and should not be used as input validator.

It most likely falsely rejects valid values and may let through invalid values.

class aioxmpp.xso.IsInstance(valid_classes)[source]

This validator checks that the value is an instance of any of the classes given in valid_classes.

valid_classes is not copied into the IsInstance instance, but instead shared; it can be mutated after the construction of IsInstance to allow addition and removal of classes.

class aioxmpp.xso.NumericRange(min_=None, max_=None)[source]

To be used with orderable types, such as DateTime or Integer.

The value is enforced to be within [min, max] (this is the interval from min_ to max_, including both ends).

Setting min_ or max_ to None disables enforcement of that end of the interval. A common use is NumericRange(min_=1) in conjunction with Integer to enforce the use of positive integers.

New in version 0.6.

Querying data from XSOs

With XML, we have XPath as query language to retrieve data from XML trees. With XSOs, we have aioxmpp.xso.query, even though it’s not as powerful as XPath.

Syntactically, it’s oriented on XPath. Consider the following XSO classes:

class FooXSO(xso.XSO):
    TAG = (None, "foo")

    attr = xso.Attr(
        "attr"
    )


class BarXSO(xso.XSO):
    TAG = (None, "bar")

    child = xso.Child([
        FooXSO,
    ])


class BazXSO(FooXSO):
    TAG = (None, "baz")

    attr2 = xso.Attr(
        "attr2"
    )


class RootXSO(xso.XSO):
    TAG = (None, "root")

    children = xso.ChildList([
        FooXSO,
        BarXSO,
    ])

    attr = xso.Attr(
        "attr"
    )

To perform a query, we first need to set up a query.EvaluationContext:

root_xso = # a RootXSO instance
ec = xso.query.EvaluationContext()
ec.set_toplevel_object(root_xso)

Using the context, we can now execute queries:

# to find all FooXSO children of the RootXSO
ec.eval(RootXSO.children / FooXSO)

# to find all BarXSO children of the RootXSO
ec.eval(RootXSO.children / BarXSO)

# to find all FooXSO children of the RootXSO, where FooXSO.attr
# is set
ec.eval(RootXSO.children / FooXSO[where(FooXSO.attr)])

# to find all FooXSO children of the RootXSO, where FooXSO.attr
# is *not* set
ec.eval(RootXSO.children / FooXSO[where(not FooXSO.attr)])

# to find all FooXSO children of the RootXSO, where FooXSO.attr
# is set to "foobar"
ec.eval(RootXSO.children / FooXSO[where(FooXSO.attr == "foobar")])

# to test whether there is a FooXSO which has attr set to
# "foobar"
ec.eval(RootXSO.children / FooXSO.attr == "foobar")

# to find the first three FooXSO children where attr is set
ec.eval(RootXSO.children / FooXSO[where(FooXSO.attr)][:3])

The following operators are available in the aioxmpp.xso namespace:

class aioxmpp.xso.where(expr)[source]

Wrap the expression expr so that it can be used as a filter in [].

aioxmpp.xso.not_(expr)[source]

Return the boolean-not of the value of expr. A expression value is true if it contains at least one element and false otherwise.

See also

EvaluationContext.eval_bool()

which is used behind the scenes to calculate the boolean value of expr.

NotOp

which actually implements the operator.

The following need to be explicitly sourced from aioxmpp.xso.query, as they are rarely used directly in user code.

class aioxmpp.xso.query.EvaluationContext[source]

The evaluation context holds contextual information for the evaluation of a query expression.

Most notably, it provides the methods for acquiring and replacing the toplevel objects of classes:

get_toplevel_object()[source]

Return the toplevel object for the given class_. Only exact matches are returned.

set_toplevel_object()[source]

Set the toplevel object to return from get_toplevel_object() when asked for class_ to instance.

If class_ is None, the type() of the instance is used.

In addition, it provides shortcuts for evaluating expressions:

eval(expr)[source]

Evaluate the expression expr and return the result.

The result of an expression is always an iterable.

eval_bool(expr)[source]

Evaluate the expression expr and return the truthness of its result. A result of an expression is said to be true if it contains at least one value. It has the same semantics as bool() on sequences.s

Note

The implementation details of the query language are documented in the source. They are not useful unless you want to implement custom query operators, which is not possible without modifying the aioxmpp.xso.query source anyways.

Predefined XSO base classes

Some patterns reoccur when using this subpackage. For these, base classes are provided which facilitate the use.

class aioxmpp.xso.AbstractTextChild(*args, **kwargs)[source]

One of the recurring patterns when using xso is the use of a XSO subclass to represent an XML node which has only character data and an xml:lang attribute.

The text and lang arguments to the constructor can be used to initialize the attributes.

This class provides exactly that. It inherits from XSO.

lang

The xml:lang of the node, as LanguageTag.

text

The textual content of the node (XML character data).

Example use as base class:

class Subject(xso.AbstractTextChild):
    TAG = (namespaces.client, "subject")

The full example can also be found in the source code of stanza.Subject.