Compare commits

...

65 Commits

Author SHA1 Message Date
Maxime “pep” Buquet
412a9169bd Fixes #3432. Allow execute to be used with the meaning of 'next'.
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-04-14 02:34:22 +01:00
Maxime “pep” Buquet
72b355de8c xep_0050: Fix indentation
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-04-14 02:19:58 +01:00
Maxime “pep” Buquet
af246dcfe1 slixmpp/jid: add types
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-04-07 23:14:44 +01:00
Maxime Buquet
9612e518fb Merge branch 'master' into 'master'
Communicate the reason for a disconnect to the application

See merge request poezio/slixmpp!12
2019-04-07 00:24:50 +02:00
Maxime “pep” Buquet
fde8264191 xep_0202: Fix plugin_init docstring
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-04-06 13:44:05 +01:00
Maxime “pep” Buquet
1cdc656208 Fixes poezio/poezio#3472: Don't remove TZ in 0202 utc tag
<utc/> MUST conform to 0082 dateTime profile and thus include a
timezone definition.

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-04-06 13:42:26 +01:00
Maxime “pep” Buquet
0042108a67 poezio/poezio#3472: Ensure tz is correctly set when offset is an int
Thanks lovetox!

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-04-03 23:16:37 +01:00
Georg Lukas
704161a285 mark end-of-stream as session-ending event 2019-03-26 15:16:52 +01:00
Georg Lukas
6b1b58a339 XEP-0199: use new 0-timeout reconnect() with reason 2019-03-26 12:09:43 +01:00
Georg Lukas
4f96e5fa75 Do not directly enqueue connect() as event handler, parameter mismatch 2019-03-26 12:09:43 +01:00
Georg Lukas
bcb90a653e Do not close stream on 0-timeout disconnect, allows 0198 resume 2019-03-26 11:02:28 +01:00
Georg Lukas
7e435b703d Propagate disconnect() reason into 'disconnected' event 2019-03-26 11:01:36 +01:00
Maxime “pep” Buquet
2dda6b80d4 Partially fix poezio/poezio#3452. Prevent groupchat_subject from triggered sent when body or thread are in the message.
0045 says:
> The subject is changed by sending a message of type "groupchat" to the
> <room@service>, where the <message/> MUST contain a <subject/> element that
> specifies the new subject but MUST NOT contain a <body/> element (or a
> <thread/> element). In accordance with the core definition of XMPP, other child
> elements are allowed (although the entity that receives them might ignore
> them).
>
> Note: A message with a <subject/> and a <body/> or a <subject/> and a <thread/>
> is a legitimate message, but it SHALL NOT be interpreted as a subject change.

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-03-23 17:06:07 +00:00
mathieui
5629e44710 Merge branch 'patch-1' into 'master'
Fix slixmpp.ClientXMPP.cancel_connection_attempt()

See merge request poezio/slixmpp!10
2019-03-15 00:38:35 +01:00
Maxime “pep” Buquet
6a06881d8b xep_0030: fix typo on 'remote' in get_info docstring
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-03-05 00:00:14 +00:00
Maxime “pep” Buquet
2b666eb1de Revert "Remove license from unused project"
This reverts commit fbab3ad214.
2019-02-24 12:16:06 +00:00
Emmanuel Gil Peyrot
400e7a3903 Remove SocksiPy license
I wrote a SOCKS5 implementation back in
9c5dd024b1, and removed SocksiPy from
slixmpp’s codebase at the same time.
2019-02-24 13:02:08 +01:00
Maxime “pep” Buquet
fbab3ad214 Remove license from unused project
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-02-24 12:00:16 +00:00
mathieui
628b357b06 Merge branch 'eme-add-method' into 'master'
xep_0380: Add add_eme method

See merge request poezio/slixmpp!11
2019-02-23 14:07:15 +01:00
Maxime “pep” Buquet
88260cc240 xep_0380: Add add_eme method
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-02-23 12:54:56 +00:00
Maxime “pep” Buquet
e9f2f503b8 xep_0380: Remove remove_handler call in plugin_end
Remove probable copy paste fail in EME plugin_end handler.

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-02-23 12:36:40 +00:00
ehendrix23
696a72247b Fix slixmpp.ClientXMPP.cancel_connection_attempt() 2019-02-22 00:41:02 +01:00
Maxime “pep” Buquet
05d76e4b1d Change more URLs to lab.louiz.org
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-02-12 10:34:57 +00:00
Maxime “pep” Buquet
d52d4fbbbe Update project URLs to lab.louiz.org
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-02-12 10:26:04 +00:00
Maxime “pep” Buquet
e53c0fcb30 README: Add pep as a contributor
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-02-10 14:18:28 +00:00
Maxime “pep” Buquet
97d68c5196 setup.py: GTalk is no more
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-02-10 14:18:28 +00:00
mathieui
b42fafabb4 Make the cache encode and decode not crash if something goes wrong 2019-02-02 17:42:24 +01:00
mathieui
3a44ec8f15 Add tests for the cache api 2019-02-02 17:32:10 +01:00
mathieui
93f385562f Add a "remove" action on the cache API 2019-02-02 17:31:48 +01:00
mathieui
9cab02438b Fix XEP-0335 2019-02-02 15:41:55 +01:00
Emmanuel Gil Peyrot
74ed50e626 Set @id by default on outgoing messages and presences.
Respects RFC6120 §8.1.3’s RECOMMENDED.
2019-01-31 16:46:51 +01:00
mathieui
9d378c611c Release 1.4.2 2019-01-31 14:50:26 +01:00
Link Mauve
d85d8f4479 Merge branch 'xep-0335' into 'master'
Add xep_0335: JSON Containers

See merge request poezio/slixmpp!5
2019-01-22 19:28:38 +01:00
Emmanuel Gil Peyrot
fb75f7cda9 Stop requesting avatar without the intervention of the client. 2019-01-22 15:12:00 +01:00
Emmanuel Gil Peyrot
41419a2161 Fix authenticating on a non-TLS socket.
This was broken since c1562b76b2.
2019-01-21 01:02:06 +01:00
Emmanuel Gil Peyrot
7cd73b594e XEP-0223: Fix default access_model, it MUST be whitelist. 2019-01-17 12:08:51 +01:00
Emmanuel Gil Peyrot
15c6b775ff Simplify the non-CDATA path of tostring.escape. 2019-01-09 15:03:05 +01:00
Emmanuel Gil Peyrot
4b482477e2 Split ns only once in fix_ns(). 2019-01-09 14:57:39 +01:00
Emmanuel Gil Peyrot
f7e4caadfe Split tag and attrib only once in tostring(). 2019-01-09 14:55:27 +01:00
Emmanuel Gil Peyrot
5f25b0b6a0 Add a default timeout to iq.send().
This fixes a leak of MatchIDSender in handlers, making it more and more
expensive to match stanzas as more iqs have been sent.
2019-01-09 14:20:31 +01:00
Mateusz Piotrowski
d228bc42ea Mention that GnuPG is required for tests 2018-12-27 17:21:12 +01:00
mathieui
ecdc44a601 Merge branch 'master' into 'master'
Decode bytes in GSSAPI handling, as expected by the kerberos module API.

See merge request poezio/slixmpp!8
2018-12-27 16:55:47 +01:00
Emmanuel Gil Peyrot
33370e42f1 XEP-0363: Use a specific exception for HTTP errors 2018-11-20 07:44:09 +01:00
Florian Klien
4699861925 catch http upload errors on upload 2018-11-20 07:34:56 +01:00
Jelmer Vernooij
2d228bdb56 Decode bytes in GSSAPI handling, as expected by the kerberos module API. 2018-10-30 22:29:20 +00:00
mathieui
570e653ac2 Release slixmpp 1.4.1 2018-10-28 14:15:51 +01:00
mathieui
282a481059 Merge branch 'setup_dependency_fix' into 'master'
added aiohttp dependency

See merge request poezio/slixmpp!6
2018-10-28 14:08:26 +01:00
Florian Klien
f386db380b docs: auto-set copyright year to current year 2018-10-28 10:48:23 +01:00
Florian Klien
7b87d98fff auto set version of Slixmpp in Docs
getting version of slixmpp from source tree for documentation
2018-10-28 10:48:23 +01:00
Florian Klien
8779d40602 typo 2018-10-27 23:38:09 +02:00
Emmanuel Gil Peyrot
f0b21c42d5 examples: Add the possibility to use another HTTP File Upload domain. 2018-10-27 23:25:59 +02:00
Emmanuel Gil Peyrot
e241d4e3c7 XEP-0030: Don’t call the timeout_callback on each domain which doesn’t reply to disco#info. 2018-10-27 23:21:27 +02:00
Emmanuel Gil Peyrot
bd22a41a78 XEP-0363: Also check for disco#info’s feature instead of just the identity. 2018-10-27 23:14:39 +02:00
Emmanuel Gil Peyrot
a29a29227a XEP-0363: Add a domain argument to discover an upload service on a specific domain. 2018-10-27 22:51:04 +02:00
Florian Klien
d4d542b741 fixing uncaught async exceptions due to missing await
fixes uncaught exceptions in the event loop.
passing timeout and timeout_callback through.
2018-10-27 22:51:04 +02:00
Florian Klien
dc4936a6d3 added aoihttp dependency
slixmpp uses aiohttp in the XEP_0363 plugin.
2018-10-22 12:41:20 +02:00
Florian Klien
897610d819 fix: failUnlessEqual -> assertEqual 2018-10-15 14:59:23 +02:00
Florian Klien
d33366badd fixing deprecation warnings for pytest 2018-10-15 14:59:23 +02:00
mathieui
809c500002 Add the loop parameters at places where it has been forgotten 2018-10-09 12:34:56 +02:00
Emmanuel Gil Peyrot
dda4e18b81 examples: Remove unused asyncio imports. 2018-10-09 10:57:19 +02:00
Emmanuel Gil Peyrot
8c09d932c8 stanzabase: Remove python2 legacy. 2018-10-03 14:56:07 +02:00
Maxime “pep” Buquet
31f5e84671 Add xep_0335: JSON Containers
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2018-09-16 22:13:41 +01:00
louiz’
ad0dc33df9 Trigger poezio’s build if this ours succeeded 2018-08-22 23:19:47 +02:00
Emmanuel Gil Peyrot
7c3b3827b4 jid: Make property aliases proper aliases. 2018-08-20 00:23:21 +01:00
Emmanuel Gil Peyrot
9f6fa65139 examples, tests: Replace all @asyncio.coroutines with proper async functions. 2018-08-19 17:47:26 +01:00
74 changed files with 591 additions and 416 deletions

View File

@@ -1,4 +1,9 @@
stages:
- test
- trigger
test:
stage: test
tags:
- docker
image: ubuntu:latest
@@ -6,3 +11,11 @@ test:
- apt update
- apt install -y python3 cython3 gpg
- ./run_tests.py
trigger_poezio:
stage: trigger
tags:
- docker
image: appropriate/curl:latest
script:
- curl --request POST -F token="$SLIXMPP_TRIGGER_TOKEN" -F ref=master https://lab.louiz.org/api/v4/projects/18/trigger/pipeline

View File

@@ -5,7 +5,7 @@ To contribute, the preferred way is to commit your changes on some
publicly-available git repository (on a fork `on github
<https://github.com/poezio/slixmpp>`_ or on your own repository) and to
notify the developers with either:
- a ticket `on the bug tracker <https://dev.poez.io/new>`_
- a ticket `on the bug tracker <https://lab.louiz.org/poezio/slixmpp/issues/new>`_
- a pull request on github
- a simple message on `the XMPP MUC <xmpp:slixmpp@muc.poez.io>`_

View File

@@ -1,6 +1,7 @@
Pre-requisites:
- Python 3.5+
- Cython 0.22 and libidn, optionally (making JID faster by compiling the stringprep module)
- GnuPG, for testing
Install:
> python3 setup.py install

25
LICENSE
View File

@@ -167,28 +167,3 @@ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
socksipy: A Python SOCKS client module.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Copyright 2006 Dan-Haim. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of Dan Haim nor the names of his contributors may be used
to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.

View File

@@ -113,6 +113,7 @@ Slixmpp Credits
- Gasper Zejn (`Gasper Zejn <mailto:zejn@kiberpipa.org>`_)
- Krzysztof Kotlenga (`Krzysztof Kotlenga <mailto:pocek@users.sf.net>`_)
- Tsukasa Hiiragi (`Tsukasa Hiiragi <mailto:bakalolka@gmail.com>`_)
- Maxime Buquet (`pep <xmpp:pep@bouah.net?message>`_)
Credits (SleekXMPP)
-------------------

View File

@@ -12,12 +12,17 @@
# serve to show the default.
import sys, os
import datetime
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))
# get version automagically from source tree
from slixmpp.version import __version__ as version
release = ".".join(version.split(".")[0:2])
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
@@ -41,16 +46,18 @@ master_doc = 'index'
# General information about the project.
project = u'Slixmpp'
copyright = u'2011, Nathan Fritz, Lance Stout'
year = datetime.datetime.now().year
copyright = u'{}, Nathan Fritz, Lance Stout'.format(year)
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# auto imported from code!
# The short X.Y version.
version = '1.1'
# version = '1.4'
# The full version, including alpha/beta/rc tags.
release = '1.1'
# release = '1.4.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@@ -11,7 +11,7 @@ Create and Run a Server Component
<xmpp:slixmpp@muc.poez.io?join>`_.
If you have not yet installed Slixmpp, do so now by either checking out a version
with `Git <http://git.poez.io/slixmpp>`_.
with `Git <https://lab.louiz.org/poezio/slixmpp>`_.
Many XMPP applications eventually graduate to requiring to run as a server
component in order to meet scalability requirements. To demonstrate how to

View File

@@ -11,7 +11,7 @@ Slixmpp Quickstart - Echo Bot
<xmpp:slixmpp@muc.poez.io?join>`_.
If you have not yet installed Slixmpp, do so now by either checking out a version
with `Git <http://git.poez.io/slixmpp>`_.
with `Git <https://lab.louiz.org/poezio/slixmpp>`_.
As a basic starting project, we will create an echo bot which will reply to any
messages sent to it. We will also go through adding some basic command line configuration
@@ -329,7 +329,7 @@ The Final Product
-----------------
Here then is what the final result should look like after working through the guide above. The code
can also be found in the Slixmpp `examples directory <http://git.poez.io/slixmpp/tree/examples>`_.
can also be found in the Slixmpp `examples directory <https://lab.louiz.org/poezio/slixmpp/tree/master/examples>`_.
.. compound::

View File

@@ -11,7 +11,7 @@ Multi-User Chat (MUC) Bot
<xmpp:slixmpp@muc.poez.io?join>`_.
If you have not yet installed Slixmpp, do so now by either checking out a version
from `Git <http://git.poez.io/slixmpp>`_.
from `Git <https://lab.louiz.org/poezio/slixmpp>`_.
Now that you've got the basic gist of using Slixmpp by following the
echobot example (:ref:`echobot`), we can use one of the bundled plugins

View File

@@ -4,9 +4,9 @@ Slixmpp
.. sidebar:: Get the Code
The latest source code for Slixmpp may be found on the `Git repo
<http://git.poez.io/slixmpp>`_. ::
<https://lab.louiz.org/poezio/slixmpp>`_. ::
git clone git://git.poez.io/slixmpp
git clone https://lab.louiz.org/poezio/slixmpp
An XMPP chat room is available for discussing and getting help with slixmpp.
@@ -14,7 +14,7 @@ Slixmpp
`slixmpp@muc.poez.io <xmpp:slixmpp@muc.poez.io?join>`_
**Reporting bugs**
You can report bugs at http://dev.louiz.org/projects/slixmpp/issues.
You can report bugs at http://lab.louiz.org/poezio/slixmpp/issues.
.. note::
slixmpp is a friendly fork of `SleekXMPP <https://github.com/fritzy/SleekXMPP>`_

View File

@@ -15,7 +15,6 @@ from argparse import ArgumentParser
import slixmpp
from slixmpp.exceptions import XMPPError
from slixmpp import asyncio
log = logging.getLogger(__name__)

View File

@@ -51,18 +51,17 @@ class AskConfirm(slixmpp.ClientXMPP):
else:
self.confirmed.set_result(True)
@asyncio.coroutine
def start(self, event):
async def start(self, event):
log.info('Sending confirm request %s to %s who wants to access %s using '
'method %s...' % (self.id, self.recipient, self.url, self.method))
try:
confirmed = yield from self['xep_0070'].ask_confirm(self.recipient,
confirmed = await self['xep_0070'].ask_confirm(self.recipient,
id=self.id,
url=self.url,
method=self.method,
message='Plz say yes or no for {method} {url} ({id}).')
if isinstance(confirmed, slixmpp.Message):
confirmed = yield from self.confirmed
confirmed = await self.confirmed
else:
confirmed = True
except IqError:

View File

@@ -15,7 +15,6 @@ from argparse import ArgumentParser
import slixmpp
from slixmpp.exceptions import IqError, IqTimeout
from slixmpp.xmlstream.asyncio import asyncio
class Disco(slixmpp.ClientXMPP):
@@ -54,8 +53,7 @@ class Disco(slixmpp.ClientXMPP):
# our roster.
self.add_event_handler("session_start", self.start)
@asyncio.coroutine
def start(self, event):
async def start(self, event):
"""
Process the session_start event.
@@ -77,13 +75,13 @@ class Disco(slixmpp.ClientXMPP):
try:
if self.get in self.info_types:
# function using the callback parameter.
info = yield from self['xep_0030'].get_info(jid=self.target_jid,
info = await self['xep_0030'].get_info(jid=self.target_jid,
node=self.target_node)
if self.get in self.items_types:
# The same applies from above. Listen for the
# disco_items event or pass a callback function
# if you need to process a non-blocking request.
items = yield from self['xep_0030'].get_items(jid=self.target_jid,
items = await self['xep_0030'].get_items(jid=self.target_jid,
node=self.target_node)
if self.get not in self.info_types and self.get not in self.items_types:
logging.error("Invalid disco request type.")

View File

@@ -47,8 +47,7 @@ class AvatarDownloader(slixmpp.ClientXMPP):
self.roster_received.set()
self.presences_received.clear()
@asyncio.coroutine
def start(self, event):
async def start(self, event):
"""
Process the session_start event.
@@ -65,16 +64,15 @@ class AvatarDownloader(slixmpp.ClientXMPP):
self.get_roster(callback=self.roster_received_cb)
print('Waiting for presence updates...\n')
yield from self.roster_received.wait()
await self.roster_received.wait()
print('Roster received')
yield from self.presences_received.wait()
await self.presences_received.wait()
self.disconnect()
@asyncio.coroutine
def on_vcard_avatar(self, pres):
async def on_vcard_avatar(self, pres):
print("Received vCard avatar update from %s" % pres['from'].bare)
try:
result = yield from self['xep_0054'].get_vcard(pres['from'].bare, cached=True,
result = await self['xep_0054'].get_vcard(pres['from'].bare, cached=True,
timeout=5)
except XMPPError:
print("Error retrieving avatar for %s" % pres['from'])
@@ -89,14 +87,13 @@ class AvatarDownloader(slixmpp.ClientXMPP):
with open(filename, 'wb+') as img:
img.write(avatar['BINVAL'])
@asyncio.coroutine
def on_avatar(self, msg):
async def on_avatar(self, msg):
print("Received avatar update from %s" % msg['from'])
metadata = msg['pubsub_event']['items']['item']['avatar_metadata']
for info in metadata['items']:
if not info['url']:
try:
result = yield from self['xep_0084'].retrieve_avatar(msg['from'].bare, info['id'],
result = await self['xep_0084'].retrieve_avatar(msg['from'].bare, info['id'],
timeout=5)
except XMPPError:
print("Error retrieving avatar for %s" % msg['from'])

View File

@@ -14,7 +14,6 @@ from getpass import getpass
from argparse import ArgumentParser
import slixmpp
from slixmpp import asyncio
log = logging.getLogger(__name__)
@@ -25,18 +24,21 @@ class HttpUpload(slixmpp.ClientXMPP):
A basic client asking an entity if they confirm the access to an HTTP URL.
"""
def __init__(self, jid, password, recipient, filename):
def __init__(self, jid, password, recipient, filename, domain=None):
slixmpp.ClientXMPP.__init__(self, jid, password)
self.recipient = recipient
self.filename = filename
self.domain = domain
self.add_event_handler("session_start", self.start)
@asyncio.coroutine
def start(self, event):
async def start(self, event):
log.info('Uploading file %s...', self.filename)
url = yield from self['xep_0363'].upload_file(self.filename)
def timeout_callback(arg):
raise TimeoutError("could not send message in time")
url = await self['xep_0363'].upload_file(
self.filename, domain=self.domain, timeout=10, timeout_callback=timeout_callback)
log.info('Upload success!')
log.info('Sending file to %s', self.recipient)
@@ -70,6 +72,8 @@ if __name__ == '__main__':
help="Recipient JID")
parser.add_argument("-f", "--file", required=True,
help="File to send")
parser.add_argument("--domain",
help="Domain to use for HTTP File Upload (leave out for your own servers)")
args = parser.parse_args()
@@ -82,7 +86,7 @@ if __name__ == '__main__':
if args.password is None:
args.password = getpass("Password: ")
xmpp = HttpUpload(args.jid, args.password, args.recipient, args.file)
xmpp = HttpUpload(args.jid, args.password, args.recipient, args.file, args.domain)
xmpp.register_plugin('xep_0071')
xmpp.register_plugin('xep_0128')
xmpp.register_plugin('xep_0363')

View File

@@ -9,7 +9,6 @@
See the file LICENSE for copying permission.
"""
import asyncio
import logging
from getpass import getpass
from argparse import ArgumentParser
@@ -39,8 +38,7 @@ class IBBSender(slixmpp.ClientXMPP):
# our roster.
self.add_event_handler("session_start", self.start)
@asyncio.coroutine
def start(self, event):
async def start(self, event):
"""
Process the session_start event.
@@ -58,13 +56,13 @@ class IBBSender(slixmpp.ClientXMPP):
try:
# Open the IBB stream in which to write to.
stream = yield from self['xep_0047'].open_stream(self.receiver, use_messages=self.use_messages)
stream = await self['xep_0047'].open_stream(self.receiver, use_messages=self.use_messages)
# If you want to send in-memory bytes, use stream.sendall() instead.
yield from stream.sendfile(self.file, timeout=10)
await stream.sendfile(self.file, timeout=10)
# And finally close the stream.
yield from stream.close(timeout=10)
await stream.close(timeout=10)
except (IqError, IqTimeout):
print('File transfer errored')
else:

View File

@@ -15,7 +15,6 @@ from argparse import ArgumentParser
import slixmpp
from slixmpp.exceptions import XMPPError
from slixmpp import asyncio
log = logging.getLogger(__name__)

View File

@@ -13,7 +13,6 @@ import logging
from getpass import getpass
from argparse import ArgumentParser
from slixmpp.exceptions import IqError, IqTimeout
from slixmpp import asyncio
import slixmpp
@@ -38,8 +37,7 @@ class PingTest(slixmpp.ClientXMPP):
# our roster.
self.add_event_handler("session_start", self.start)
@asyncio.coroutine
def start(self, event):
async def start(self, event):
"""
Process the session_start event.
@@ -56,7 +54,7 @@ class PingTest(slixmpp.ClientXMPP):
self.get_roster()
try:
rtt = yield from self['xep_0199'].ping(self.pingjid,
rtt = await self['xep_0199'].ping(self.pingjid,
timeout=10)
logging.info("Success! RTT: %s", rtt)
except IqError as e:

View File

@@ -5,7 +5,6 @@ import logging
from getpass import getpass
from argparse import ArgumentParser
import asyncio
import slixmpp
from slixmpp.exceptions import XMPPError
from slixmpp.xmlstream import ET, tostring
@@ -32,87 +31,86 @@ class PubsubClient(slixmpp.ClientXMPP):
self.add_event_handler('session_start', self.start)
@asyncio.coroutine
def start(self, event):
async def start(self, event):
self.get_roster()
self.send_presence()
try:
yield from getattr(self, self.action)()
await getattr(self, self.action)()
except:
logging.exception('Could not execute %s:', self.action)
self.disconnect()
def nodes(self):
async def nodes(self):
try:
result = yield from self['xep_0060'].get_nodes(self.pubsub_server, self.node)
result = await self['xep_0060'].get_nodes(self.pubsub_server, self.node)
for item in result['disco_items']['items']:
logging.info(' - %s', str(item))
except XMPPError as error:
logging.error('Could not retrieve node list: %s', error.format())
def create(self):
async def create(self):
try:
yield from self['xep_0060'].create_node(self.pubsub_server, self.node)
await self['xep_0060'].create_node(self.pubsub_server, self.node)
logging.info('Created node %s', self.node)
except XMPPError as error:
logging.error('Could not create node %s: %s', self.node, error.format())
def delete(self):
async def delete(self):
try:
yield from self['xep_0060'].delete_node(self.pubsub_server, self.node)
await self['xep_0060'].delete_node(self.pubsub_server, self.node)
logging.info('Deleted node %s', self.node)
except XMPPError as error:
logging.error('Could not delete node %s: %s', self.node, error.format())
def get_configure(self):
async def get_configure(self):
try:
configuration_form = yield from self['xep_0060'].get_node_config(self.pubsub_server, self.node)
configuration_form = await self['xep_0060'].get_node_config(self.pubsub_server, self.node)
logging.info('Configure form received from node %s: %s', self.node, configuration_form['pubsub_owner']['configure']['form'])
except XMPPError as error:
logging.error('Could not retrieve configure form from node %s: %s', self.node, error.format())
def publish(self):
async def publish(self):
payload = ET.fromstring("<test xmlns='test'>%s</test>" % self.data)
try:
result = yield from self['xep_0060'].publish(self.pubsub_server, self.node, payload=payload)
result = await self['xep_0060'].publish(self.pubsub_server, self.node, payload=payload)
logging.info('Published at item id: %s', result['pubsub']['publish']['item']['id'])
except XMPPError as error:
logging.error('Could not publish to %s: %s', self.node, error.format())
def get(self):
async def get(self):
try:
result = yield from self['xep_0060'].get_item(self.pubsub_server, self.node, self.data)
result = await self['xep_0060'].get_item(self.pubsub_server, self.node, self.data)
for item in result['pubsub']['items']['substanzas']:
logging.info('Retrieved item %s: %s', item['id'], tostring(item['payload']))
except XMPPError as error:
logging.error('Could not retrieve item %s from node %s: %s', self.data, self.node, error.format())
def retract(self):
async def retract(self):
try:
yield from self['xep_0060'].retract(self.pubsub_server, self.node, self.data)
await self['xep_0060'].retract(self.pubsub_server, self.node, self.data)
logging.info('Retracted item %s from node %s', self.data, self.node)
except XMPPError as error:
logging.error('Could not retract item %s from node %s: %s', self.data, self.node, error.format())
def purge(self):
async def purge(self):
try:
yield from self['xep_0060'].purge(self.pubsub_server, self.node)
await self['xep_0060'].purge(self.pubsub_server, self.node)
logging.info('Purged all items from node %s', self.node)
except XMPPError as error:
logging.error('Could not purge items from node %s: %s', self.node, error.format())
def subscribe(self):
async def subscribe(self):
try:
iq = yield from self['xep_0060'].subscribe(self.pubsub_server, self.node)
iq = await self['xep_0060'].subscribe(self.pubsub_server, self.node)
subscription = iq['pubsub']['subscription']
logging.info('Subscribed %s to node %s', subscription['jid'], subscription['node'])
except XMPPError as error:
logging.error('Could not subscribe %s to node %s: %s', self.boundjid.bare, self.node, error.format())
def unsubscribe(self):
async def unsubscribe(self):
try:
yield from self['xep_0060'].unsubscribe(self.pubsub_server, self.node)
await self['xep_0060'].unsubscribe(self.pubsub_server, self.node)
logging.info('Unsubscribed %s from node %s', self.boundjid.bare, self.node)
except XMPPError as error:
logging.error('Could not unsubscribe %s from node %s: %s', self.boundjid.bare, self.node, error.format())

View File

@@ -66,7 +66,7 @@ class RegisterBot(slixmpp.ClientXMPP):
# We're only concerned about registering, so nothing more to do here.
self.disconnect()
def register(self, iq):
async def register(self, iq):
"""
Fill out and submit a registration form.
@@ -90,7 +90,7 @@ class RegisterBot(slixmpp.ClientXMPP):
resp['register']['password'] = self.password
try:
yield from resp.send()
await resp.send()
logging.info("Account created for %s!" % self.boundjid)
except IqError as e:
logging.error("Could not register account: %s" %

View File

@@ -38,8 +38,7 @@ class RosterBrowser(slixmpp.ClientXMPP):
self.received = set()
self.presences_received = asyncio.Event()
@asyncio.coroutine
def start(self, event):
async def start(self, event):
"""
Process the session_start event.
@@ -57,7 +56,7 @@ class RosterBrowser(slixmpp.ClientXMPP):
future.set_result(None)
try:
self.get_roster(callback=callback)
yield from future
await future
except IqError as err:
print('Error: %s' % err.iq['error']['condition'])
except IqTimeout:
@@ -66,7 +65,7 @@ class RosterBrowser(slixmpp.ClientXMPP):
print('Waiting for presence updates...\n')
yield from asyncio.sleep(10)
await asyncio.sleep(10)
print('Roster for %s' % self.boundjid.bare)
groups = self.client_roster.groups()

View File

@@ -9,7 +9,6 @@
See the file LICENSE for copying permission.
"""
import asyncio
import logging
from getpass import getpass
from argparse import ArgumentParser

View File

@@ -9,7 +9,6 @@
See the file LICENSE for copying permission.
"""
import asyncio
import logging
from getpass import getpass
from argparse import ArgumentParser
@@ -36,8 +35,7 @@ class S5BSender(slixmpp.ClientXMPP):
# and the XML streams are ready for use.
self.add_event_handler("session_start", self.start)
@asyncio.coroutine
def start(self, event):
async def start(self, event):
"""
Process the session_start event.
@@ -53,14 +51,14 @@ class S5BSender(slixmpp.ClientXMPP):
try:
# Open the S5B stream in which to write to.
proxy = yield from self['xep_0065'].handshake(self.receiver)
proxy = await self['xep_0065'].handshake(self.receiver)
# Send the entire file.
while True:
data = self.file.read(1048576)
if not data:
break
yield from proxy.write(data)
await proxy.write(data)
# And finally close the stream.
proxy.transport.write_eof()

View File

@@ -18,7 +18,6 @@ from argparse import ArgumentParser
import slixmpp
from slixmpp.exceptions import XMPPError
from slixmpp import asyncio
class AvatarSetter(slixmpp.ClientXMPP):
@@ -33,8 +32,7 @@ class AvatarSetter(slixmpp.ClientXMPP):
self.filepath = filepath
@asyncio.coroutine
def start(self, event):
async def start(self, event):
"""
Process the session_start event.
@@ -68,20 +66,20 @@ class AvatarSetter(slixmpp.ClientXMPP):
used_xep84 = False
print('Publish XEP-0084 avatar data')
result = yield from self['xep_0084'].publish_avatar(avatar)
result = await self['xep_0084'].publish_avatar(avatar)
if isinstance(result, XMPPError):
print('Could not publish XEP-0084 avatar')
else:
used_xep84 = True
print('Update vCard with avatar')
result = yield from self['xep_0153'].set_avatar(avatar=avatar, mtype=avatar_type)
result = await self['xep_0153'].set_avatar(avatar=avatar, mtype=avatar_type)
if isinstance(result, XMPPError):
print('Could not set vCard avatar')
if used_xep84:
print('Advertise XEP-0084 avatar metadata')
result = yield from self['xep_0084'].publish_avatar_metadata([
result = await self['xep_0084'].publish_avatar_metadata([
{'id': avatar_id,
'type': avatar_type,
'bytes': avatar_bytes}

View File

@@ -20,8 +20,7 @@ from run_tests import TestCommand
from slixmpp.version import __version__
VERSION = __version__
DESCRIPTION = ('Slixmpp is an elegant Python library for XMPP (aka Jabber, '
'Google Talk, etc).')
DESCRIPTION = ('Slixmpp is an elegant Python library for XMPP (aka Jabber).')
with open('README.rst', encoding='utf8') as readme:
LONG_DESCRIPTION = readme.read()
@@ -79,12 +78,12 @@ setup(
long_description=LONG_DESCRIPTION,
author='Florent Le Coz',
author_email='louiz@louiz.org',
url='https://dev.louiz.org/projects/slixmpp',
url='https://lab.louiz.org/poezio/slixmpp',
license='MIT',
platforms=['any'],
packages=packages,
ext_modules=ext_modules,
install_requires=['aiodns>=1.0', 'pyasn1', 'pyasn1_modules'],
install_requires=['aiodns>=1.0', 'pyasn1', 'pyasn1_modules', 'aiohttp'],
classifiers=CLASSIFIERS,
cmdclass={'test': TestCommand}
)

View File

@@ -104,12 +104,12 @@ class BaseXMPP(XMLStream):
#: :attr:`use_message_ids` to `True` will assign all outgoing
#: messages an ID. Some plugin features require enabling
#: this option.
self.use_message_ids = False
self.use_message_ids = True
#: Presence updates may optionally be tagged with ID values.
#: Setting :attr:`use_message_ids` to `True` will assign all
#: outgoing messages an ID.
self.use_presence_ids = False
self.use_presence_ids = True
#: The API registry is a way to process callbacks based on
#: JID+node combinations. Each callback in the registry is

View File

@@ -97,7 +97,10 @@ class FeatureMechanisms(BasePlugin):
jid = self.xmpp.requested_jid.bare
result[value] = creds.get('email', jid)
elif value == 'channel_binding':
result[value] = self.xmpp.socket.get_channel_binding()
if isinstance(self.xmpp.socket, (ssl.SSLSocket, ssl.SSLObject)):
result[value] = self.xmpp.socket.get_channel_binding()
else:
result[value] = None
elif value == 'host':
result[value] = creds.get('host', self.xmpp.requested_jid.domain)
elif value == 'realm':

View File

@@ -16,6 +16,7 @@ import socket
from copy import deepcopy
from functools import lru_cache
from typing import Optional
from slixmpp.stringprep import nodeprep, resourceprep, idna, StringprepError
@@ -71,7 +72,7 @@ def _parse_jid(data):
return node, domain, resource
def _validate_node(node):
def _validate_node(node: Optional[str]):
"""Validate the local, or username, portion of a JID.
:raises InvalidJID:
@@ -93,7 +94,7 @@ def _validate_node(node):
return node
def _validate_domain(domain):
def _validate_domain(domain: str):
"""Validate the domain portion of a JID.
IP literal addresses are left as-is, if valid. Domain names
@@ -152,7 +153,7 @@ def _validate_domain(domain):
return domain
def _validate_resource(resource):
def _validate_resource(resource: Optional[str]):
"""Validate the resource portion of a JID.
:raises InvalidJID:
@@ -174,7 +175,7 @@ def _validate_resource(resource):
return resource
def _unescape_node(node):
def _unescape_node(node: str):
"""Unescape a local portion of a JID.
.. note::
@@ -199,7 +200,11 @@ def _unescape_node(node):
return ''.join(unescaped)
def _format_jid(local=None, domain=None, resource=None):
def _format_jid(
local: Optional[str] = None,
domain: Optional[str] = None,
resource: Optional[str] = None,
):
"""Format the given JID components into a full or bare JID.
:param string local: Optional. The local portion of the JID.
@@ -237,12 +242,17 @@ class UnescapedJID:
__slots__ = ('_node', '_domain', '_resource')
def __init__(self, node, domain, resource):
def __init__(
self,
node: Optional[str],
domain: Optional[str],
resource: Optional[str],
):
self._node = node
self._domain = domain
self._resource = resource
def __getattribute__(self, name):
def __getattribute__(self, name: str):
"""Retrieve the given JID component.
:param name: one of: user, server, domain, resource,
@@ -301,7 +311,7 @@ class JID:
__slots__ = ('_node', '_domain', '_resource', '_bare', '_full')
def __init__(self, jid=None):
def __init__(self, jid: Optional[str] = None):
if not jid:
self._node = ''
self._domain = ''
@@ -346,30 +356,10 @@ class JID:
def node(self):
return self._node
@property
def user(self):
return self._node
@property
def local(self):
return self._node
@property
def username(self):
return self._node
@property
def domain(self):
return self._domain
@property
def server(self):
return self._domain
@property
def host(self):
return self._domain
@property
def resource(self):
return self._resource
@@ -382,47 +372,18 @@ class JID:
def full(self):
return self._full
@property
def jid(self):
return self._full
@node.setter
def node(self, value):
self._node = _validate_node(value)
self._update_bare_full()
@user.setter
def user(self, value):
self._node = _validate_node(value)
self._update_bare_full()
@local.setter
def local(self, value):
self._node = _validate_node(value)
self._update_bare_full()
@username.setter
def username(self, value):
def node(self, value: str):
self._node = _validate_node(value)
self._update_bare_full()
@domain.setter
def domain(self, value):
self._domain = _validate_domain(value)
self._update_bare_full()
@server.setter
def server(self, value):
self._domain = _validate_domain(value)
self._update_bare_full()
@host.setter
def host(self, value):
def domain(self, value: str):
self._domain = _validate_domain(value)
self._update_bare_full()
@bare.setter
def bare(self, value):
def bare(self, value: str):
node, domain, resource = _parse_jid(value)
assert not resource
self._node = node
@@ -430,19 +391,23 @@ class JID:
self._update_bare_full()
@resource.setter
def resource(self, value):
def resource(self, value: str):
self._resource = _validate_resource(value)
self._update_bare_full()
@full.setter
def full(self, value):
def full(self, value: str):
self._node, self._domain, self._resource = _parse_jid(value)
self._update_bare_full()
@jid.setter
def jid(self, value):
self._node, self._domain, self._resource = _parse_jid(value)
self._update_bare_full()
user = node
local = node
username = node
server = domain
host = domain
jid = full
def __str__(self):
"""Use the full JID as the string value."""

View File

@@ -299,23 +299,27 @@ class XEP_0030(BasePlugin):
return self.api['has_identity'](jid, node, ifrom, data)
async def get_info_from_domain(self, domain=None, timeout=None,
cached=True, callback=None, **kwargs):
cached=True, callback=None):
if domain is None:
domain = self.xmpp.boundjid.domain
if not cached or domain not in self.domain_infos:
infos = [self.get_info(
domain, timeout=timeout, **kwargs)]
domain, timeout=timeout)]
iq_items = await self.get_items(
domain, timeout=timeout, **kwargs)
domain, timeout=timeout)
items = iq_items['disco_items']['items']
infos += [
self.get_info(item[0], timeout=timeout, **kwargs)
self.get_info(item[0], timeout=timeout)
for item in items]
info_futures, _ = await asyncio.wait(infos, timeout=timeout)
info_futures, _ = await asyncio.wait(
infos,
timeout=timeout,
loop=self.xmpp.loop
)
self.domain_infos[domain] = [
future.result() for future in info_futures]
future.result() for future in info_futures if not future.exception()]
results = self.domain_infos[domain]
@@ -344,7 +348,7 @@ class XEP_0030(BasePlugin):
combination handled by this Slixmpp instance and
no stanzas need to be sent.
Otherwise, a disco stanza must be sent to the
remove JID to retrieve the info.
remote JID to retrieve the info.
cached -- If true, then look for the disco info data from
the local cache system. If no results are found,
send the query as usual. The self.use_cache

View File

@@ -9,7 +9,7 @@ from __future__ import with_statement
import logging
from slixmpp import Presence
from slixmpp import Presence, Message
from slixmpp.plugins import BasePlugin, register_plugin
from slixmpp.xmlstream import register_stanza_plugin, ElementBase, JID, ET
from slixmpp.xmlstream.handler.callback import Callback
@@ -181,7 +181,7 @@ class XEP_0045(BasePlugin):
if got_online:
self.xmpp.event("muc::%s::got_online" % entry['room'], pr)
def handle_groupchat_message(self, msg):
def handle_groupchat_message(self, msg: Message) -> None:
""" Handle a message event in a muc.
"""
self.xmpp.event('groupchat_message', msg)
@@ -195,10 +195,14 @@ class XEP_0045(BasePlugin):
def handle_groupchat_subject(self, msg):
def handle_groupchat_subject(self, msg: Message) -> None:
""" Handle a message coming from a muc indicating
a change of subject (or announcing it when joining the room)
"""
# See poezio#3452. A message containing subject _and_ (body or thread)
# is not a subject change.
if msg['body'] or msg['thread']:
return None
self.xmpp.event('groupchat_subject', msg)
def jid_in_room(self, room, jid):

View File

@@ -89,31 +89,17 @@ class XEP_0050(BasePlugin):
self.commands = {}
self.xmpp.register_handler(
Callback("Ad-Hoc Execute",
StanzaPath('iq@type=set/command'),
self._handle_command))
Callback("Ad-Hoc Execute",
StanzaPath('iq@type=set/command'),
self._handle_command))
register_stanza_plugin(Iq, Command)
register_stanza_plugin(Command, Form, iterable=True)
self.xmpp.add_event_handler('command_execute',
self._handle_command_start)
self.xmpp.add_event_handler('command_next',
self._handle_command_next)
self.xmpp.add_event_handler('command_cancel',
self._handle_command_cancel)
self.xmpp.add_event_handler('command_complete',
self._handle_command_complete)
self.xmpp.add_event_handler('command', self._handle_command_all)
def plugin_end(self):
self.xmpp.del_event_handler('command_execute',
self._handle_command_start)
self.xmpp.del_event_handler('command_next',
self._handle_command_next)
self.xmpp.del_event_handler('command_cancel',
self._handle_command_cancel)
self.xmpp.del_event_handler('command_complete',
self._handle_command_complete)
self.xmpp.del_event_handler('command', self._handle_command_all)
self.xmpp.remove_handler('Ad-Hoc Execute')
self.xmpp['xep_0030'].del_feature(feature=Command.namespace)
self.xmpp['xep_0030'].set_items(node=Command.namespace, items=tuple())
@@ -201,8 +187,27 @@ class XEP_0050(BasePlugin):
def _handle_command(self, iq):
"""Raise command events based on the command action."""
self.xmpp.event('command', iq)
self.xmpp.event('command_%s' % iq['command']['action'], iq)
def _handle_command_all(self, iq: Iq) -> None:
action = iq['command']['action']
sessionid = iq['command']['sessionid']
session = self.sessions.get(sessionid)
if session is None:
return self._handle_command_start(iq)
if action in ('next', 'execute'):
return self._handle_command_next(iq)
if action == 'prev':
return self._handle_command_prev(iq)
if action == 'complete':
return self._handle_command_complete(iq)
if action == 'cancel':
return self._handle_command_cancel(iq)
return None
def _handle_command_start(self, iq):
"""
Process an initial request to execute a command.
@@ -468,7 +473,7 @@ class XEP_0050(BasePlugin):
**kwargs)
def send_command(self, jid, node, ifrom=None, action='execute',
payload=None, sessionid=None, flow=False, **kwargs):
payload=None, sessionid=None, flow=False, **kwargs):
"""
Create and send a command stanza, without using the provided
workflow management APIs.

View File

@@ -59,7 +59,7 @@ class XEP_0077(BasePlugin):
def _force_stream_feature(self, stanza):
if isinstance(stanza, StreamFeatures):
if self.xmpp.use_tls or self.xmpp.use_ssl:
if not self.xmpp.disable_starttls:
if 'starttls' not in self.xmpp.features:
return stanza
elif not isinstance(self.xmpp.socket, ssl.SSLSocket):

View File

@@ -130,7 +130,7 @@ def time(hour=None, min=None, sec=None, micro=None, offset=None, obj=False):
sec = now.second
if micro is None:
micro = now.microsecond
if offset is None:
if offset in (None, 0):
offset = tzutc()
elif not isinstance(offset, dt.tzinfo):
offset = tzoffset(None, offset)
@@ -177,7 +177,7 @@ def datetime(year=None, month=None, day=None, hour=None,
sec = now.second
if micro is None:
micro = now.microsecond
if offset is None:
if offset in (None, 0):
offset = tzutc()
elif not isinstance(offset, dt.tzinfo):
offset = tzoffset(None, offset)

View File

@@ -167,10 +167,7 @@ class XEP_0153(BasePlugin):
data = pres['vcard_temp_update']['photo']
if data is None:
return
elif data == '' or data != self.api['get_hash'](pres['from']):
ifrom = pres['to'] if self.xmpp.is_component else None
self.api['reset_hash'](pres['from'], ifrom=ifrom)
self.xmpp.event('vcard_avatar_update', pres)
self.xmpp.event('vcard_avatar_update', pres)
# =================================================================

View File

@@ -62,7 +62,10 @@ class XEP_0163(BasePlugin):
for ns in namespace:
self.xmpp['xep_0030'].add_feature('%s+notify' % ns,
jid=jid)
asyncio.ensure_future(self.xmpp['xep_0115'].update_caps(jid))
asyncio.ensure_future(
self.xmpp['xep_0115'].update_caps(jid),
loop=self.xmpp.loop,
)
def remove_interest(self, namespace, jid=None):
"""
@@ -81,7 +84,10 @@ class XEP_0163(BasePlugin):
for ns in namespace:
self.xmpp['xep_0030'].del_feature(jid=jid,
feature='%s+notify' % namespace)
asyncio.ensure_future(self.xmpp['xep_0115'].update_caps(jid))
asyncio.ensure_future(
self.xmpp['xep_0115'].update_caps(jid),
loop=self.xmpp.loop,
)
def publish(self, stanza, node=None, id=None, options=None, ifrom=None,
timeout_callback=None, callback=None, timeout=None):

View File

@@ -95,7 +95,10 @@ class XEP_0199(BasePlugin):
self.timeout = timeout
self.keepalive = True
handler = lambda event=None: asyncio.ensure_future(self._keepalive(event))
handler = lambda event=None: asyncio.ensure_future(
self._keepalive(event),
loop=self.xmpp.loop,
)
self.xmpp.schedule('Ping keepalive',
self.interval,
handler,
@@ -109,9 +112,9 @@ class XEP_0199(BasePlugin):
try:
rtt = await self.ping(self.xmpp.boundjid.host, timeout=self.timeout)
except IqTimeout:
log.debug("Did not receive ping back in time." + \
log.debug("Did not receive ping back in time. " + \
"Requesting Reconnect.")
self.xmpp.reconnect()
self.xmpp.reconnect(0.0, "Ping timeout after %ds" % self.timeout)
else:
log.debug('Keepalive RTT: %s' % rtt)

View File

@@ -123,5 +123,5 @@ class EntityTime(ElementBase):
if not isinstance(value, dt.datetime):
date = xep_0082.parse(value)
date = date.astimezone(tzutc())
value = xep_0082.format_datetime(date)[:-1]
value = xep_0082.format_datetime(date)
self._set_sub_text('utc', value)

View File

@@ -40,7 +40,7 @@ class XEP_0202(BasePlugin):
}
def plugin_init(self):
"""Start the XEP-0203 plugin."""
"""Start the XEP-0202 plugin."""
if not self.local_time:
def default_local_time(jid):

View File

@@ -26,7 +26,7 @@ class XEP_0223(BasePlugin):
dependencies = {'xep_0163', 'xep_0060', 'xep_0004'}
profile = {'pubsub#persist_items': True,
'pubsub#send_last_published_item': 'never'}
'pubsub#access_model': 'whitelist'}
def configure(self, node, ifrom=None, callback=None, timeout=None):
"""

View File

@@ -0,0 +1,15 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2018 Maxime “pep” Buquet
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp.plugins.base import register_plugin
from slixmpp.plugins.xep_0335 import stanza
from slixmpp.plugins.xep_0335.stanza import JSON_Container
from slixmpp.plugins.xep_0335.json_containers import XEP_0335
register_plugin(XEP_0335)

View File

@@ -0,0 +1,23 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2018 Maxime “pep” Buquet
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp import Message
from slixmpp.plugins import BasePlugin
from slixmpp.xmlstream import register_stanza_plugin
from slixmpp.plugins.xep_0335 import JSON_Container
from slixmpp.plugins.xep_0335 import stanza
class XEP_0335(BasePlugin):
name = 'xep_0335'
description = 'XEP-0335: JSON Containers'
stanza = stanza
def plugin_init(self):
register_stanza_plugin(Message, JSON_Container)

View File

@@ -0,0 +1,28 @@
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2018 Maxime “pep” Buquet
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import json
from slixmpp.xmlstream import ElementBase
class JSON_Container(ElementBase):
name = 'json'
plugin_attrib = 'json'
namespace = 'urn:xmpp:json:0'
interfaces = {'value'}
def get_value(self):
return json.loads(self.xml.text)
def set_value(self, value):
if not isinstance(value, str):
value = json.dumps(value)
self.xml.text = value
def del_value(self):
self.xml.text = ''

View File

@@ -6,7 +6,6 @@
See the file LICENSE for copying permission.
"""
import asyncio
import logging
import os.path
@@ -31,6 +30,10 @@ class UploadServiceNotFound(FileUploadError):
class FileTooBig(FileUploadError):
pass
class HTTPError(FileUploadError):
def __str__(self):
return 'Could not upload file: %d (%s)' % (self.args[0], self.args[1])
class XEP_0363(BasePlugin):
''' This plugin only supports Python 3.5+ '''
@@ -68,12 +71,18 @@ class XEP_0363(BasePlugin):
def _handle_request(self, iq):
self.xmpp.event('http_upload_request', iq)
async def find_upload_service(self, timeout=None):
results = await self.xmpp['xep_0030'].get_info_from_domain()
async def find_upload_service(self, domain=None, timeout=None):
results = await self.xmpp['xep_0030'].get_info_from_domain(
domain=domain, timeout=timeout)
candidates = []
for info in results:
for identity in info['disco_info']['identities']:
if identity[0] == 'store' and identity[1] == 'file':
candidates.append(info)
for info in candidates:
for feature in info['disco_info']['features']:
if feature == Request.namespace:
return info
def request_slot(self, jid, filename, size, content_type=None, ifrom=None,
@@ -90,11 +99,12 @@ class XEP_0363(BasePlugin):
timeout_callback=timeout_callback)
async def upload_file(self, filename, size=None, content_type=None, *,
input_file=None, ifrom=None, timeout=None,
input_file=None, ifrom=None, domain=None, timeout=None,
callback=None, timeout_callback=None):
''' Helper function which does all of the uploading process. '''
if self.upload_service is None:
info_iq = await self.find_upload_service(timeout=timeout)
info_iq = await self.find_upload_service(
domain=domain, timeout=timeout)
if info_iq is None:
raise UploadServiceNotFound()
self.upload_service = info_iq['from']
@@ -125,7 +135,8 @@ class XEP_0363(BasePlugin):
basename = os.path.basename(filename)
slot_iq = await self.request_slot(self.upload_service, basename, size,
content_type, ifrom, timeout)
content_type, ifrom, timeout,
timeout_callback=timeout_callback)
slot = slot_iq['http_upload_slot']
headers = {
@@ -141,6 +152,8 @@ class XEP_0363(BasePlugin):
data=input_file,
headers=headers,
timeout=timeout)
if response.status >= 400:
raise HTTPError(response.status, await response.text())
log.info('Response code: %d (%s)', response.status, await response.text())
response.close()
return slot['get']['url']

View File

@@ -49,15 +49,17 @@ class XEP_0380(BasePlugin):
register_stanza_plugin(Message, Encryption)
def plugin_end(self):
self.xmpp.remove_handler('Chat State')
def session_bind(self, jid):
self.xmpp.plugin['xep_0030'].add_feature(Encryption.namespace)
def has_eme(self, msg):
return msg.xml.find('{%s}encryption' % Encryption.namespace) is not None
def add_eme(self, msg: Message, namespace: str) -> Message:
msg['eme']['name'] = self.mechanisms[namespace]
msg['eme']['namespace'] = namespace
return msg
def replace_body_with_eme(self, msg):
eme = msg['eme']
namespace = eme['namespace']

View File

@@ -187,6 +187,10 @@ class Iq(RootStanza):
future = asyncio.Future()
# Prevents handlers from existing forever.
if timeout is None:
timeout = 120
def callback_success(result):
type_ = result['type']
if type_ == 'result':

View File

@@ -222,7 +222,7 @@ class SlixTest(unittest.TestCase):
if Matcher is None:
raise ValueError("Unknown matching method.")
test = Matcher(criteria)
self.failUnless(test.match(stanza),
self.assertTrue(test.match(stanza),
"Stanza did not match using %s method:\n" % method + \
"Criteria:\n%s\n" % str(criteria) + \
"Stanza:\n%s" % str(stanza))
@@ -280,7 +280,7 @@ class SlixTest(unittest.TestCase):
debug += "Generated stanza:\n%s\n" % highlight(tostring(stanza2.xml))
result = self.compare(xml, stanza.xml, stanza2.xml)
self.failUnless(result, debug)
self.assertTrue(result, debug)
# ------------------------------------------------------------------
# Methods for simulating stanza streams.
@@ -361,6 +361,7 @@ class SlixTest(unittest.TestCase):
# Some plugins require messages to have ID values. Set
# this to True in tests related to those plugins.
self.xmpp.use_message_ids = False
self.xmpp.use_presence_ids = False
def make_header(self, sto='',
sfrom='',
@@ -487,7 +488,7 @@ class SlixTest(unittest.TestCase):
recv_xml.clear()
recv_xml.attrib = attrib
self.failUnless(
self.assertTrue(
self.compare(xml, recv_xml),
"Stream headers do not match:\nDesired:\n%s\nReceived:\n%s" % (
'%s %s' % (xml.tag, xml.attrib),
@@ -543,7 +544,7 @@ class SlixTest(unittest.TestCase):
xml = self.parse_xml(header2)
sent_xml = self.parse_xml(sent_header2)
self.failUnless(
self.assertTrue(
self.compare(xml, sent_xml),
"Stream headers do not match:\nDesired:\n%s\nSent:\n%s" % (
header, sent_header))
@@ -557,12 +558,12 @@ class SlixTest(unittest.TestCase):
if sent_data is None:
self.fail("No stanza was sent.")
if method == 'exact':
self.failUnless(self.compare(xml, sent_xml),
self.assertTrue(self.compare(xml, sent_xml),
"Features do not match.\nDesired:\n%s\nReceived:\n%s" % (
highlight(tostring(xml)), highlight(tostring(sent_xml))))
elif method == 'mask':
matcher = MatchXMLMask(xml)
self.failUnless(matcher.match(sent_xml),
self.assertTrue(matcher.match(sent_xml),
"Stanza did not match using %s method:\n" % method + \
"Criteria:\n%s\n" % highlight(tostring(xml)) + \
"Stanza:\n%s" % highlight(tostring(sent_xml)))

View File

@@ -18,6 +18,9 @@ class Cache:
def store(self, key, value):
raise NotImplementedError
def remove(self, key):
raise NotImplemented
class PerJidCache:
def retrieve_by_jid(self, jid, key):
raise NotImplementedError
@@ -25,6 +28,9 @@ class PerJidCache:
def store_by_jid(self, jid, key, value):
raise NotImplementedError
def remove_by_jid(self, jid, key):
raise NotImplementedError
class MemoryCache(Cache):
def __init__(self):
self.cache = {}
@@ -36,6 +42,11 @@ class MemoryCache(Cache):
self.cache[key] = value
return True
def remove(self, key):
if key in self.cache:
del self.cache[key]
return True
class MemoryPerJidCache(PerJidCache):
def __init__(self):
self.cache = {}
@@ -51,6 +62,12 @@ class MemoryPerJidCache(PerJidCache):
cache[key] = value
return True
def remove_by_jid(self, jid, key):
cache = self.cache.get(jid, None)
if cache is not None and key in cache:
del cache[key]
return True
class FileSystemStorage:
def __init__(self, encode, decode, binary):
self.encode = encode if encode is not None else lambda x: x
@@ -67,7 +84,10 @@ class FileSystemStorage:
log.debug('%s not present in cache', key)
except OSError:
log.debug('Failed to read %s from cache:', key, exc_info=True)
return None
except Exception:
log.debug('Failed to decode %s from cache:', key, exc_info=True)
log.debug('Removing %s entry', key)
self._remove(directory, key)
def _store(self, directory, key, value):
filename = os.path.join(directory, key.replace('/', '_'))
@@ -79,6 +99,17 @@ class FileSystemStorage:
except OSError:
log.debug('Failed to store %s to cache:', key, exc_info=True)
return False
except Exception:
log.debug('Failed to encode %s to cache:', key, exc_info=True)
def _remove(self, directory, key):
filename = os.path.join(directory, key.replace('/', '_'))
try:
os.remove(filename)
except OSError:
log.debug('Failed to remove %s from cache:', key, exc_info=True)
return False
return True
class FileSystemCache(Cache, FileSystemStorage):
def __init__(self, directory, cache_type, *, encode=None, decode=None, binary=False):
@@ -91,6 +122,9 @@ class FileSystemCache(Cache, FileSystemStorage):
def store(self, key, value):
return self._store(self.base_dir, key, value)
def remove(self, key):
return self._remove(self.base_dir, key)
class FileSystemPerJidCache(PerJidCache, FileSystemStorage):
def __init__(self, directory, cache_type, *, encode=None, decode=None, binary=False):
FileSystemStorage.__init__(self, encode, decode, binary)
@@ -103,3 +137,7 @@ class FileSystemPerJidCache(PerJidCache, FileSystemStorage):
def store_by_jid(self, jid, key, value):
directory = os.path.join(self.base_dir, jid)
return self._store(directory, key, value)
def remove_by_jid(self, jid, key):
directory = os.path.join(self.base_dir, jid)
return self._remove(directory, key)

View File

@@ -516,13 +516,13 @@ else:
def setup(self, name):
authzid = self.credentials['authzid']
if not authzid:
authzid = 'xmpp@%s' % self.credentials['service-name']
authzid = 'xmpp@' + self.credentials['service-name'].decode()
_, self.gss = kerberos.authGSSClientInit(authzid)
self.step = 0
def process(self, challenge=b''):
b64_challenge = b64encode(challenge)
b64_challenge = b64encode(challenge).decode('ascii')
try:
if self.step == 0:
result = kerberos.authGSSClientStep(self.gss, b64_challenge)
@@ -536,7 +536,7 @@ else:
kerberos.authGSSClientUnwrap(self.gss, b64_challenge)
resp = kerberos.authGSSClientResponse(self.gss)
kerberos.authGSSClientWrap(self.gss, resp, username)
kerberos.authGSSClientWrap(self.gss, resp, username.decode())
resp = kerberos.authGSSClientResponse(self.gss)
except kerberos.GSSError as e:

View File

@@ -9,5 +9,5 @@
# We don't want to have to import the entire library
# just to get the version info for setup.py
__version__ = '1.4.0'
__version_info__ = (1, 4, 0)
__version__ = '1.4.2'
__version_info__ = (1, 4, 2)

View File

@@ -177,8 +177,9 @@ def fix_ns(xpath, split=False, propagate_ns=True, default_ns=''):
if '}' in ns_block:
# Apply the found namespace to following elements
# that do not have namespaces.
namespace = ns_block.split('}')[0]
elements = ns_block.split('}')[1].split('/')
ns_block_split = ns_block.split('}')
namespace = ns_block_split[0]
elements = ns_block_split[1].split('/')
else:
# Apply the stanza's namespace to the following
# elements since no namespace was provided.
@@ -1291,15 +1292,6 @@ class ElementBase(object):
def __bool__(self):
"""Stanza objects should be treated as True in boolean contexts.
Python 3.x version.
"""
return True
def __nonzero__(self):
"""Stanza objects should be treated as True in boolean contexts.
Python 2.x version.
"""
return True

View File

@@ -45,11 +45,12 @@ def tostring(xml=None, xmlns='', stream=None, outbuffer='',
output = [outbuffer]
# Extract the element's tag name.
tag_name = xml.tag.split('}', 1)[-1]
tag_split = xml.tag.split('}', 1)
tag_name = tag_split[-1]
# Extract the element's namespace if it is defined.
if '}' in xml.tag:
tag_xmlns = xml.tag.split('}', 1)[0][1:]
tag_xmlns = tag_split[0][1:]
else:
tag_xmlns = ''
@@ -82,8 +83,9 @@ def tostring(xml=None, xmlns='', stream=None, outbuffer='',
if '}' not in attrib:
output.append(' %s="%s"' % (attrib, value))
else:
attrib_ns = attrib.split('}')[0][1:]
attrib = attrib.split('}')[1]
attrib_split = attrib.split('}')
attrib_ns = attrib_split[0][1:]
attrib = attrib_split[1]
if attrib_ns == XML_NS:
output.append(' xml:%s="%s"' % (attrib, value))
elif stream and attrib_ns in stream.namespace_map:
@@ -144,10 +146,7 @@ def escape(text, use_cdata=False):
'"': '&quot;'}
if not use_cdata:
text = list(text)
for i, c in enumerate(text):
text[i] = escapes.get(c, c)
return ''.join(text)
return ''.join(escapes.get(c, c) for c in text)
else:
escape_needed = False
for c in text:

View File

@@ -215,6 +215,9 @@ class XMLStream(asyncio.BaseProtocol):
#: ``_xmpp-client._tcp`` service.
self.dns_service = None
#: The reason why we are disconnecting from the server
self.disconnect_reason = None
#: An asyncio Future being done when the stream is disconnected.
self.disconnected = asyncio.Future()
@@ -268,6 +271,7 @@ class XMLStream(asyncio.BaseProtocol):
localhost
"""
self.disconnect_reason = None
self.cancel_connection_attempt()
if host and port:
self.address = (host, int(port))
@@ -285,7 +289,10 @@ class XMLStream(asyncio.BaseProtocol):
self.disable_starttls = disable_starttls
self.event("connecting")
self._current_connection_attempt = asyncio.ensure_future(self._connect_routine())
self._current_connection_attempt = asyncio.ensure_future(
self._connect_routine(),
loop=self.loop,
)
async def _connect_routine(self):
self.event_when_connected = "connected"
@@ -306,7 +313,9 @@ class XMLStream(asyncio.BaseProtocol):
else:
ssl_context = None
await asyncio.sleep(self.connect_loop_wait)
await asyncio.sleep(self.connect_loop_wait, loop=self.loop)
if self._current_connection_attempt is None:
return
try:
await self.loop.create_connection(lambda: self,
self.address[0],
@@ -320,8 +329,13 @@ class XMLStream(asyncio.BaseProtocol):
except OSError as e:
log.debug('Connection failed: %s', e)
self.event("connection_failed", e)
if self._current_connection_attempt is None:
return
self.connect_loop_wait = self.connect_loop_wait * 2 + 1
self._current_connection_attempt = asyncio.ensure_future(self._connect_routine())
self._current_connection_attempt = asyncio.ensure_future(
self._connect_routine(),
loop=self.loop,
)
def process(self, *, forever=True, timeout=None):
"""Process all the available XMPP events (receiving or sending data on the
@@ -336,10 +350,10 @@ class XMLStream(asyncio.BaseProtocol):
else:
self.loop.run_until_complete(self.disconnected)
else:
tasks = [asyncio.sleep(timeout)]
tasks = [asyncio.sleep(timeout, loop=self.loop)]
if not forever:
tasks.append(self.disconnected)
self.loop.run_until_complete(asyncio.wait(tasks))
self.loop.run_until_complete(asyncio.wait(tasks, loop=self.loop))
def init_parser(self):
"""init the XML parser. The parser must always be reset for each new
@@ -392,7 +406,9 @@ class XMLStream(asyncio.BaseProtocol):
if self.xml_depth == 0:
# The stream's root element has closed,
# terminating the stream.
self.end_session_on_disconnect = True
log.debug("End of stream received")
self.disconnect_reason = "End of stream"
self.abort()
elif self.xml_depth == 1:
# A stanza is an XML element that is a direct child of
@@ -427,7 +443,7 @@ class XMLStream(asyncio.BaseProtocol):
closure of the TCP connection
"""
log.info("connection_lost: %s", (exception,))
self.event("disconnected")
self.event("disconnected", self.disconnect_reason or exception and exception.strerror)
if self.end_session_on_disconnect:
self.event('session_end')
# All these objects are associated with one TCP connection. Since
@@ -447,22 +463,24 @@ class XMLStream(asyncio.BaseProtocol):
self._current_connection_attempt.cancel()
self._current_connection_attempt = None
def disconnect(self, wait=2.0):
def disconnect(self, wait=2.0, reason=None):
"""Close the XML stream and wait for an acknowldgement from the server for
at most `wait` seconds. After the given number of seconds has
passed without a response from the serveur, or when the server
successfully responds with a closure of its own stream, abort() is
called. If wait is 0.0, this is almost equivalent to calling abort()
directly.
called. If wait is 0.0, this will call abort() directly without closing
the stream.
Does nothing if we are not connected.
:param wait: Time to wait for a response from the server.
"""
self.disconnect_reason = reason
self.cancel_connection_attempt()
if self.transport:
self.send_raw(self.stream_footer)
if wait > 0.0:
self.send_raw(self.stream_footer)
self.schedule('Disconnect wait', wait,
self.abort, repeat=False)
@@ -478,13 +496,13 @@ class XMLStream(asyncio.BaseProtocol):
self.disconnected.set_result(True)
self.disconnected = asyncio.Future()
def reconnect(self, wait=2.0):
def reconnect(self, wait=2.0, reason="Reconnecting"):
"""Calls disconnect(), and once we are disconnected (after the timeout, or
when the server acknowledgement is received), call connect()
"""
log.debug("reconnecting...")
self.disconnect(wait)
self.add_event_handler('disconnected', self.connect, disposable=True)
self.disconnect(wait, reason)
self.add_event_handler('disconnected', lambda event: self.connect(), disposable=True)
def configure_socket(self):
"""Set timeout and other options for self.socket.
@@ -781,7 +799,10 @@ class XMLStream(asyncio.BaseProtocol):
old_exception(e)
else:
self.exception(e)
asyncio.ensure_future(handler_callback_routine(handler_callback))
asyncio.ensure_future(
handler_callback_routine(handler_callback),
loop=self.loop,
)
else:
try:
handler_callback(data)
@@ -995,4 +1016,3 @@ class XMLStream(asyncio.BaseProtocol):
:param exception: An unhandled exception object.
"""
pass

74
tests/test_cache.py Normal file
View File

@@ -0,0 +1,74 @@
import unittest
from slixmpp.test import SlixTest
from slixmpp.util import (
MemoryCache, MemoryPerJidCache,
FileSystemCache, FileSystemPerJidCache
)
from tempfile import TemporaryDirectory
class TestCacheClass(SlixTest):
def testMemoryCache(self):
cache = MemoryCache()
cache.store("test", "test_value")
self.assertEqual(cache.retrieve("test"), "test_value")
self.assertEqual(cache.retrieve("test2"), None)
cache.remove("test")
self.assertEqual(cache.retrieve("test"), None)
def testMemoryPerJidcache(self):
cache = MemoryPerJidCache()
cache.store_by_jid("test@example.com", "test", "test_value")
self.assertEqual(
cache.retrieve_by_jid("test@example.com", "test"),
"test_value"
)
cache.remove_by_jid("test@example.com", "test")
self.assertEqual(
cache.retrieve_by_jid("test@example.com", "test"),
None
)
def testFileSystemCache(self):
def failing_decode(value):
if value == "failme":
raise Exception("you failed")
return value
with TemporaryDirectory() as tmpdir:
cache = FileSystemCache(tmpdir, "test", decode=failing_decode)
cache.store("test", "test_value")
cache.store("test2", "failme")
self.assertEqual(
cache.retrieve("test"),
"test_value"
)
cache.remove("test")
self.assertEqual(
cache.retrieve("test"),
None
)
self.assertEqual(
cache.retrieve("test2"),
None
)
def testFileSystemPerJidCache(self):
with TemporaryDirectory() as tmpdir:
cache = FileSystemPerJidCache(tmpdir, "test")
cache.store_by_jid("test@example.com", "test", "test_value")
self.assertEqual(
cache.retrieve_by_jid("test@example.com", "test"),
"test_value"
)
cache.remove_by_jid("test@example.com", "test")
self.assertEqual(
cache.retrieve_by_jid("test@example.com", "test"),
None
)
suite = unittest.TestLoader().loadTestsFromTestCase(TestCacheClass)

View File

@@ -23,7 +23,7 @@ class TestEvents(SlixTest):
self.xmpp.event("test_event")
msg = "Event was not triggered the correct number of times: %s"
self.failUnless(happened == [True, True], msg)
self.assertTrue(happened == [True, True], msg)
def testDelEvent(self):
"""Test handler working, then deleted and not triggered"""
@@ -41,7 +41,7 @@ class TestEvents(SlixTest):
self.xmpp.event("test_event", {})
msg = "Event was not triggered the correct number of times: %s"
self.failUnless(happened == [True], msg % happened)
self.assertTrue(happened == [True], msg % happened)
def testAddDelAddEvent(self):
"""Test adding, then removing, then adding an event handler."""
@@ -61,7 +61,7 @@ class TestEvents(SlixTest):
self.xmpp.event("test_event", {})
msg = "Event was not triggered the correct number of times: %s"
self.failUnless(happened == [True, True], msg % happened)
self.assertTrue(happened == [True, True], msg % happened)
def testDisposableEvent(self):
"""Test disposable handler working, then not being triggered again."""
@@ -78,7 +78,7 @@ class TestEvents(SlixTest):
self.xmpp.event("test_event", {})
msg = "Event was not triggered the correct number of times: %s"
self.failUnless(happened == [True], msg % happened)
self.assertTrue(happened == [True], msg % happened)
suite = unittest.TestLoader().loadTestsFromTestCase(TestEvents)

View File

@@ -16,11 +16,11 @@ class TestOverall(unittest.TestCase):
"""Testing all modules by compiling them"""
src = '.%sslixmpp' % os.sep
rx = re.compile('/[.]svn|.*26.*')
self.failUnless(compileall.compile_dir(src, rx=rx, quiet=True))
self.assertTrue(compileall.compile_dir(src, rx=rx, quiet=True))
def testTabNanny(self):
"""Testing that indentation is consistent"""
self.failIf(tabnanny.check('..%sslixmpp' % os.sep))
self.assertFalse(tabnanny.check('..%sslixmpp' % os.sep))
suite = unittest.TestLoader().loadTestsFromTestCase(TestOverall)

View File

@@ -9,37 +9,37 @@ class TestStanzaBase(SlixTest):
"""Test the 'to' interface of StanzaBase."""
stanza = StanzaBase()
stanza['to'] = 'user@example.com'
self.failUnless(str(stanza['to']) == 'user@example.com',
self.assertTrue(str(stanza['to']) == 'user@example.com',
"Setting and retrieving stanza 'to' attribute did not work.")
def testFrom(self):
"""Test the 'from' interface of StanzaBase."""
stanza = StanzaBase()
stanza['from'] = 'user@example.com'
self.failUnless(str(stanza['from']) == 'user@example.com',
self.assertTrue(str(stanza['from']) == 'user@example.com',
"Setting and retrieving stanza 'from' attribute did not work.")
def testPayload(self):
"""Test the 'payload' interface of StanzaBase."""
stanza = StanzaBase()
self.failUnless(stanza['payload'] == [],
self.assertTrue(stanza['payload'] == [],
"Empty stanza does not have an empty payload.")
stanza['payload'] = ET.Element("{foo}foo")
self.failUnless(len(stanza['payload']) == 1,
self.assertTrue(len(stanza['payload']) == 1,
"Stanza contents and payload do not match.")
stanza['payload'] = ET.Element('{bar}bar')
self.failUnless(len(stanza['payload']) == 2,
self.assertTrue(len(stanza['payload']) == 2,
"Stanza payload was not appended.")
del stanza['payload']
self.failUnless(stanza['payload'] == [],
self.assertTrue(stanza['payload'] == [],
"Stanza payload not cleared after deletion.")
stanza['payload'] = [ET.Element('{foo}foo'),
ET.Element('{bar}bar')]
self.failUnless(len(stanza['payload']) == 2,
self.assertTrue(len(stanza['payload']) == 2,
"Adding multiple elements to stanza's payload did not work.")
def testClear(self):
@@ -49,9 +49,9 @@ class TestStanzaBase(SlixTest):
stanza['payload'] = ET.Element("{foo}foo")
stanza.clear()
self.failUnless(stanza['payload'] == [],
self.assertTrue(stanza['payload'] == [],
"Stanza payload was not cleared after calling .clear()")
self.failUnless(str(stanza['to']) == "user@example.com",
self.assertTrue(str(stanza['to']) == "user@example.com",
"Stanza attributes were not preserved after calling .clear()")
def testReply(self):
@@ -63,9 +63,9 @@ class TestStanzaBase(SlixTest):
stanza = stanza.reply()
self.failUnless(str(stanza['to'] == "sender@example.com"),
self.assertTrue(str(stanza['to'] == "sender@example.com"),
"Stanza reply did not change 'to' attribute.")
self.failUnless(stanza['payload'] == [],
self.assertTrue(stanza['payload'] == [],
"Stanza reply did not empty stanza payload.")
def testError(self):
@@ -73,7 +73,7 @@ class TestStanzaBase(SlixTest):
stanza = StanzaBase()
stanza['type'] = 'get'
stanza.error()
self.failUnless(stanza['type'] == 'error',
self.assertTrue(stanza['type'] == 'error',
"Stanza type is not 'error' after calling error()")

View File

@@ -17,7 +17,7 @@ class TestElementBase(SlixTest):
"{%s}bar" % ns,
"{abc}baz",
"{%s}more" % ns])
self.failUnless(expected == result,
self.assertTrue(expected == result,
"Incorrect namespace fixing result: %s" % str(result))
@@ -80,7 +80,7 @@ class TestElementBase(SlixTest):
'lang': '',
'bar': 'c',
'baz': ''}]}
self.failUnless(values == expected,
self.assertTrue(values == expected,
"Unexpected stanza values:\n%s\n%s" % (str(expected), str(values)))
@@ -170,13 +170,13 @@ class TestElementBase(SlixTest):
'meh': ''}
for interface, value in expected.items():
result = stanza[interface]
self.failUnless(result == value,
self.assertTrue(result == value,
"Incorrect stanza interface access result: %s" % result)
# Test plugin interfaces
self.failUnless(isinstance(stanza['foobar'], TestStanzaPlugin),
self.assertTrue(isinstance(stanza['foobar'], TestStanzaPlugin),
"Incorrect plugin object result.")
self.failUnless(stanza['foobar']['fizz'] == 'c',
self.assertTrue(stanza['foobar']['fizz'] == 'c',
"Incorrect plugin subvalue result.")
def testSetItem(self):
@@ -269,7 +269,7 @@ class TestElementBase(SlixTest):
<foo xmlns="foo" />
""")
self.failUnless(stanza._get_attr('bar') == '',
self.assertTrue(stanza._get_attr('bar') == '',
"Incorrect value returned for an unset XML attribute.")
stanza._set_attr('bar', 'a')
@@ -279,7 +279,7 @@ class TestElementBase(SlixTest):
<foo xmlns="foo" bar="a" baz="b" />
""")
self.failUnless(stanza._get_attr('bar') == 'a',
self.assertTrue(stanza._get_attr('bar') == 'a',
"Retrieved XML attribute value is incorrect.")
stanza._set_attr('bar', None)
@@ -289,7 +289,7 @@ class TestElementBase(SlixTest):
<foo xmlns="foo" />
""")
self.failUnless(stanza._get_attr('bar', 'c') == 'c',
self.assertTrue(stanza._get_attr('bar', 'c') == 'c',
"Incorrect default value returned for an unset XML attribute.")
def testGetSubText(self):
@@ -311,7 +311,7 @@ class TestElementBase(SlixTest):
return self._get_sub_text("wrapper/bar", default="not found")
stanza = TestStanza()
self.failUnless(stanza['bar'] == 'not found',
self.assertTrue(stanza['bar'] == 'not found',
"Default _get_sub_text value incorrect.")
stanza['bar'] = 'found'
@@ -322,7 +322,7 @@ class TestElementBase(SlixTest):
</wrapper>
</foo>
""")
self.failUnless(stanza['bar'] == 'found',
self.assertTrue(stanza['bar'] == 'found',
"_get_sub_text value incorrect: %s." % stanza['bar'])
def testSubElement(self):
@@ -481,45 +481,45 @@ class TestElementBase(SlixTest):
register_stanza_plugin(TestStanza, TestStanzaPlugin)
stanza = TestStanza()
self.failUnless(stanza.match("foo"),
self.assertTrue(stanza.match("foo"),
"Stanza did not match its own tag name.")
self.failUnless(stanza.match("{foo}foo"),
self.assertTrue(stanza.match("{foo}foo"),
"Stanza did not match its own namespaced name.")
stanza['bar'] = 'a'
self.failUnless(stanza.match("foo@bar=a"),
self.assertTrue(stanza.match("foo@bar=a"),
"Stanza did not match its own name with attribute value check.")
stanza['baz'] = 'b'
self.failUnless(stanza.match("foo@bar=a@baz=b"),
self.assertTrue(stanza.match("foo@bar=a@baz=b"),
"Stanza did not match its own name with multiple attributes.")
stanza['qux'] = 'c'
self.failUnless(stanza.match("foo/qux"),
self.assertTrue(stanza.match("foo/qux"),
"Stanza did not match with subelements.")
stanza['qux'] = ''
self.failUnless(stanza.match("foo/qux") == False,
self.assertTrue(stanza.match("foo/qux") == False,
"Stanza matched missing subinterface element.")
self.failUnless(stanza.match("foo/bar") == False,
self.assertTrue(stanza.match("foo/bar") == False,
"Stanza matched nonexistent element.")
stanza['plugin']['attrib'] = 'c'
self.failUnless(stanza.match("foo/plugin@attrib=c"),
self.assertTrue(stanza.match("foo/plugin@attrib=c"),
"Stanza did not match with plugin and attribute.")
self.failUnless(stanza.match("foo/{http://test/slash/bar}plugin"),
self.assertTrue(stanza.match("foo/{http://test/slash/bar}plugin"),
"Stanza did not match with namespaced plugin.")
substanza = TestSubStanza()
substanza['attrib'] = 'd'
stanza.append(substanza)
self.failUnless(stanza.match("foo/sub@attrib=d"),
self.assertTrue(stanza.match("foo/sub@attrib=d"),
"Stanza did not match with substanzas and attribute.")
self.failUnless(stanza.match("foo/{baz}sub"),
self.assertTrue(stanza.match("foo/{baz}sub"),
"Stanza did not match with namespaced substanza.")
def testComparisons(self):
@@ -533,19 +533,19 @@ class TestElementBase(SlixTest):
stanza1 = TestStanza()
stanza1['bar'] = 'a'
self.failUnless(stanza1,
self.assertTrue(stanza1,
"Stanza object does not evaluate to True")
stanza2 = TestStanza()
stanza2['baz'] = 'b'
self.failUnless(stanza1 != stanza2,
self.assertTrue(stanza1 != stanza2,
"Different stanza objects incorrectly compared equal.")
stanza1['baz'] = 'b'
stanza2['bar'] = 'a'
self.failUnless(stanza1 == stanza2,
self.assertTrue(stanza1 == stanza2,
"Equal stanzas incorrectly compared inequal.")
def testKeys(self):
@@ -561,12 +561,12 @@ class TestElementBase(SlixTest):
stanza = TestStanza()
self.failUnless(set(stanza.keys()) == {'lang', 'bar', 'baz'},
self.assertTrue(set(stanza.keys()) == {'lang', 'bar', 'baz'},
"Returned set of interface keys does not match expected.")
stanza.enable('qux')
self.failUnless(set(stanza.keys()) == {'lang', 'bar', 'baz', 'qux'},
self.assertTrue(set(stanza.keys()) == {'lang', 'bar', 'baz', 'qux'},
"Incorrect set of interface and plugin keys.")
def testGet(self):
@@ -580,10 +580,10 @@ class TestElementBase(SlixTest):
stanza = TestStanza()
stanza['bar'] = 'a'
self.failUnless(stanza.get('bar') == 'a',
self.assertTrue(stanza.get('bar') == 'a',
"Incorrect value returned by stanza.get")
self.failUnless(stanza.get('baz', 'b') == 'b',
self.assertTrue(stanza.get('baz', 'b') == 'b',
"Incorrect default value returned by stanza.get")
def testSubStanzas(self):
@@ -608,7 +608,7 @@ class TestElementBase(SlixTest):
substanza2['qux'] = 'b'
# Test appending substanzas
self.failUnless(len(stanza) == 0,
self.assertTrue(len(stanza) == 0,
"Incorrect empty stanza size.")
stanza.append(substanza1)
@@ -617,7 +617,7 @@ class TestElementBase(SlixTest):
<foobar qux="a" />
</foo>
""", use_values=False)
self.failUnless(len(stanza) == 1,
self.assertTrue(len(stanza) == 1,
"Incorrect stanza size with 1 substanza.")
stanza.append(substanza2)
@@ -627,7 +627,7 @@ class TestElementBase(SlixTest):
<foobar qux="b" />
</foo>
""", use_values=False)
self.failUnless(len(stanza) == 2,
self.assertTrue(len(stanza) == 2,
"Incorrect stanza size with 2 substanzas.")
# Test popping substanzas
@@ -643,7 +643,7 @@ class TestElementBase(SlixTest):
results = []
for substanza in stanza:
results.append(substanza['qux'])
self.failUnless(results == ['b', 'a'],
self.assertTrue(results == ['b', 'a'],
"Iteration over substanzas failed: %s." % str(results))
def testCopy(self):
@@ -659,11 +659,11 @@ class TestElementBase(SlixTest):
stanza2 = stanza1.__copy__()
self.failUnless(stanza1 == stanza2,
self.assertTrue(stanza1 == stanza2,
"Copied stanzas are not equal to each other.")
stanza1['baz'] = 'b'
self.failUnless(stanza1 != stanza2,
self.assertTrue(stanza1 != stanza2,
"Divergent stanza copies incorrectly compared equal.")
def testExtension(self):
@@ -701,7 +701,7 @@ class TestElementBase(SlixTest):
</foo>
""")
self.failUnless(stanza['extended'] == 'testing',
self.assertTrue(stanza['extended'] == 'testing',
"Could not retrieve stanza extension value.")
del stanza['extended']

View File

@@ -34,7 +34,7 @@ class TestErrorStanzas(SlixTest):
</message>
""")
self.failUnless(msg['error']['condition'] == 'item-not-found', "Error condition doesn't match.")
self.assertTrue(msg['error']['condition'] == 'item-not-found', "Error condition doesn't match.")
msg['error']['condition'] = 'resource-constraint'

View File

@@ -62,30 +62,30 @@ class TestGmail(SlixTest):
iq = self.Iq(xml=xml)
mailbox = iq['mailbox']
self.failUnless(mailbox['result-time'] == '1118012394209', "result-time doesn't match")
self.failUnless(mailbox['url'] == 'http://mail.google.com/mail', "url doesn't match")
self.failUnless(mailbox['matched'] == '95', "total-matched incorrect")
self.failUnless(mailbox['estimate'] == False, "total-estimate incorrect")
self.failUnless(len(mailbox['threads']) == 1, "could not extract message threads")
self.assertTrue(mailbox['result-time'] == '1118012394209', "result-time doesn't match")
self.assertTrue(mailbox['url'] == 'http://mail.google.com/mail', "url doesn't match")
self.assertTrue(mailbox['matched'] == '95', "total-matched incorrect")
self.assertTrue(mailbox['estimate'] == False, "total-estimate incorrect")
self.assertTrue(len(mailbox['threads']) == 1, "could not extract message threads")
thread = mailbox['threads'][0]
self.failUnless(thread['tid'] == '1172320964060972012', "thread tid doesn't match")
self.failUnless(thread['participation'] == '1', "thread participation incorrect")
self.failUnless(thread['messages'] == '28', "thread message count incorrect")
self.failUnless(thread['date'] == '1118012394209', "thread date doesn't match")
self.failUnless(thread['url'] == 'http://mail.google.com/mail?view=cv', "thread url doesn't match")
self.failUnless(thread['labels'] == 'act1scene3', "thread labels incorrect")
self.failUnless(thread['subject'] == 'Put thy rapier up.', "thread subject doesn't match")
self.failUnless(thread['snippet'] == "Ay, ay, a scratch, a scratch; marry, 'tis enough.", "snippet doesn't match")
self.failUnless(len(thread['senders']) == 3, "could not extract senders")
self.assertTrue(thread['tid'] == '1172320964060972012', "thread tid doesn't match")
self.assertTrue(thread['participation'] == '1', "thread participation incorrect")
self.assertTrue(thread['messages'] == '28', "thread message count incorrect")
self.assertTrue(thread['date'] == '1118012394209', "thread date doesn't match")
self.assertTrue(thread['url'] == 'http://mail.google.com/mail?view=cv', "thread url doesn't match")
self.assertTrue(thread['labels'] == 'act1scene3', "thread labels incorrect")
self.assertTrue(thread['subject'] == 'Put thy rapier up.', "thread subject doesn't match")
self.assertTrue(thread['snippet'] == "Ay, ay, a scratch, a scratch; marry, 'tis enough.", "snippet doesn't match")
self.assertTrue(len(thread['senders']) == 3, "could not extract senders")
sender1 = thread['senders'][0]
self.failUnless(sender1['name'] == 'Me', "sender name doesn't match")
self.failUnless(sender1['address'] == 'romeo@gmail.com', "sender address doesn't match")
self.failUnless(sender1['originator'] == True, "sender originator incorrect")
self.failUnless(sender1['unread'] == False, "sender unread incorrectly True")
self.assertTrue(sender1['name'] == 'Me', "sender name doesn't match")
self.assertTrue(sender1['address'] == 'romeo@gmail.com', "sender address doesn't match")
self.assertTrue(sender1['originator'] == True, "sender originator incorrect")
self.assertTrue(sender1['unread'] == False, "sender unread incorrectly True")
sender2 = thread['senders'][2]
self.failUnless(sender2['unread'] == True, "sender unread incorrectly False")
self.assertTrue(sender2['unread'] == True, "sender unread incorrectly False")
suite = unittest.TestLoader().loadTestsFromTestCase(TestGmail)

View File

@@ -70,7 +70,7 @@ class TestIqStanzas(SlixTest):
</iq>
""")
self.failUnless(iq['query'] == 'query_ns2', "Query namespace doesn't match")
self.assertTrue(iq['query'] == 'query_ns2', "Query namespace doesn't match")
del iq['query']
self.check(iq, """

View File

@@ -18,7 +18,7 @@ class TestMessageStanzas(SlixTest):
msg['type'] = 'groupchat'
msg['body'] = "this is a message"
msg = msg.reply()
self.failUnless(str(msg['to']) == 'room@someservice.someserver.tld')
self.assertTrue(str(msg['to']) == 'room@someservice.someserver.tld')
def testHTMLPlugin(self):
"Test message/html/body stanza"

View File

@@ -15,7 +15,7 @@ class TestPresenceStanzas(SlixTest):
p = self.Presence()
p['type'] = 'available'
self.check(p, "<presence />")
self.failUnless(p['type'] == 'available',
self.assertTrue(p['type'] == 'available',
"Incorrect presence['type'] for type 'available': %s" % p['type'])
for showtype in ['away', 'chat', 'dnd', 'xa']:
@@ -23,7 +23,7 @@ class TestPresenceStanzas(SlixTest):
self.check(p, """
<presence><show>%s</show></presence>
""" % showtype)
self.failUnless(p['type'] == showtype,
self.assertTrue(p['type'] == showtype,
"Incorrect presence['type'] for type '%s'" % showtype)
p['type'] = None
@@ -47,10 +47,10 @@ class TestPresenceStanzas(SlixTest):
c.add_event_handler("changed_status", handlechangedpresence)
c._handle_presence(p)
self.failUnless(happened == [],
self.assertTrue(happened == [],
"changed_status event triggered for extra unavailable presence")
roster = c.roster['crap@wherever']
self.failUnless(roster['bill@chadmore.com'].resources == {},
self.assertTrue(roster['bill@chadmore.com'].resources == {},
"Roster updated for superfulous unavailable presence")
def testNickPlugin(self):

View File

@@ -61,7 +61,7 @@ class TestRosterStanzas(SlixTest):
debug = "Roster items don't match after retrieval."
debug += "\nReturned: %s" % str(iq['roster']['items'])
debug += "\nExpected: %s" % str(expected)
self.failUnless(iq['roster']['items'] == expected, debug)
self.assertTrue(iq['roster']['items'] == expected, debug)
def testDelItems(self):
"""Test clearing items from a roster stanza."""

View File

@@ -258,7 +258,7 @@ class TestDisco(SlixTest):
('client', 'pc', 'no', None),
('client', 'pc', 'en', None),
('client', 'pc', 'fr', None)}
self.failUnless(iq['disco_info']['identities'] == expected,
self.assertTrue(iq['disco_info']['identities'] == expected,
"Identities do not match:\n%s\n%s" % (
expected,
iq['disco_info']['identities']))
@@ -276,7 +276,7 @@ class TestDisco(SlixTest):
expected = {('client', 'pc', 'no', None)}
result = iq['disco_info'].get_identities(lang='no')
self.failUnless(result == expected,
self.assertTrue(result == expected,
"Identities do not match:\n%s\n%s" % (
expected, result))
@@ -337,7 +337,7 @@ class TestDisco(SlixTest):
iq['disco_info'].add_feature('baz')
expected = {'foo', 'bar', 'baz'}
self.failUnless(iq['disco_info']['features'] == expected,
self.assertTrue(iq['disco_info']['features'] == expected,
"Features do not match:\n%s\n%s" % (
expected,
iq['disco_info']['features']))
@@ -475,7 +475,7 @@ class TestDisco(SlixTest):
expected = {('user@localhost', None, None),
('user@localhost', 'foo', None),
('test@localhost', 'bar', 'Tester')}
self.failUnless(iq['disco_items']['items'] == expected,
self.assertTrue(iq['disco_items']['items'] == expected,
"Items do not match:\n%s\n%s" % (
expected,
iq['disco_items']['items']))

View File

@@ -17,13 +17,13 @@ class TestAdHocCommandStanzas(SlixTest):
iq['command']['node'] = 'foo'
iq['command']['action'] = 'execute'
self.failUnless(iq['command']['action'] == 'execute')
self.assertTrue(iq['command']['action'] == 'execute')
iq['command']['action'] = 'complete'
self.failUnless(iq['command']['action'] == 'complete')
self.assertTrue(iq['command']['action'] == 'complete')
iq['command']['action'] = 'cancel'
self.failUnless(iq['command']['action'] == 'cancel')
self.assertTrue(iq['command']['action'] == 'cancel')
def testSetActions(self):
"""Test setting next actions in a command stanza."""
@@ -98,7 +98,7 @@ class TestAdHocCommandStanzas(SlixTest):
('error', "I can't let you do that")]
iq['command']['notes'] = notes
self.failUnless(iq['command']['notes'] == notes,
self.assertTrue(iq['command']['notes'] == notes,
"Notes don't match: %s %s" % (notes, iq['command']['notes']))
self.check(iq, """

View File

@@ -24,7 +24,7 @@ class TestSetStanzas(SlixTest):
"""
s = Set(ET.fromstring(xml_string))
expected = '10'
self.failUnless(s['first_index'] == expected)
self.assertTrue(s['first_index'] == expected)
def testDelFirstIndex(self):
xml_string = """
@@ -57,7 +57,7 @@ class TestSetStanzas(SlixTest):
"""
s = Set(ET.fromstring(xml_string))
expected = True
self.failUnless(s['before'] == expected)
self.assertTrue(s['before'] == expected)
def testGetBefore(self):
xml_string = """
@@ -89,7 +89,7 @@ class TestSetStanzas(SlixTest):
"""
s = Set(ET.fromstring(xml_string))
expected = 'id'
self.failUnless(s['before'] == expected)
self.assertTrue(s['before'] == expected)
def testGetBeforeVal(self):
xml_string = """

View File

@@ -112,7 +112,7 @@ class TestHandlers(SlixTest):
# Check that the waiter is no longer registered
waiter_exists = self.xmpp.remove_handler('IqWait_test2')
self.failUnless(waiter_exists == False,
self.assertTrue(waiter_exists == False,
"Waiter handler was not removed.")
def testIqCallback(self):
@@ -145,7 +145,7 @@ class TestHandlers(SlixTest):
</iq>
""")
self.failUnless(events == ['foo'],
self.assertTrue(events == ['foo'],
"Iq callback was not executed: %s" % events)
def testMultipleHandlersForStanza(self):

View File

@@ -52,7 +52,7 @@ class TestStreamRoster(SlixTest):
pending_out=True,
groups=['Friends', 'Examples'])
self.failUnless(len(roster_updates) == 1,
self.assertTrue(len(roster_updates) == 1,
"Wrong number of roster_update events fired: %s (should be 1)" % len(roster_updates))
def testRosterSet(self):
@@ -89,7 +89,7 @@ class TestStreamRoster(SlixTest):
groups=['Friends', 'Examples'])
self.failUnless('roster_update' in events,
self.assertTrue('roster_update' in events,
"Roster updated event not triggered: %s" % events)
def testRosterPushRemove(self):
@@ -188,7 +188,7 @@ class TestStreamRoster(SlixTest):
</iq>
""")
self.failUnless(events == ['roster_callback'],
self.assertTrue(events == ['roster_callback'],
"Roster timeout event not triggered: %s." % events)
def testRosterUnicode(self):
@@ -209,7 +209,7 @@ class TestStreamRoster(SlixTest):
groups=['Unicode'])
jids = list(self.xmpp.client_roster.keys())
self.failUnless(jids == ['andré@foo'],
self.assertTrue(jids == ['andré@foo'],
"Too many roster entries found: %s" % jids)
self.recv("""
@@ -223,7 +223,7 @@ class TestStreamRoster(SlixTest):
expected = {'bar': {'status':'Testing',
'show':'away',
'priority':0}}
self.failUnless(result == expected,
self.assertTrue(result == expected,
"Unexpected roster values: %s" % result)
def testSendLastPresence(self):

View File

@@ -79,8 +79,7 @@ class TestInBandByteStreams(SlixTest):
self.assertEqual(events, {'ibb_stream_start', 'callback'})
@asyncio.coroutine
def testSendData(self):
async def testSendData(self):
"""Test sending data over an in-band bytestream."""
streams = []
@@ -117,7 +116,7 @@ class TestInBandByteStreams(SlixTest):
# Test sending data out
yield from stream.send("Testing")
await stream.send("Testing")
self.send("""
<iq type="set" id="2"

View File

@@ -626,7 +626,7 @@ class TestAdHocCommands(SlixTest):
</iq>
""")
self.failUnless(results == ['foo', 'bar', 'baz'],
self.assertTrue(results == ['foo', 'bar', 'baz'],
'Incomplete command workflow: %s' % results)
def testClientAPICancel(self):
@@ -689,7 +689,7 @@ class TestAdHocCommands(SlixTest):
</iq>
""")
self.failUnless(results == ['foo', 'bar'],
self.assertTrue(results == ['foo', 'bar'],
'Incomplete command workflow: %s' % results)
def testClientAPIError(self):
@@ -727,7 +727,7 @@ class TestAdHocCommands(SlixTest):
</iq>
""")
self.failUnless(results == ['foo'],
self.assertTrue(results == ['foo'],
'Incomplete command workflow: %s' % results)
def testClientAPIErrorStrippedResponse(self):
@@ -762,7 +762,7 @@ class TestAdHocCommands(SlixTest):
</iq>
""")
self.failUnless(results == ['foo'],
self.assertTrue(results == ['foo'],
'Incomplete command workflow: %s' % results)

View File

@@ -50,7 +50,7 @@ class TestStreamChatStates(SlixTest):
""")
expected = ['active', 'inactive', 'paused', 'composing', 'gone']
self.failUnless(results == expected,
self.assertTrue(results == expected,
"Chat state event not handled: %s" % results)

View File

@@ -35,7 +35,7 @@ class TestStreamDirectInvite(SlixTest):
</message>
""")
self.failUnless(events == [True],
self.assertTrue(events == [True],
"Event not raised: %s" % events)
def testSentDirectInvite(self):

View File

@@ -456,7 +456,7 @@ class TestStreamSensorData(SlixTest):
</iq>
""")
self.failUnless(results == ["rejected"],
self.assertTrue(results == ["rejected"],
"Rejected callback was not properly executed")
def testRequestAcceptedAPI(self):
@@ -493,7 +493,7 @@ class TestStreamSensorData(SlixTest):
</iq>
""")
self.failUnless(results == ["accepted"],
self.assertTrue(results == ["accepted"],
"Accepted callback was not properly executed")
def testRequestFieldsAPI(self):
@@ -561,19 +561,19 @@ class TestStreamSensorData(SlixTest):
</message>
""")
self.failUnlessEqual(results, ["accepted","fields","done"])
self.assertEqual(results, ["accepted","fields","done"])
# self.assertIn("nodeId", callback_data);
self.assertTrue("nodeId" in callback_data)
self.failUnlessEqual(callback_data["nodeId"], "Device33")
self.assertEqual(callback_data["nodeId"], "Device33")
# self.assertIn("timestamp", callback_data);
self.assertTrue("timestamp" in callback_data)
self.failUnlessEqual(callback_data["timestamp"], "2000-01-01T00:01:02")
self.assertEqual(callback_data["timestamp"], "2000-01-01T00:01:02")
#self.assertIn("field_Voltage", callback_data);
self.assertTrue("field_Voltage" in callback_data)
self.failUnlessEqual(callback_data["field_Voltage"], {"name": "Voltage", "value": "230.4", "typename": "numeric", "unit": "V", "flags": {"invoiced": "true"}})
self.assertEqual(callback_data["field_Voltage"], {"name": "Voltage", "value": "230.4", "typename": "numeric", "unit": "V", "flags": {"invoiced": "true"}})
#self.assertIn("field_TestBool", callback_data);
self.assertTrue("field_TestBool" in callback_data)
self.failUnlessEqual(callback_data["field_TestBool"], {"name": "TestBool", "value": "true", "typename": "boolean" })
self.assertEqual(callback_data["field_TestBool"], {"name": "TestBool", "value": "true", "typename": "boolean" })
def testServiceDiscoveryClient(self):
self.stream_start(mode='client',
@@ -675,16 +675,16 @@ class TestStreamSensorData(SlixTest):
</message>
""")
self.failUnlessEqual(results, ["accepted","failure"]);
self.assertEqual(results, ["accepted","failure"]);
# self.assertIn("nodeId", callback_data);
self.assertTrue("nodeId" in callback_data)
self.failUnlessEqual(callback_data["nodeId"], "Device33")
self.assertEqual(callback_data["nodeId"], "Device33")
# self.assertIn("timestamp", callback_data);
self.assertTrue("timestamp" in callback_data)
self.failUnlessEqual(callback_data["timestamp"], "2013-03-07T17:13:30")
self.assertEqual(callback_data["timestamp"], "2013-03-07T17:13:30")
# self.assertIn("error_msg", callback_data);
self.assertTrue("error_msg" in callback_data)
self.failUnlessEqual(callback_data["error_msg"], "Timeout.")
self.assertEqual(callback_data["error_msg"], "Timeout.")
def testDelayedRequest(self):
self.stream_start(mode='component',
@@ -1071,19 +1071,19 @@ class TestStreamSensorData(SlixTest):
</message>
""")
self.failUnlessEqual(results, ["queued","started","fields","done"]);
self.assertEqual(results, ["queued","started","fields","done"]);
# self.assertIn("nodeId", callback_data);
self.assertTrue("nodeId" in callback_data)
self.failUnlessEqual(callback_data["nodeId"], "Device33")
self.assertEqual(callback_data["nodeId"], "Device33")
# self.assertIn("timestamp", callback_data);
self.assertTrue("timestamp" in callback_data)
self.failUnlessEqual(callback_data["timestamp"], "2000-01-01T00:01:02")
self.assertEqual(callback_data["timestamp"], "2000-01-01T00:01:02")
# self.assertIn("field_Voltage", callback_data);
self.assertTrue("field_Voltage" in callback_data)
self.failUnlessEqual(callback_data["field_Voltage"], {"name": "Voltage", "value": "230.4", "typename": "numeric", "unit": "V", "flags": {"invoiced": "true"}})
self.assertEqual(callback_data["field_Voltage"], {"name": "Voltage", "value": "230.4", "typename": "numeric", "unit": "V", "flags": {"invoiced": "true"}})
# self.assertIn("field_TestBool", callback_data);
self.assertTrue("field_TestBool" in callback_data)
self.failUnlessEqual(callback_data["field_TestBool"], {"name": "TestBool", "value": "true", "typename": "boolean" })
self.assertEqual(callback_data["field_TestBool"], {"name": "TestBool", "value": "true", "typename": "boolean" })
def testRequestFieldsCancelAPI(self):
@@ -1139,7 +1139,7 @@ class TestStreamSensorData(SlixTest):
</iq>
""")
self.failUnlessEqual(results, ["accepted","cancelled"])
self.assertEqual(results, ["accepted","cancelled"])
def testDelayedRequestCancel(self):
self.stream_start(mode='component',

View File

@@ -25,7 +25,7 @@ class TestToString(SlixTest):
else:
xml=original
result = tostring(xml, **kwargs)
self.failUnless(result == expected, "%s: %s" % (message, result))
self.assertTrue(result == expected, "%s: %s" % (message, result))
def testXMLEscape(self):
"""Test escaping XML special characters."""
@@ -34,7 +34,7 @@ class TestToString(SlixTest):
desired = """&lt;foo bar=&quot;baz&quot;&gt;&apos;Hi"""
desired += """ &amp; welcome!&apos;&lt;/foo&gt;"""
self.failUnless(escaped == desired,
self.assertTrue(escaped == desired,
"XML escaping did not work: %s." % escaped)
def testEmptyElement(self):
@@ -99,7 +99,7 @@ class TestToString(SlixTest):
msg['body'] = utf8_message.decode('utf-8')
expected = '<message><body>\xe0\xb2\xa0_\xe0\xb2\xa0</body></message>'
result = msg.__str__()
self.failUnless(result == expected,
self.assertTrue(result == expected,
"Stanza Unicode handling is incorrect: %s" % result)
def testXMLLang(self):
@@ -112,7 +112,7 @@ class TestToString(SlixTest):
expected = '<message xml:lang="no" />'
result = msg.__str__()
self.failUnless(expected == result,
self.assertTrue(expected == result,
"Serialization with xml:lang failed: %s" % result)