slixmpp/docs/getting_started/iq.rst

186 lines
5.8 KiB
ReStructuredText
Raw Permalink Normal View History

Send/Receive IQ Stanzas
=======================
2011-12-31 06:28:41 +00:00
2014-07-17 12:19:04 +00:00
Unlike :class:`~slixmpp.stanza.message.Message` and
:class:`~slixmpp.stanza.presence.Presence` stanzas which only use
2021-02-03 22:17:37 +00:00
text data for basic usage, :class:`~slixmpp.stanza.Iq` stanzas
2011-12-31 06:28:41 +00:00
require using XML payloads, and generally entail creating a new
2014-07-17 12:19:04 +00:00
Slixmpp plugin to provide the necessary convenience methods to
2011-12-31 06:28:41 +00:00
make working with them easier.
Basic Use
---------
2021-02-03 22:17:37 +00:00
XMPP's use of :class:`~slixmpp.stanza.Iq` stanzas is built around
2011-12-31 06:28:41 +00:00
namespaced ``<query />`` elements. For clients, just sending the
empty ``<query />`` element will suffice for retrieving information. For
example, a very basic implementation of service discovery would just
need to be able to send:
.. code-block:: xml
<iq to="user@example.com" type="get" id="1">
<query xmlns="http://jabber.org/protocol/disco#info" />
</iq>
Creating Iq Stanzas
~~~~~~~~~~~~~~~~~~~
2021-02-03 22:17:37 +00:00
Slixmpp provides built-in support for creating basic :class:`~slixmpp.stanza.Iq`
2011-12-31 06:28:41 +00:00
stanzas this way. The relevant methods are:
2014-07-17 12:19:04 +00:00
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq`
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq_get`
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq_set`
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq_result`
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq_error`
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq_query`
2011-12-31 06:28:41 +00:00
2014-08-17 19:53:34 +00:00
These methods all follow the same pattern: create or modify an existing
2021-02-03 22:17:37 +00:00
:class:`~slixmpp.stanza.Iq` stanza, set the ``'type'`` value based
2011-12-31 06:28:41 +00:00
on the method name, and finally add a ``<query />`` element with the given
namespace. For example, to produce the query above, you would use:
.. code-block:: python
self.make_iq_get(queryxmlns='http://jabber.org/protocol/disco#info',
ito='user@example.com')
Sending Iq Stanzas
~~~~~~~~~~~~~~~~~~
2021-02-03 22:17:37 +00:00
Once an :class:`~slixmpp.stanza.Iq` stanza is created, sending it
over the wire is done using its :meth:`~slixmpp.stanza.Iq.send()`
2011-12-31 06:28:41 +00:00
method, like any other stanza object. However, there are a few extra
2021-02-03 22:17:37 +00:00
options to control how to wait for the query's response, as well as
how to handle the result.
2011-12-31 06:28:41 +00:00
2021-02-03 22:17:37 +00:00
:meth:`~slixmpp.stanza.Iq.send()` returns an :class:`~asyncio.Future`
object, which can be awaited on until a ``result`` is received.
2011-12-31 06:28:41 +00:00
2021-02-03 22:17:37 +00:00
These options are:
2011-12-31 06:28:41 +00:00
* ``timeout``: When using the blocking behaviour, the call will eventually
timeout with an error. The default timeout is 30 seconds, but this may
be overidden two ways. To change the timeout globally, set:
.. code-block:: python
self.response_timeout = 10
To change the timeout for a single call, the ``timeout`` parameter works:
.. code-block:: python
2014-08-17 19:53:34 +00:00
2011-12-31 06:28:41 +00:00
iq.send(timeout=60)
* ``callback``: When not using a blocking call, using the ``callback``
argument is a simple way to register a handler that will execute
2021-02-03 22:17:37 +00:00
whenever a response is finally received.
2011-12-31 06:28:41 +00:00
.. code-block:: python
2021-02-03 22:17:37 +00:00
iq.send(callback=self.a_callback)
* ``timeout_callback``: A callback to execute when the provided
``timeout`` is reached before an answer is received.
.. note::
2011-12-31 06:28:41 +00:00
2021-02-03 22:17:37 +00:00
Both ``callback`` and ``timeout_callback`` can be effectively
replaced using ``await``, and standard exception handling
(see below), which provide a more linear and readable workflow.
2011-12-31 06:28:41 +00:00
2021-02-03 22:17:37 +00:00
Properly working with :class:`~slixmpp.stanza.Iq` stanzas requires
2011-12-31 06:28:41 +00:00
handling the intended, normal flow, error responses, and timed out
requests. To make this easier, two exceptions may be thrown by
2021-02-03 22:17:37 +00:00
:meth:`~slixmpp.stanza.Iq.send()`: :exc:`~slixmpp.exceptions.IqError`
2014-07-17 12:19:04 +00:00
and :exc:`~slixmpp.exceptions.IqTimeout`. These exceptions only
2011-12-31 06:28:41 +00:00
apply to the default, blocking calls.
.. code-block:: python
try:
2021-02-03 22:17:37 +00:00
resp = await iq.send()
2011-12-31 06:28:41 +00:00
# ... do stuff with expected Iq result
except IqError as e:
err_resp = e.iq
# ... handle error case
except IqTimeout:
# ... no response received in time
pass
If you do not care to distinguish between errors and timeouts, then you
2014-07-17 12:19:04 +00:00
can combine both cases with a generic :exc:`~slixmpp.exceptions.XMPPError`
2011-12-31 06:28:41 +00:00
exception:
.. code-block:: python
try:
2021-02-03 22:17:37 +00:00
resp = await iq.send()
2011-12-31 06:28:41 +00:00
except XMPPError:
# ... Don't care about the response
pass
Advanced Use
------------
2014-07-17 12:19:04 +00:00
Going beyond the basics provided by Slixmpp requires building at least a
rudimentary Slixmpp plugin to create a :term:`stanza object` for
2021-02-03 22:17:37 +00:00
interfacting with the :class:`~slixmpp.stanza.Iq` payload.
2011-12-31 06:28:41 +00:00
.. seealso::
* :ref:`create-plugin`
* :ref:`work-with-stanzas`
* :ref:`using-handlers-matchers`
2014-08-17 19:53:34 +00:00
2011-12-31 06:28:41 +00:00
2021-02-03 22:17:37 +00:00
The typical way to respond to :class:`~slixmpp.stanza.Iq` requests is
2011-12-31 06:28:41 +00:00
to register stream handlers. As an example, suppose we create a stanza class
named ``CustomXEP`` which uses the XML element ``<query xmlns="custom-xep" />``,
2014-07-17 12:19:04 +00:00
and has a :attr:`~slixmpp.xmlstream.stanzabase.ElementBase.plugin_attrib` value
2011-12-31 06:28:41 +00:00
of ``custom_xep``.
2021-02-03 22:17:37 +00:00
There are two types of incoming :class:`~slixmpp.stanza.Iq` requests:
2011-12-31 06:28:41 +00:00
``get`` and ``set``. You can register a handler that will accept both and then
filter by type as needed, as so:
.. code-block:: python
self.register_handler(Callback(
'CustomXEP Handler',
StanzaPath('iq/custom_xep'),
self._handle_custom_iq))
# ...
def _handle_custom_iq(self, iq):
if iq['type'] == 'get':
# ...
pass
elif iq['type'] == 'set':
# ...
pass
else:
# ... This will capture error responses too
pass
If you want to filter out query types beforehand, you can adjust the matching
filter by using ``@type=get`` or ``@type=set`` if you are using the recommended
2014-07-17 12:19:04 +00:00
:class:`~slixmpp.xmlstream.matcher.stanzapath.StanzaPath` matcher.
2011-12-31 06:28:41 +00:00
.. code-block:: python
self.register_handler(Callback(
'CustomXEP Handler',
StanzaPath('iq@type=get/custom_xep'),
self._handle_custom_iq_get))
# ...
def _handle_custom_iq_get(self, iq):
assert(iq['type'] == 'get')