mirror of
https://github.com/correl/SleekXMPP.git
synced 2024-11-24 03:00:15 +00:00
Merge branch 'develop' into develop-1.1
This commit is contained in:
commit
e0545bf0bc
17 changed files with 221 additions and 21 deletions
14
docs/api/exceptions.rst
Normal file
14
docs/api/exceptions.rst
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
Exceptions
|
||||||
|
==========
|
||||||
|
|
||||||
|
.. module:: sleekxmpp.exceptions
|
||||||
|
|
||||||
|
|
||||||
|
.. autoexception:: XMPPError
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoexception:: IqError
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoexception:: IqTimeout
|
||||||
|
:members:
|
|
@ -1,3 +1,5 @@
|
||||||
|
.. _create-plugin:
|
||||||
|
|
||||||
Creating a SleekXMPP Plugin
|
Creating a SleekXMPP Plugin
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,182 @@
|
||||||
Send/Receive IQ Stanzas
|
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')
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
|
.. _using-handlers-matchers:
|
||||||
|
|
||||||
Using Stream Handlers and Matchers
|
Using Stream Handlers and Matchers
|
||||||
==================================
|
==================================
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
.. _work-with-stanzas:
|
||||||
|
|
||||||
How to Work with Stanza Objects
|
How to Work with Stanza Objects
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
|
|
|
@ -192,14 +192,14 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
# Connect to the XMPP server and start processing XMPP stanzas.
|
# Connect to the XMPP server and start processing XMPP stanzas.
|
||||||
if xmpp.connect():
|
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
|
# 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
|
# the one in the JID. For example, to use Google Talk you would
|
||||||
# need to use:
|
# need to use:
|
||||||
#
|
#
|
||||||
# if xmpp.connect(('talk.google.com', 5222)):
|
# if xmpp.connect(('talk.google.com', 5222)):
|
||||||
# ...
|
# ...
|
||||||
xmpp.process(threaded=False)
|
xmpp.process(block=True)
|
||||||
print("Done")
|
print("Done")
|
||||||
else:
|
else:
|
||||||
print("Unable to connect.")
|
print("Unable to connect.")
|
||||||
|
|
|
@ -198,14 +198,14 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
# Connect to the XMPP server and start processing XMPP stanzas.
|
# Connect to the XMPP server and start processing XMPP stanzas.
|
||||||
if xmpp.connect():
|
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
|
# 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
|
# the one in the JID. For example, to use Google Talk you would
|
||||||
# need to use:
|
# need to use:
|
||||||
#
|
#
|
||||||
# if xmpp.connect(('talk.google.com', 5222)):
|
# if xmpp.connect(('talk.google.com', 5222)):
|
||||||
# ...
|
# ...
|
||||||
xmpp.process(threaded=False)
|
xmpp.process(block=True)
|
||||||
print("Done")
|
print("Done")
|
||||||
else:
|
else:
|
||||||
print("Unable to connect.")
|
print("Unable to connect.")
|
||||||
|
|
|
@ -188,13 +188,13 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
# Connect to the XMPP server and start processing XMPP stanzas.
|
# Connect to the XMPP server and start processing XMPP stanzas.
|
||||||
if xmpp.connect():
|
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
|
# 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
|
# the one in the JID. For example, to use Google Talk you would
|
||||||
# need to use:
|
# need to use:
|
||||||
#
|
#
|
||||||
# if xmpp.connect(('talk.google.com', 5222)):
|
# if xmpp.connect(('talk.google.com', 5222)):
|
||||||
# ...
|
# ...
|
||||||
xmpp.process(threaded=False)
|
xmpp.process(block=True)
|
||||||
else:
|
else:
|
||||||
print("Unable to connect.")
|
print("Unable to connect.")
|
||||||
|
|
|
@ -132,14 +132,14 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
# Connect to the XMPP server and start processing XMPP stanzas.
|
# Connect to the XMPP server and start processing XMPP stanzas.
|
||||||
if xmpp.connect():
|
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
|
# 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
|
# the one in the JID. For example, to use Google Talk you would
|
||||||
# need to use:
|
# need to use:
|
||||||
#
|
#
|
||||||
# if xmpp.connect(('talk.google.com', 5222)):
|
# if xmpp.connect(('talk.google.com', 5222)):
|
||||||
# ...
|
# ...
|
||||||
xmpp.process(threaded=False)
|
xmpp.process(block=True)
|
||||||
print("Done")
|
print("Done")
|
||||||
else:
|
else:
|
||||||
print("Unable to connect.")
|
print("Unable to connect.")
|
||||||
|
|
|
@ -116,7 +116,7 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
# Connect to the XMPP server and start processing XMPP stanzas.
|
# Connect to the XMPP server and start processing XMPP stanzas.
|
||||||
if xmpp.connect():
|
if xmpp.connect():
|
||||||
xmpp.process(threaded=False)
|
xmpp.process(block=True)
|
||||||
print("Done")
|
print("Done")
|
||||||
else:
|
else:
|
||||||
print("Unable to connect.")
|
print("Unable to connect.")
|
||||||
|
|
|
@ -175,14 +175,14 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
# Connect to the XMPP server and start processing XMPP stanzas.
|
# Connect to the XMPP server and start processing XMPP stanzas.
|
||||||
if xmpp.connect():
|
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
|
# 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
|
# the one in the JID. For example, to use Google Talk you would
|
||||||
# need to use:
|
# need to use:
|
||||||
#
|
#
|
||||||
# if xmpp.connect(('talk.google.com', 5222)):
|
# if xmpp.connect(('talk.google.com', 5222)):
|
||||||
# ...
|
# ...
|
||||||
xmpp.process(threaded=False)
|
xmpp.process(block=True)
|
||||||
print("Done")
|
print("Done")
|
||||||
else:
|
else:
|
||||||
print("Unable to connect.")
|
print("Unable to connect.")
|
||||||
|
|
|
@ -129,14 +129,14 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
# Connect to the XMPP server and start processing XMPP stanzas.
|
# Connect to the XMPP server and start processing XMPP stanzas.
|
||||||
if xmpp.connect():
|
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
|
# 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
|
# the one in the JID. For example, to use Google Talk you would
|
||||||
# need to use:
|
# need to use:
|
||||||
#
|
#
|
||||||
# if xmpp.connect(('talk.google.com', 5222)):
|
# if xmpp.connect(('talk.google.com', 5222)):
|
||||||
# ...
|
# ...
|
||||||
xmpp.process(threaded=False)
|
xmpp.process(block=True)
|
||||||
print("Done")
|
print("Done")
|
||||||
else:
|
else:
|
||||||
print("Unable to connect.")
|
print("Unable to connect.")
|
||||||
|
|
|
@ -156,14 +156,14 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
# Connect to the XMPP server and start processing XMPP stanzas.
|
# Connect to the XMPP server and start processing XMPP stanzas.
|
||||||
if xmpp.connect():
|
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
|
# 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
|
# the one in the JID. For example, to use Google Talk you would
|
||||||
# need to use:
|
# need to use:
|
||||||
#
|
#
|
||||||
# if xmpp.connect(('talk.google.com', 5222)):
|
# if xmpp.connect(('talk.google.com', 5222)):
|
||||||
# ...
|
# ...
|
||||||
xmpp.process(threaded=False)
|
xmpp.process(block=True)
|
||||||
print("Done")
|
print("Done")
|
||||||
else:
|
else:
|
||||||
print("Unable to connect.")
|
print("Unable to connect.")
|
||||||
|
|
|
@ -160,14 +160,14 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
# Connect to the XMPP server and start processing XMPP stanzas.
|
# Connect to the XMPP server and start processing XMPP stanzas.
|
||||||
if xmpp.connect():
|
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
|
# 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
|
# the one in the JID. For example, to use Google Talk you would
|
||||||
# need to use:
|
# need to use:
|
||||||
#
|
#
|
||||||
# if xmpp.connect(('talk.google.com', 5222)):
|
# if xmpp.connect(('talk.google.com', 5222)):
|
||||||
# ...
|
# ...
|
||||||
xmpp.process(threaded=False)
|
xmpp.process(block=True)
|
||||||
else:
|
else:
|
||||||
print("Unable to connect.")
|
print("Unable to connect.")
|
||||||
|
|
||||||
|
|
|
@ -131,14 +131,14 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
# Connect to the XMPP server and start processing XMPP stanzas.
|
# Connect to the XMPP server and start processing XMPP stanzas.
|
||||||
if xmpp.connect():
|
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
|
# 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
|
# the one in the JID. For example, to use Google Talk you would
|
||||||
# need to use:
|
# need to use:
|
||||||
#
|
#
|
||||||
# if xmpp.connect(('talk.google.com', 5222)):
|
# if xmpp.connect(('talk.google.com', 5222)):
|
||||||
# ...
|
# ...
|
||||||
xmpp.process(threaded=False)
|
xmpp.process(block=True)
|
||||||
print("Done")
|
print("Done")
|
||||||
else:
|
else:
|
||||||
print("Unable to connect.")
|
print("Unable to connect.")
|
||||||
|
|
|
@ -79,7 +79,7 @@ class Message(RootStanza):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def normal(self):
|
def normal(self):
|
||||||
"""Set the message type to 'chat'."""
|
"""Set the message type to 'normal'."""
|
||||||
self['type'] = 'normal'
|
self['type'] = 'normal'
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
|
@ -167,7 +167,7 @@ class ElementBase(object):
|
||||||
#: For :class:`ElementBase` subclasses which are intended to be used
|
#: For :class:`ElementBase` subclasses which are intended to be used
|
||||||
#: as plugins, the ``plugin_attrib`` value defines the plugin name.
|
#: as plugins, the ``plugin_attrib`` value defines the plugin name.
|
||||||
#: Plugins may be accessed by using the ``plugin_attrib`` value as
|
#: 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)
|
#: register_stanza_plugin(Message, FooPlugin)
|
||||||
#: msg = Message()
|
#: msg = Message()
|
||||||
|
|
Loading…
Reference in a new issue