Merge branch 'block-threaded-examples-docs' into 'master'

Remove the remaining block and threaded from examples

See merge request poezio/slixmpp!105
This commit is contained in:
mathieui 2021-01-27 00:20:31 +01:00
commit 370abb1d98
5 changed files with 68 additions and 72 deletions

View file

@ -18,7 +18,7 @@ messages sent to it. We will also go through adding some basic command line conf
for enabling or disabling debug log outputs and setting the username and password for enabling or disabling debug log outputs and setting the username and password
for the bot. for the bot.
For the command line options processing, we will use the built-in ``optparse`` For the command line options processing, we will use the built-in ``argparse``
module and the ``getpass`` module for reading in passwords. module and the ``getpass`` module for reading in passwords.
TL;DR Just Give Me the Code TL;DR Just Give Me the Code
@ -39,7 +39,8 @@ To get started, here is a brief outline of the structure that the final project
import asyncio import asyncio
import logging import logging
import getpass import getpass
from optparse import OptionParser
from argparse import ArgumentParser
import slixmpp import slixmpp
@ -93,9 +94,9 @@ we also need to define the ``self.start`` handler.
.. code-block:: python .. code-block:: python
def start(self, event): async def start(self, event):
self.send_presence() self.send_presence()
self.get_roster() await self.get_roster()
.. warning:: .. warning::
@ -144,6 +145,11 @@ The XMPP stanzas from the roster retrieval process could look like this:
</query> </query>
</iq> </iq>
Additionally, since :meth:`get_roster <slixmpp.clientxmpp.ClientXMPP.get_roster>` is using
``<iq/>`` stanzas, which will always receive an answer, it should be awaited on, to keep
a synchronous flow.
Responding to Messages Responding to Messages
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
Now that an ``EchoBot`` instance handles :term:`session_start`, we can begin receiving and Now that an ``EchoBot`` instance handles :term:`session_start`, we can begin receiving and
@ -212,8 +218,7 @@ Command Line Arguments and Logging
While this isn't part of Slixmpp itself, we do want our echo bot program to be able While this isn't part of Slixmpp itself, we do want our echo bot program to be able
to accept a JID and password from the command line instead of hard coding them. We will to accept a JID and password from the command line instead of hard coding them. We will
use the ``optparse`` module for this, though there are several alternative methods, including use the ``argparse`` module for this.
the newer ``argparse`` module.
We want to accept three parameters: the JID for the echo bot, its password, and a flag for We want to accept three parameters: the JID for the echo bot, its password, and a flag for
displaying the debugging logs. We also want these to be optional parameters, since passing displaying the debugging logs. We also want these to be optional parameters, since passing
@ -222,22 +227,29 @@ a password directly through the command line can be a security risk.
.. code-block:: python .. code-block:: python
if __name__ == '__main__': if __name__ == '__main__':
optp = OptionParser() # Setup the command line arguments.
parser = ArgumentParser(description=EchoBot.__doc__)
optp.add_option('-d', '--debug', help='set logging to DEBUG', # Output verbosity options.
action='store_const', dest='loglevel', parser.add_argument("-q", "--quiet", help="set logging to ERROR",
const=logging.DEBUG, default=logging.INFO) action="store_const", dest="loglevel",
optp.add_option("-j", "--jid", dest="jid", const=logging.ERROR, default=logging.INFO)
help="JID to use") parser.add_argument("-d", "--debug", help="set logging to DEBUG",
optp.add_option("-p", "--password", dest="password", action="store_const", dest="loglevel",
help="password to use") const=logging.DEBUG, default=logging.INFO)
opts, args = optp.parse_args() # JID and password options.
parser.add_argument("-j", "--jid", dest="jid",
help="JID to use")
parser.add_argument("-p", "--password", dest="password",
help="password to use")
if opts.jid is None: args = parser.parse_args()
opts.jid = raw_input("Username: ")
if opts.password is None: if args.jid is None:
opts.password = getpass.getpass("Password: ") args.jid = input("Username: ")
if args.password is None:
args.password = getpass("Password: ")
Since we included a flag for enabling debugging logs, we need to configure the Since we included a flag for enabling debugging logs, we need to configure the
``logging`` module to behave accordingly. ``logging`` module to behave accordingly.
@ -248,7 +260,7 @@ Since we included a flag for enabling debugging logs, we need to configure the
# .. option parsing from above .. # .. option parsing from above ..
logging.basicConfig(level=opts.loglevel, logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
@ -276,52 +288,36 @@ at this stage. For example, let's say we want our bot to support `service discov
If the ``EchoBot`` class had a hard dependency on a plugin, we could register that plugin in If the ``EchoBot`` class had a hard dependency on a plugin, we could register that plugin in
the ``EchoBot.__init__`` method instead. the ``EchoBot.__init__`` method instead.
.. note::
If you are using the OpenFire server, you will need to include an additional
configuration step. OpenFire supports a different version of SSL than what
most servers and Slixmpp support.
.. code-block:: python
import ssl
xmpp.ssl_version = ssl.PROTOCOL_SSLv3
Now we're ready to connect and begin echoing messages. If you have the package Now we're ready to connect and begin echoing messages. If you have the package
``aiodns`` installed, then the :meth:`slixmpp.clientxmpp.ClientXMPP` method ``aiodns`` installed, then the :meth:`slixmpp.clientxmpp.ClientXMPP.connect` method
will perform a DNS query to find the appropriate server to connect to for the will perform a DNS query to find the appropriate server to connect to for the
given JID. If you do not have ``aiodns``, then Slixmpp will attempt to given JID. If you do not have ``aiodns``, then Slixmpp will attempt to
connect to the hostname used by the JID, unless an address tuple is supplied connect to the hostname used by the JID, unless an address tuple is supplied
to :meth:`slixmpp.clientxmpp.ClientXMPP`. to :meth:`slixmpp.clientxmpp.ClientXMPP.connect`.
.. code-block:: python .. code-block:: python
if __name__ == '__main__': if __name__ == '__main__':
# .. option parsing & echo bot configuration # .. option parsing & echo bot configuration
xmpp.connect():
xmpp.process(forever=True)
if xmpp.connect():
xmpp.process(block=True)
else:
print('Unable to connect')
To begin responding to messages, you'll see we called :meth:`slixmpp.basexmpp.BaseXMPP.process` The :meth:`slixmpp.basexmpp.BaseXMPP.connect` will only schedule a connection
which will start the event handling, send queue, and XML reader threads. It will also call asynchronously. To actually connect, you need to let the event loop take over.
the :meth:`slixmpp.plugins.base.BasePlugin.post_init` method on all registered plugins. By This is done with the :meth:`slixmpp.basexmpp.BaseXMPP.process` method,
passing ``block=True`` to :meth:`slixmpp.basexmpp.BaseXMPP.process` we are running the which can either run forever (``forever=True``, the default), run for a (maximum)
main processing loop in the main thread of execution. The :meth:`slixmpp.basexmpp.BaseXMPP.process` duration of time (``timeout=n``), and/or run until it gets disconnected (``forever=False``).
call will not return until after Slixmpp disconnects. If you need to run the client in the background
for another program, use ``block=False`` to spawn the processing loop in its own thread. However, calling ``process()`` is not required if you already have an event loop
running, so you can handle the logic around it however you like.
.. note:: .. note::
Before 1.0, controlling the blocking behaviour of :meth:`slixmpp.basexmpp.BaseXMPP.process` was Before slixmpp, :meth:slixmpp.basexmpp.BaseXMPP.process` took ``block`` and ``threaded``
done via the ``threaded`` argument. This arrangement was a source of confusion because some users arguments. These do not make sense anymore and have been removed. Slixmpp does not use
interpreted that as controlling whether or not Slixmpp used threads at all, instead of how threads at all.
the processing loop itself was spawned.
The statements ``xmpp.process(threaded=False)`` and ``xmpp.process(block=True)`` are equivalent.
.. _echobot_complete: .. _echobot_complete:

View file

@ -31,23 +31,23 @@ for the JID that will receive our message, and the string content of the message
self.add_event_handler('session_start', self.start) self.add_event_handler('session_start', self.start)
def start(self, event): async def start(self, event):
self.send_presence() self.send_presence()
self.get_roster() await self.get_roster()
Note that as in :ref:`echobot`, we need to include send an initial presence and request Note that as in :ref:`echobot`, we need to include send an initial presence and request
the roster. Next, we want to send our message, and to do that we will use :meth:`send_message <slixmpp.basexmpp.BaseXMPP.send_message>`. the roster. Next, we want to send our message, and to do that we will use :meth:`send_message <slixmpp.basexmpp.BaseXMPP.send_message>`.
.. code-block:: python .. code-block:: python
def start(self, event): async def start(self, event):
self.send_presence() self.send_presence()
self.get_roster() await self.get_roster()
self.send_message(mto=self.recipient, mbody=self.msg) self.send_message(mto=self.recipient, mbody=self.msg)
Finally, we need to disconnect the client using :meth:`disconnect <slixmpp.xmlstream.XMLStream.disconnect>`. Finally, we need to disconnect the client using :meth:`disconnect <slixmpp.xmlstream.XMLStream.disconnect>`.
Now, sent stanzas are placed in a queue to pass them to the send thread. Now, sent stanzas are placed in a queue to pass them to the send routine.
:meth:`disconnect <slixmpp.xmlstream.XMLStream.disconnect>` by default will wait for an :meth:`disconnect <slixmpp.xmlstream.XMLStream.disconnect>` by default will wait for an
acknowledgement from the server for at least `2.0` seconds. This time is configurable with acknowledgement from the server for at least `2.0` seconds. This time is configurable with
the `wait` parameter. If `0.0` is passed for `wait`, :meth:`disconnect the `wait` parameter. If `0.0` is passed for `wait`, :meth:`disconnect
@ -55,9 +55,9 @@ the `wait` parameter. If `0.0` is passed for `wait`, :meth:`disconnect
.. code-block:: python .. code-block:: python
def start(self, event): async def start(self, event):
self.send_presence() self.send_presence()
self.get_roster() await self.get_roster()
self.send_message(mto=self.recipient, mbody=self.msg) self.send_message(mto=self.recipient, mbody=self.msg)

View file

@ -9,10 +9,8 @@ Glossary
stream handler stream handler
A callback function that accepts stanza objects pulled directly A callback function that accepts stanza objects pulled directly
from the XML stream. A stream handler is encapsulated in a from the XML stream. A stream handler is encapsulated in a
object that includes a :class:`Matcher <.MatcherBase>` object, and object that includes a :class:`Matcher <.MatcherBase>` object
which provides additional semantics. For example, the which provides additional semantics.
:class:`.Waiter` handler wrapper blocks thread execution until a
matching stanza is received.
event handler event handler
A callback function that responds to events raised by A callback function that responds to events raised by

View file

@ -168,13 +168,13 @@ if __name__ == '__main__':
xmpp.beClientOrServer(server=True) xmpp.beClientOrServer(server=True)
while not(xmpp.testForRelease()): while not(xmpp.testForRelease()):
xmpp.connect() xmpp.connect()
xmpp.process(block=True) xmpp.process(forever=False)
logging.debug("lost connection") logging.debug("lost connection")
if args.sensorjid: if args.sensorjid:
logging.debug("will try to call another device for data") logging.debug("will try to call another device for data")
xmpp.beClientOrServer(server=False,clientJID=args.sensorjid) xmpp.beClientOrServer(server=False,clientJID=args.sensorjid)
xmpp.connect() xmpp.connect()
xmpp.process(block=True) xmpp.process(forever=False)
logging.debug("ready ending") logging.debug("ready ending")
else: else:

View file

@ -73,21 +73,21 @@ old_xmpp = slixmpp.ClientXMPP(args.old_jid, args.old_password)
roster = [] roster = []
def on_session(event): async def on_session(event):
roster.append(old_xmpp.get_roster()) roster.append(await old_xmpp.get_roster())
old_xmpp.disconnect() old_xmpp.disconnect()
old_xmpp.add_event_handler('session_start', on_session) old_xmpp.add_event_handler('session_start', on_session)
if old_xmpp.connect(): if old_xmpp.connect():
old_xmpp.process(block=True) old_xmpp.process(forever=False)
if not roster: if not roster:
print('No roster to migrate') print('No roster to migrate')
sys.exit() sys.exit()
new_xmpp = slixmpp.ClientXMPP(args.new_jid, args.new_password) new_xmpp = slixmpp.ClientXMPP(args.new_jid, args.new_password)
def on_session2(event): async def on_session2(event):
new_xmpp.get_roster() await new_xmpp.get_roster()
new_xmpp.send_presence() new_xmpp.send_presence()
logging.info(roster[0]) logging.info(roster[0])
@ -97,9 +97,11 @@ def on_session2(event):
for jid, item in data.items(): for jid, item in data.items():
if item['subscription'] != 'none': if item['subscription'] != 'none':
new_xmpp.send_presence(ptype='subscribe', pto=jid) new_xmpp.send_presence(ptype='subscribe', pto=jid)
new_xmpp.update_roster(jid, await new_xmpp.update_roster(
name = item['name'], jid,
groups = item['groups']) name=item['name'],
groups=item['groups']
)
new_xmpp.disconnect() new_xmpp.disconnect()
new_xmpp.add_event_handler('session_start', on_session2) new_xmpp.add_event_handler('session_start', on_session2)