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 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.
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 logging
import getpass
from optparse import OptionParser
from argparse import ArgumentParser
import slixmpp
@ -93,9 +94,9 @@ we also need to define the ``self.start`` handler.
.. code-block:: python
def start(self, event):
async def start(self, event):
self.send_presence()
self.get_roster()
await self.get_roster()
.. warning::
@ -144,6 +145,11 @@ The XMPP stanzas from the roster retrieval process could look like this:
</query>
</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
~~~~~~~~~~~~~~~~~~~~~~
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
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
the newer ``argparse`` module.
use the ``argparse`` module for this.
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
@ -222,22 +227,29 @@ a password directly through the command line can be a security risk.
.. code-block:: python
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',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
# Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR",
action="store_const", dest="loglevel",
const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG",
action="store_const", dest="loglevel",
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:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
args = parser.parse_args()
if args.jid is None:
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
``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 ..
logging.basicConfig(level=opts.loglevel,
logging.basicConfig(level=args.loglevel,
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
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
``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
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
to :meth:`slixmpp.clientxmpp.ClientXMPP`.
to :meth:`slixmpp.clientxmpp.ClientXMPP.connect`.
.. code-block:: python
if __name__ == '__main__':
# .. 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`
which will start the event handling, send queue, and XML reader threads. It will also call
the :meth:`slixmpp.plugins.base.BasePlugin.post_init` method on all registered plugins. By
passing ``block=True`` to :meth:`slixmpp.basexmpp.BaseXMPP.process` we are running the
main processing loop in the main thread of execution. The :meth:`slixmpp.basexmpp.BaseXMPP.process`
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.
The :meth:`slixmpp.basexmpp.BaseXMPP.connect` will only schedule a connection
asynchronously. To actually connect, you need to let the event loop take over.
This is done with the :meth:`slixmpp.basexmpp.BaseXMPP.process` method,
which can either run forever (``forever=True``, the default), run for a (maximum)
duration of time (``timeout=n``), and/or run until it gets disconnected (``forever=False``).
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::
Before 1.0, controlling the blocking behaviour of :meth:`slixmpp.basexmpp.BaseXMPP.process` was
done via the ``threaded`` argument. This arrangement was a source of confusion because some users
interpreted that as controlling whether or not Slixmpp used threads at all, instead of how
the processing loop itself was spawned.
The statements ``xmpp.process(threaded=False)`` and ``xmpp.process(block=True)`` are equivalent.
Before slixmpp, :meth:slixmpp.basexmpp.BaseXMPP.process` took ``block`` and ``threaded``
arguments. These do not make sense anymore and have been removed. Slixmpp does not use
threads at all.
.. _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)
def start(self, event):
async def start(self, event):
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
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
def start(self, event):
async def start(self, event):
self.send_presence()
self.get_roster()
await self.get_roster()
self.send_message(mto=self.recipient, mbody=self.msg)
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
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
@ -55,9 +55,9 @@ the `wait` parameter. If `0.0` is passed for `wait`, :meth:`disconnect
.. code-block:: python
def start(self, event):
async def start(self, event):
self.send_presence()
self.get_roster()
await self.get_roster()
self.send_message(mto=self.recipient, mbody=self.msg)

View file

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

View file

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

View file

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