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:
commit
370abb1d98
5 changed files with 68 additions and 72 deletions
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue