xso — Working with XML stream contents

This subpackage deals with XML Stream Objects. XSOs can be stanzas, but in general anything which is sent after the XML stream header.

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.

Terminology

Defenition 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.

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 SAX 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

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. scalar descriptor types always accept a value of None, which represents the absence of the object (unless it is required by some means, e.g. Attr(required=True)). Non-scalar descriptors generally have a different way to describe the absence and in addition have a mutable value. Assignment to the descriptor attribute is strictly type-checked.

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

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

The following arguments occur at several of the descriptor classes, and are all available at Attr.

Parameters:
  • type – An object which fulfills the type interface proposed by AbstractType. Usually, this is defaulted to a String instance.
  • validator – An object which has a validate() method. That method receives a value which was either assigned to the property (depending on the validate argument) or parsed from XML (after it passed through type_).
  • validate – A value from the ValidateMode enum, which defines which values have to pass through the validator. At some points it makes sense to only validate outgoing values, but be liberal with incoming values. This defaults to ValidateMode.FROM_RECV.
  • default – The value which the attribute has if no value has been assigned. This must be given to allow the attribute to be missing. It defaults to a special value. If the attribute has not been assigned to and default has not been set, accessing the attribute for reading raises AttributeError. An attribute with default value is not emitted in the output.
  • missing – A callable which takes a Context instance. It is called whenever the attribute is missing (independent from the fact whether it is required or not). The callable shall return a not-None value for the attribute to use. If the value is None, the usual handling of missing attributes takes place.

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)

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)

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

The LangAttr is identical to Attr, except that the type_, tag and missing arguments are already bound. 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])

When assigned to a class’ attribute, it collects any child which matches any XSO.TAG of the given classes.

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 valid value for the described attribute. Otherwise, a missing matching child is an error and the attribute cannot be set to None.

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)

Detect the object to instanciate 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)

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

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

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)
to_sax(instance, dest)
class aioxmpp.xso.ChildText(tag, *[, child_policy=UnknownChildPolicy.FAIL][, attr_policy=UnknownAttrPolicy.FAIL][, type_=xso.String()][, validator=None][, validate=ValidateMode.FROM_RECV][, default])

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

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

child_policy is applied when from_events() encounters an element in the child element of which it is supposed to extract text. Likewise, attr_policy is applied if an attribute is encountered on the element.

declare_prefix works as for ChildTag.

get_tag_map()

Return an iterable yielding tag.

This is for compatiblity with the Child interface.

from_events(instance, ev_args, ctx)

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)

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.Text(*[, type_=xso.String()][, validator=None][, validate=ValidateMode.FROM_RECV][, default])

When assigned to a class’ attribute, it collects all character data of the XML element.

Note that this destroys 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 and default arguments behave like in Attr.

from_value(instance, value)

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

to_sax(instance, dest)

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)

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)

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

to_sax(instance, dest)

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

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)

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

to_sax(instance, dest)

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)

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)

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_)

A list of values, generated by child tags.

This descriptor requires a single argument, the xso.AbstractType which implements the packing/unpacking of the native python values to XSOs.

The type_ must return a xso.XSO subclass from xso.AbstractType.get_formatted_type(). The parse() method receives the XSO as parsed from the events and is expected to return a non-XSO object. The format() method is called to prepare the values in the list for serialisation. It must return instances of get_formatted_type(), holding the value passed to format(). The XSO is then serialised as child of the object this descriptor is applied to.

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)

A mapping of keys to values, generated by child tags.

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.AbstractType.parse() and must accept such key-value pairs in xso.AbstractType.format().

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.ChildTextMap(xso_type)

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.

For an example, see stanza.Message.

class aioxmpp.xso.Collector

When assigned to a class’ attribute, it collects all children which are not known to any other descriptor into a list of XML subtrees.

The default is fixed at an empty list.

from_events(instance, ev_args, ctx)

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)

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[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 fucntion 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

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):
    nonlocal result
    result = value

parser = aioxmpp.xso.XSOParser()
parser.add_class(Message, catch_result)
sd = aioxmpp.xso.SAXDriver(parser)
lxml.sax.saxify(lmxl.etree.fromstring(
    "<message id='foo' from='bar' type='chat' />"
))

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

add_class(cls, callback)

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)

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()

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)

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()

Clean up all internal state.

Base and meta class

The XSO base class makes use of the 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

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

The documentation of xso.model.XMLStreamClass holds valuable information with respect to subclassing and modifying XSO subclasses, as well as restrictions on the use of the said attribute descriptors.

Note

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

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", required=True)
    from_ = aioxmpp.xso.Attr(tag="from", required=True)
    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()

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:

unparse_to_sax(dest)

The following class methods are provided by the metaclass:

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.

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()

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)

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

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

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 overriden by subclasses to make use of the captured events:

_set_captured_events(events)

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

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.

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

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

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

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

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

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)

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)

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)

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)

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.

Types

Types are used to convert strings obtained from XML character data or attribute contents to python types. They are valid values for type_ arguments e.g. for Attr.

The basic type interface

class aioxmpp.xso.AbstractType

This is the interface all types must implement.

get_formatted_type()

Return the type of the values returned by format().

The default implementation returns str. For AbstractType subclasses which are used for anything different than text, it might make sense to return a different type.

xso.Attr and xso.Text require that this function returns str.

New in version 0.5.

coerce(v)

Force the given value v to be of the type represented by this AbstractType. check() 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.

parse(v)

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 check().

format(v)

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

This conversion does not fail.

Attribute and text types

class aioxmpp.xso.String(prepfunc=None)

Interpret the input value as string.

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.Integer

Parse the value as base-10 integer and return the result as int.

class aioxmpp.xso.Bool

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)

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

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

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)

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

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

class aioxmpp.xso.JID

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

class aioxmpp.xso.ConnectionLocation

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

class aioxmpp.xso.LanguageTag

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.

Child list and map types

class aioxmpp.xso.TextChildMap(xso_type)

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 consturctor.

For an example see the source of aioxmpp.stanza.Message.

New in version 0.5.

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

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

validate(value)

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.

validate_detailed(value)

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)

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

class aioxmpp.xso.Nmtoken

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)

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.

Predefined XSO base classes

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

class aioxmpp.xso.AbstractTextChild(text=None, lang=None)[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.