stanzabase.rst 4.24 KB
Newer Older
Lance Stout's avatar
Lance Stout committed
1
2
.. _stanzabase:

3
4
5
==============
Stanza Objects
==============
Lance Stout's avatar
Lance Stout committed
6

louiz’'s avatar
louiz’ committed
7
.. module:: slixmpp.xmlstream.stanzabase
Lance Stout's avatar
Lance Stout committed
8

louiz’'s avatar
louiz’ committed
9
The :mod:`~slixmpp.xmlstream.stanzabase` module provides a wrapper for the
10
standard :mod:`~xml.etree.ElementTree` module that makes working with XML
Lance Stout's avatar
Lance Stout committed
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
less painful. Instead of having to manually move up and down an element
tree and insert subelements and attributes, you can interact with an object
that behaves like a normal dictionary or JSON object, which silently maps
keys to XML attributes and elements behind the scenes.

Overview
--------

The usefulness of this layer grows as the XML you have to work with
becomes nested. The base unit here, :class:`ElementBase`, can map to a
single XML element, or several depending on how advanced of a mapping
is desired from interface keys to XML structures. For example, a single
:class:`ElementBase` derived class could easily describe:

.. code-block:: xml

    <message to="user@example.com" from="friend@example.com">
      <body>Hi!</body>
      <x:extra>
        <x:item>Custom item 1</x:item>
        <x:item>Custom item 2</x:item>
        <x:item>Custom item 3</x:item>
      </x:extra>
    </message>

If that chunk of XML were put in the :class:`ElementBase` instance
``msg``, we could extract the data from the XML using::

    >>> msg['extra']
    ['Custom item 1', 'Custom item 2', 'Custom item 3']

Provided we set up the handler for the ``'extra'`` interface to load the
``<x:item>`` element content into a list.

The key concept is that given an XML structure that will be repeatedly
used, we can define a set of :term:`interfaces` which when we read from,
write to, or delete, will automatically manipulate the underlying XML
as needed. In addition, some of these interfaces may in turn reference
child objects which expose interfaces for particularly complex child
elements of the original XML chunk.

.. seealso::
    :ref:`create-stanza-interfaces`.

louiz’'s avatar
louiz’ committed
55
Because the :mod:`~slixmpp.xmlstream.stanzabase` module was developed
Lance Stout's avatar
Lance Stout committed
56
as part of an `XMPP <http://xmpp.org>`_ library, these chunks of XML are
louiz’'s avatar
louiz’ committed
57
referred to as :term:`stanzas <stanza>`, and in Slixmpp we refer to a
Lance Stout's avatar
Lance Stout committed
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
subclass of :class:`ElementBase` which defines the interfaces needed for
interacting with a given :term:`stanza` a :term:`stanza object`.

To make dealing with more complicated and nested :term:`stanzas <stanza>`
or XML chunks easier, :term:`stanza objects <stanza object>` can be
composed in two ways: as iterable child objects or as plugins. Iterable
child stanzas, or :term:`substanzas`, are accessible through a special
``'substanzas'`` interface. This option is useful for stanzas which
may contain more than one of the same kind of element. When there is
only one child element, the plugin method is more useful. For plugins,
a parent stanza object delegates one of its XML child elements to the
plugin stanza object. Here is an example:

.. code-block:: xml

    <iq type="result">
      <query xmlns="http://jabber.org/protocol/disco#info">
louiz’'s avatar
louiz’ committed
75
        <identity category="client" type="bot" name="Slixmpp Bot" />
Lance Stout's avatar
Lance Stout committed
76
77
78
79
80
81
82
83
84
85
86
      </query>
    </iq>

We can can arrange this stanza into two objects: an outer, wrapper object for
dealing with the ``<iq />`` element and its attributes, and a plugin object to
control the ``<query />`` payload element. If we give the plugin object the
name ``'disco_info'`` (using its :attr:`ElementBase.plugin_attrib` value), then
we can access the plugin as so::

    >>> iq['disco_info']
    '<query xmlns="http://jabber.org/protocol/disco#info">
louiz’'s avatar
louiz’ committed
87
      <identity category="client" type="bot" name="Slixmpp Bot" />
Lance Stout's avatar
Lance Stout committed
88
89
90
91
92
    </query>'

We can then drill down through the plugin object's interfaces as desired::

    >>> iq['disco_info']['identities']
louiz’'s avatar
louiz’ committed
93
    [('client', 'bot', 'Slixmpp Bot')]
Lance Stout's avatar
Lance Stout committed
94
95
96
97
98
99
100

Plugins may also add new interfaces to the parent stanza object as if they
had been defined by the parent directly, and can also override the behaviour
of an interface defined by the parent.

.. seealso::

Lance Stout's avatar
Lance Stout committed
101
102
103
    - :ref:`create-stanza-plugins`
    - :ref:`create-extension-plugins`
    - :ref:`override-parent-interfaces`
Lance Stout's avatar
Lance Stout committed
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
     

Registering Stanza Plugins
--------------------------

.. autofunction:: register_stanza_plugin

ElementBase
-----------

.. autoclass:: ElementBase
    :members:
    :private-members:
    :special-members:

StanzaBase
----------

.. autoclass:: StanzaBase
    :members: