summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/api/exceptions.rst14
-rw-r--r--docs/create_plugin.rst2
-rw-r--r--docs/getting_started/iq.rst180
-rw-r--r--docs/handlersmatchers.rst2
-rw-r--r--docs/howto/stanzas.rst2
-rwxr-xr-xexamples/adhoc_provider.py4
-rwxr-xr-xexamples/adhoc_user.py4
-rwxr-xr-xexamples/disco_browser.py4
-rwxr-xr-xexamples/echo_client.py4
-rwxr-xr-xexamples/echo_component.py2
-rwxr-xr-xexamples/muc.py4
-rwxr-xr-xexamples/ping.py4
-rwxr-xr-xexamples/proxy_echo_client.py4
-rw-r--r--examples/roster_browser.py4
-rwxr-xr-xexamples/send_client.py4
-rw-r--r--sleekxmpp/stanza/message.py2
-rw-r--r--sleekxmpp/xmlstream/stanzabase.py2
17 files changed, 221 insertions, 21 deletions
diff --git a/docs/api/exceptions.rst b/docs/api/exceptions.rst
new file mode 100644
index 00000000..7bc72ce5
--- /dev/null
+++ b/docs/api/exceptions.rst
@@ -0,0 +1,14 @@
+Exceptions
+==========
+
+.. module:: sleekxmpp.exceptions
+
+
+.. autoexception:: XMPPError
+ :members:
+
+.. autoexception:: IqError
+ :members:
+
+.. autoexception:: IqTimeout
+ :members:
diff --git a/docs/create_plugin.rst b/docs/create_plugin.rst
index 112ef505..12efa84c 100644
--- a/docs/create_plugin.rst
+++ b/docs/create_plugin.rst
@@ -1,3 +1,5 @@
+.. _create-plugin:
+
Creating a SleekXMPP Plugin
===========================
diff --git a/docs/getting_started/iq.rst b/docs/getting_started/iq.rst
index 7ac1508c..98e0bdaf 100644
--- a/docs/getting_started/iq.rst
+++ b/docs/getting_started/iq.rst
@@ -1,2 +1,182 @@
Send/Receive IQ Stanzas
=======================
+
+Unlike :class:`~sleekxmpp.stanza.message.Message` and
+:class:`~sleekxmpp.stanza.presence.Presence` stanzas which only use
+text data for basic usage, :class:`~sleekxmpp.stanza.iq.Iq` stanzas
+require using XML payloads, and generally entail creating a new
+SleekXMPP plugin to provide the necessary convenience methods to
+make working with them easier.
+
+Basic Use
+---------
+
+XMPP's use of :class:`~sleekxmpp.stanza.iq.Iq` stanzas is built around
+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
+~~~~~~~~~~~~~~~~~~~
+
+SleekXMPP provides built-in support for creating basic :class:`~sleekxmpp.stanza.iq.Iq`
+stanzas this way. The relevant methods are:
+
+* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq`
+* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_get`
+* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_set`
+* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_result`
+* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_error`
+* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_query`
+
+These methods all follow the same pattern: create or modify an existing
+:class:`~sleekxmpp.stanza.iq.Iq` stanza, set the ``'type'`` value based
+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
+~~~~~~~~~~~~~~~~~~
+
+Once an :class:`~sleekxmpp.stanza.iq.Iq` stanza is created, sending it
+over the wire is done using its :meth:`~sleekxmpp.stanza.iq.Iq.send()`
+method, like any other stanza object. However, there are a few extra
+options to control how to wait for the query's response.
+
+These options are:
+
+* ``block``: The default behaviour is that :meth:`~sleekxmpp.stanza.iq.Iq.send()`
+ will block until a response is received and the response stanza will be the
+ return value. Setting ``block`` to ``False`` will cause the call to return
+ immediately. In which case, you will need to arrange some way to capture
+ the response stanza if you need it.
+
+* ``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
+
+ 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
+ whenever a response is finally received. Using this method, there
+ is no timeout limit. In case you need to remove the callback, the
+ name of the newly created callback is returned.
+
+ .. code-block:: python
+
+ cb_name = iq.send(callback=self.a_callback)
+
+ # ... later if we need to cancel
+ self.remove_handler(cb_name)
+
+Properly working with :class:`~sleekxmpp.stanza.iq.Iq` stanzas requires
+handling the intended, normal flow, error responses, and timed out
+requests. To make this easier, two exceptions may be thrown by
+:meth:`~sleekxmpp.stanza.iq.Iq.send()`: :exc:`~sleekxmpp.exceptions.IqError`
+and :exc:`~sleekxmpp.exceptions.IqTimeout`. These exceptions only
+apply to the default, blocking calls.
+
+.. code-block:: python
+
+ try:
+ resp = iq.send()
+ # ... 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
+can combine both cases with a generic :exc:`~sleekxmpp.exceptions.XMPPError`
+exception:
+
+.. code-block:: python
+
+ try:
+ resp = iq.send()
+ except XMPPError:
+ # ... Don't care about the response
+ pass
+
+Advanced Use
+------------
+
+Going beyond the basics provided by SleekXMPP requires building at least a
+rudimentary SleekXMPP plugin to create a :term:`stanza object` for
+interfacting with the :class:`~sleekxmpp.stanza.iq.Iq` payload.
+
+.. seealso::
+
+ * :ref:`create-plugin`
+ * :ref:`work-with-stanzas`
+ * :ref:`using-handlers-matchers`
+
+
+The typical way to respond to :class:`~sleekxmpp.stanza.iq.Iq` requests is
+to register stream handlers. As an example, suppose we create a stanza class
+named ``CustomXEP`` which uses the XML element ``<query xmlns="custom-xep" />``,
+and has a :attr:`~sleekxmpp.xmlstream.stanzabase.ElementBase.plugin_attrib` value
+of ``custom_xep``.
+
+There are two types of incoming :class:`~sleekxmpp.stanza.iq.Iq` requests:
+``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
+:class:`~sleekxmpp.xmlstream.matcher.stanzapath.StanzaPath` matcher.
+
+.. 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')
diff --git a/docs/handlersmatchers.rst b/docs/handlersmatchers.rst
index 7ac750c0..628c4142 100644
--- a/docs/handlersmatchers.rst
+++ b/docs/handlersmatchers.rst
@@ -1,2 +1,4 @@
+.. _using-handlers-matchers:
+
Using Stream Handlers and Matchers
==================================
diff --git a/docs/howto/stanzas.rst b/docs/howto/stanzas.rst
index 7ca7bbfd..d52a90d4 100644
--- a/docs/howto/stanzas.rst
+++ b/docs/howto/stanzas.rst
@@ -1,3 +1,5 @@
+.. _work-with-stanzas:
+
How to Work with Stanza Objects
===============================
diff --git a/examples/adhoc_provider.py b/examples/adhoc_provider.py
index 0a7905b8..4d4c3610 100755
--- a/examples/adhoc_provider.py
+++ b/examples/adhoc_provider.py
@@ -192,14 +192,14 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
- # If you do not have the pydns library installed, you will need
+ # If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
- xmpp.process(threaded=False)
+ xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")
diff --git a/examples/adhoc_user.py b/examples/adhoc_user.py
index ac157edd..0bc03c15 100755
--- a/examples/adhoc_user.py
+++ b/examples/adhoc_user.py
@@ -198,14 +198,14 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
- # If you do not have the pydns library installed, you will need
+ # If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
- xmpp.process(threaded=False)
+ xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")
diff --git a/examples/disco_browser.py b/examples/disco_browser.py
index 0526bfc4..6023dd7e 100755
--- a/examples/disco_browser.py
+++ b/examples/disco_browser.py
@@ -188,13 +188,13 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
- # If you do not have the pydns library installed, you will need
+ # If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
- xmpp.process(threaded=False)
+ xmpp.process(block=True)
else:
print("Unable to connect.")
diff --git a/examples/echo_client.py b/examples/echo_client.py
index 7e882a4a..cbb04683 100755
--- a/examples/echo_client.py
+++ b/examples/echo_client.py
@@ -132,14 +132,14 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
- # If you do not have the pydns library installed, you will need
+ # If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
- xmpp.process(threaded=False)
+ xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")
diff --git a/examples/echo_component.py b/examples/echo_component.py
index f569e001..d8bcd752 100755
--- a/examples/echo_component.py
+++ b/examples/echo_component.py
@@ -116,7 +116,7 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
- xmpp.process(threaded=False)
+ xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")
diff --git a/examples/muc.py b/examples/muc.py
index 96b5fb83..7af37449 100755
--- a/examples/muc.py
+++ b/examples/muc.py
@@ -175,14 +175,14 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
- # If you do not have the pydns library installed, you will need
+ # If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
- xmpp.process(threaded=False)
+ xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")
diff --git a/examples/ping.py b/examples/ping.py
index 462b8379..81194eef 100755
--- a/examples/ping.py
+++ b/examples/ping.py
@@ -129,14 +129,14 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
- # If you do not have the pydns library installed, you will need
+ # If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
- xmpp.process(threaded=False)
+ xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")
diff --git a/examples/proxy_echo_client.py b/examples/proxy_echo_client.py
index 3466dc9b..1f4ba9d3 100755
--- a/examples/proxy_echo_client.py
+++ b/examples/proxy_echo_client.py
@@ -156,14 +156,14 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
- # If you do not have the pydns library installed, you will need
+ # If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
- xmpp.process(threaded=False)
+ xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")
diff --git a/examples/roster_browser.py b/examples/roster_browser.py
index 4a58cc1c..7926b096 100644
--- a/examples/roster_browser.py
+++ b/examples/roster_browser.py
@@ -160,14 +160,14 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
- # If you do not have the pydns library installed, you will need
+ # If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
- xmpp.process(threaded=False)
+ xmpp.process(block=True)
else:
print("Unable to connect.")
diff --git a/examples/send_client.py b/examples/send_client.py
index d1dafee6..94bb584d 100755
--- a/examples/send_client.py
+++ b/examples/send_client.py
@@ -131,14 +131,14 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
- # If you do not have the pydns library installed, you will need
+ # If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
- xmpp.process(threaded=False)
+ xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")
diff --git a/sleekxmpp/stanza/message.py b/sleekxmpp/stanza/message.py
index 3518fc7a..19d4d9e2 100644
--- a/sleekxmpp/stanza/message.py
+++ b/sleekxmpp/stanza/message.py
@@ -79,7 +79,7 @@ class Message(RootStanza):
return self
def normal(self):
- """Set the message type to 'chat'."""
+ """Set the message type to 'normal'."""
self['type'] = 'normal'
return self
diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py
index 8678ca14..d1409706 100644
--- a/sleekxmpp/xmlstream/stanzabase.py
+++ b/sleekxmpp/xmlstream/stanzabase.py
@@ -167,7 +167,7 @@ class ElementBase(object):
#: For :class:`ElementBase` subclasses which are intended to be used
#: as plugins, the ``plugin_attrib`` value defines the plugin name.
#: Plugins may be accessed by using the ``plugin_attrib`` value as
- #: the interface. An example using ``plugin_attrib = 'foo'``:
+ #: the interface. An example using ``plugin_attrib = 'foo'``::
#:
#: register_stanza_plugin(Message, FooPlugin)
#: msg = Message()