1.. _using_asyncio:
2
3=============
4Using asyncio
5=============
6
7Block on IQ sending
8~~~~~~~~~~~~~~~~~~~
9
10:meth:`.Iq.send` now returns a :class:`~.Future` so you can easily block with:
11
12.. code-block:: python
13
14    result = yield from iq.send()
15
16.. warning::
17
18    If the reply is an IQ with an ``error`` type, this will raise an
19    :class:`.IqError`, and if it timeouts, it will raise an
20    :class:`.IqTimeout`. Don't forget to catch it.
21
22You can still use callbacks instead.
23
24XEP plugin integration
25~~~~~~~~~~~~~~~~~~~~~~
26
27The same changes from the SleekXMPP API apply, so you can do:
28
29.. code-block:: python
30
31    iq_info = yield from self.xmpp['xep_0030'].get_info(jid)
32
33But the following will only return a Future:
34
35.. code-block:: python
36
37    iq_info = self.xmpp['xep_0030'].get_info(jid)
38
39
40Callbacks, Event Handlers, and Stream Handlers
41~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
42
43IQ callbacks and :term:`Event Handlers <event handler>` can be coroutine
44functions; in this case, they will be scheduled in the event loop using
45:meth:`.asyncio.async` and not ran immediately.
46
47A :class:`.CoroutineCallback` class has been added as well for
48:term:`Stream Handlers <stream handler>`, which will use
49:meth:`.asyncio.async` to schedule the callback.
50
51Running the event loop
52~~~~~~~~~~~~~~~~~~~~~~
53
54:meth:`.XMLStream.process` is only a thin wrapper on top of
55``loop.run_forever()`` (if ``timeout`` is provided then it will
56only run for this amount of time, and if ``forever`` is False it will
57run until disconnection).
58
59Therefore you can handle the event loop in any way you like
60instead of using ``process()``.
61
62
63Examples
64~~~~~~~~
65
66Blocking until the session is established
67-----------------------------------------
68
69This code blocks until the XMPP session is fully established, which
70can be useful to make sure external events aren’t triggering XMPP
71callbacks while everything is not ready.
72
73.. code-block:: python
74
75    import asyncio, slixmpp
76
77    client = slixmpp.ClientXMPP('jid@example', 'password')
78    client.connected_event = asyncio.Event()
79    callback = lambda _: client.connected_event.set()
80    client.add_event_handler('session_start', callback)
81    client.connect()
82    loop.run_until_complete(event.wait())
83    # do some other stuff before running the event loop, e.g.
84    # loop.run_until_complete(httpserver.init())
85    client.process()
86
87
88Use with other asyncio-based libraries
89--------------------------------------
90
91This code interfaces with aiohttp to retrieve two pages asynchronously
92when the session is established, and then send the HTML content inside
93a simple <message>.
94
95.. code-block:: python
96
97    import asyncio, aiohttp, slixmpp
98
99    @asyncio.coroutine
100    def get_pythonorg(event):
101        req = yield from aiohttp.request('get', 'http://www.python.org')
102        text = yield from req.text
103        client.send_message(mto='jid2@example', mbody=text)
104
105    @asyncio.coroutine
106    def get_asyncioorg(event):
107        req = yield from aiohttp.request('get', 'http://www.asyncio.org')
108        text = yield from req.text
109        client.send_message(mto='jid3@example', mbody=text)
110
111    client = slixmpp.ClientXMPP('jid@example', 'password')
112    client.add_event_handler('session_start', get_pythonorg)
113    client.add_event_handler('session_start', get_asyncioorg)
114    client.connect()
115    client.process()
116
117
118Blocking Iq
119-----------
120
121This client checks (via XEP-0092) the software used by every entity it
122receives a message from. After this, it sends a message to a specific
123JID indicating its findings.
124
125.. code-block:: python
126
127    import asyncio, slixmpp
128
129    class ExampleClient(slixmpp.ClientXMPP):
130        def __init__(self, *args, **kwargs):
131            slixmpp.ClientXMPP.__init__(self, *args, **kwargs)
132            self.register_plugin('xep_0092')
133            self.add_event_handler('message', self.on_message)
134
135        @asyncio.coroutine
136        def on_message(self, event):
137            # You should probably handle IqError and IqTimeout exceptions here
138            # but this is an example.
139            version = yield from self['xep_0092'].get_version(message['from'])
140            text = "%s sent me a message, he runs %s" % (message['from'],
141                                                         version['software_version']['name'])
142            self.send_message(mto='master@example.tld', mbody=text)
143
144    client = ExampleClient('jid@example', 'password')
145    client.connect()
146    client.process()
147
148
149