summaryrefslogtreecommitdiff
path: root/docs/using_asyncio.rst
blob: 55ed76795c963c3e5264a0def01aa5513863548a (plain)
1
2
3
4
5
6
7
8
9
10
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
.. _using_asyncio:

=============
Using asyncio
=============

Block on IQ sending
~~~~~~~~~~~~~~~~~~~

:meth:`.Iq.send` now returns a :class:`~.Future` so you can easily block with:

.. code-block:: python

    result = yield from iq.send()

.. warning::

    If the reply is an IQ with an ``error`` type, this will raise an
    :class:`.IqError`, and if it timeouts, it will raise an
    :class:`.IqTimeout`. Don't forget to catch it.

You can still use callbacks instead.

XEP plugin integration
~~~~~~~~~~~~~~~~~~~~~~

The same changes from the SleekXMPP API apply, so you can do:

.. code-block:: python

    iq_info = yield from self.xmpp['xep_0030'].get_info(jid)

But the following will only return a Future:

.. code-block:: python

    iq_info = self.xmpp['xep_0030'].get_info(jid)


Callbacks, Event Handlers, and Stream Handlers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

IQ callbacks and :term:`Event Handlers <event handler>` can be coroutine
functions; in this case, they will be scheduled in the event loop using
:meth:`.asyncio.async` and not ran immediately.

A :class:`.CoroutineCallback` class has been added as well for
:term:`Stream Handlers <stream handler>`, which will use
:meth:`.asyncio.async` to schedule the callback.

Running the event loop
~~~~~~~~~~~~~~~~~~~~~~

:meth:`.XMLStream.process` is only a thin wrapper on top of
``loop.run_forever()`` (if ``timeout`` is provided then it will
only run for this amount of time, and if ``forever`` is False it will
run until disconnection).

Therefore you can handle the event loop in any way you like
instead of using ``process()``.


Examples
~~~~~~~~

Blocking until the session is established
-----------------------------------------

This code blocks until the XMPP session is fully established, which
can be useful to make sure external events aren’t triggering XMPP
callbacks while everything is not ready.

.. code-block:: python

    import asyncio, slixmpp

    client = slixmpp.ClientXMPP('jid@example', 'password')
    client.connected_event = asyncio.Event()
    callback = lambda _: client.connected_event.set()
    client.add_event_handler('session_start', callback)
    client.connect()
    loop.run_until_complete(event.wait())
    # do some other stuff before running the event loop, e.g.
    # loop.run_until_complete(httpserver.init())
    client.process()


Use with other asyncio-based libraries
--------------------------------------

This code interfaces with aiohttp to retrieve two pages asynchronously
when the session is established, and then send the HTML content inside
a simple <message>.

.. code-block:: python

    import asyncio, aiohttp, slixmpp

    @asyncio.coroutine
    def get_pythonorg(event):
        req = yield from aiohttp.request('get', 'http://www.python.org')
        text = yield from req.text
        client.send_message(mto='jid2@example', mbody=text)

    @asyncio.coroutine
    def get_asyncioorg(event):
        req = yield from aiohttp.request('get', 'http://www.asyncio.org')
        text = yield from req.text
        client.send_message(mto='jid3@example', mbody=text)

    client = slixmpp.ClientXMPP('jid@example', 'password')
    client.add_event_handler('session_start', get_pythonorg)
    client.add_event_handler('session_start', get_asyncioorg)
    client.connect()
    client.process()


Blocking Iq
-----------

This client checks (via XEP-0092) the software used by every entity it
receives a message from. After this, it sends a message to a specific
JID indicating its findings.

.. code-block:: python

    import asyncio, slixmpp

    class ExampleClient(slixmpp.ClientXMPP):
        def __init__(self, *args, **kwargs):
            slixmpp.ClientXMPP.__init__(self, *args, **kwargs)
            self.register_plugin('xep_0092')
            self.add_event_handler('message', self.on_message)

        @asyncio.coroutine
        def on_message(self, event):
            # You should probably handle IqError and IqTimeout exceptions here
            # but this is an example.
            version = yield from self['xep_0092'].get_version(message['from'])
            text = "%s sent me a message, he runs %s" % (message['from'],
                                                         version['software_version']['name'])
            self.send_message(mto='master@example.tld', mbody=text)

    client = ExampleClient('jid@example', 'password')
    client.connect()
    client.process()