summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/pull_request_template.md13
-rw-r--r--.travis.yml6
-rw-r--r--INSTALL2
-rw-r--r--README.rst2
-rw-r--r--docs/_static/haiku.css21
-rw-r--r--docs/_templates/layout.html1
-rw-r--r--docs/api/xmlstream/tostring.rst2
-rw-r--r--docs/differences.rst5
-rw-r--r--docs/index.rst2
-rwxr-xr-xexamples/adhoc_provider.py4
-rwxr-xr-xexamples/adhoc_user.py6
-rwxr-xr-xexamples/admin_commands.py4
-rwxr-xr-xexamples/disco_browser.py2
-rwxr-xr-xexamples/download_avatars.py2
-rwxr-xr-xexamples/echo_client.py4
-rwxr-xr-xexamples/gtalk_custom_domain.py4
-rwxr-xr-xexamples/markup.py4
-rwxr-xr-xexamples/migrate_roster.py2
-rwxr-xr-xexamples/muc.py4
-rwxr-xr-xexamples/ping.py4
-rwxr-xr-xexamples/proxy_echo_client.py4
-rwxr-xr-xexamples/pubsub_client.py2
-rwxr-xr-xexamples/pubsub_events.py4
-rwxr-xr-xexamples/register_account.py4
-rwxr-xr-xexamples/roster_browser.py8
-rwxr-xr-xexamples/send_client.py6
-rwxr-xr-xexamples/set_avatar.py4
-rwxr-xr-xexamples/thirdparty_auth.py4
-rwxr-xr-xexamples/user_location.py4
-rwxr-xr-xexamples/user_tune.py4
-rwxr-xr-xsetup.py3
-rw-r--r--slixmpp/plugins/xep_0009/stanza/RPC.py2
-rw-r--r--slixmpp/plugins/xep_0045.py50
-rw-r--r--slixmpp/plugins/xep_0196/stanza.py3
-rw-r--r--slixmpp/plugins/xep_0198/stream_management.py46
-rw-r--r--slixmpp/plugins/xep_0323/stanza/sensordata.py2
-rw-r--r--slixmpp/plugins/xep_0363/http_upload.py8
-rw-r--r--slixmpp/thirdparty/mini_dateutil.py2
-rw-r--r--slixmpp/version.py4
-rw-r--r--slixmpp/xmlstream/stanzabase.py15
-rw-r--r--slixmpp/xmlstream/xmlstream.py47
41 files changed, 180 insertions, 140 deletions
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 00000000..50ea54cb
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,13 @@
+################ Please use Gitlab instead of Github ###################################
+
+Hello, thank you for contributing to slixmpp!
+
+You’re about to open a pull request on github. However this github repository is not the official place for contributions on slixmpp.
+
+Please open your merge request on https://lab.louiz.org/poezio/slixmpp/
+
+You should be able to log in there with your github credentials, clone the slixmpp repository in your namespace, push your existing pull request into a new branch, and then open a merge request with one click, within 3 minutes.
+
+This will help us review your contribution, avoid spreading things everywhere and it will even run the tests automatically with your changes.
+
+Thank you.
diff --git a/.travis.yml b/.travis.yml
index f503be34..be8e089c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,9 +1,7 @@
language: python
python:
- - "3.4"
- - "3.5"
- - "3.6"
- - "3.7-dev"
+ - "3.7"
+ - "3.8-dev"
install:
- "pip install ."
script: testall.py
diff --git a/INSTALL b/INSTALL
index 9b14b232..146031f4 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1,5 +1,5 @@
Pre-requisites:
-- Python 3.5+
+- Python 3.7+
- Cython 0.22 and libidn, optionally (making JID faster by compiling the stringprep module)
- GnuPG, for testing
diff --git a/README.rst b/README.rst
index d27f0698..6a25248d 100644
--- a/README.rst
+++ b/README.rst
@@ -1,7 +1,7 @@
Slixmpp
#########
-Slixmpp is an MIT licensed XMPP library for Python 3.5+. It is a fork of
+Slixmpp is an MIT licensed XMPP library for Python 3.7+. It is a fork of
SleekXMPP.
Slixmpp's goals is to only rewrite the core of the library (the low level
diff --git a/docs/_static/haiku.css b/docs/_static/haiku.css
index 3d8ee6a7..a76f55de 100644
--- a/docs/_static/haiku.css
+++ b/docs/_static/haiku.css
@@ -408,24 +408,3 @@ div.viewcode-block:target {
margin: -1px -12px;
padding: 0 12px;
}
-
-#from_andyet {
- -webkit-box-shadow: #CCC 0px 0px 3px;
- background: rgba(255, 255, 255, 1);
- bottom: 0px;
- right: 17px;
- padding: 3px 10px;
- position: fixed;
-}
-
-#from_andyet h2 {
- background-image: url("images/from_&yet.png");
- background-repeat: no-repeat;
- height: 29px;
- line-height: 0;
- text-indent: -9999em;
- width: 79px;
- margin-top: 0;
- margin: 0px;
- padding: 0px;
-}
diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html
index de6f7244..0a97cb70 100644
--- a/docs/_templates/layout.html
+++ b/docs/_templates/layout.html
@@ -65,6 +65,5 @@
<div class="bottomnav">
{{ nav() }}
</div>
- <a id="from_andyet" href="http://andyet.net"><h2>From &amp;yet</h2></a>
{% endblock %}
diff --git a/docs/api/xmlstream/tostring.rst b/docs/api/xmlstream/tostring.rst
index 68abbdb6..107e97b0 100644
--- a/docs/api/xmlstream/tostring.rst
+++ b/docs/api/xmlstream/tostring.rst
@@ -13,7 +13,7 @@ hides namespaces when able and does not introduce excessive namespace
prefixes::
>>> from slixmpp.xmlstream.tostring import tostring
- >>> from xml.etree import cElementTree as ET
+ >>> from xml.etree import ElementTree as ET
>>> xml = ET.fromstring('<foo xmlns="bar"><baz /></foo>')
>>> ET.tostring(xml)
'<ns0:foo xmlns:ns0="bar"><ns0:baz /></foo>'
diff --git a/docs/differences.rst b/docs/differences.rst
index 8a86427b..7c781571 100644
--- a/docs/differences.rst
+++ b/docs/differences.rst
@@ -3,8 +3,9 @@
Differences from SleekXMPP
==========================
-**Python 3.5+ only**
- slixmpp will only work on python 3.5 and above.
+**Python 3.7+ only**
+ slixmpp will work on python 3.7 and above. It may work with previous
+ versions but we provide no guarantees.
**Stanza copies**
The same stanza object is given through all the handlers; a handler that
diff --git a/docs/index.rst b/docs/index.rst
index 2f556d83..a18c77a7 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -21,7 +21,7 @@ Slixmpp
which goal is to use asyncio instead of threads to handle networking. See
:ref:`differences`.
-Slixmpp is an :ref:`MIT licensed <license>` XMPP library for Python 3.5+,
+Slixmpp is an :ref:`MIT licensed <license>` XMPP library for Python 3.7+,
Slixmpp's design goals and philosphy are:
diff --git a/examples/adhoc_provider.py b/examples/adhoc_provider.py
index 2bab2f46..d2c3afd6 100755
--- a/examples/adhoc_provider.py
+++ b/examples/adhoc_provider.py
@@ -33,7 +33,7 @@ class CommandBot(slixmpp.ClientXMPP):
# our roster.
self.add_event_handler("session_start", self.start)
- def start(self, event):
+ async def start(self, event):
"""
Process the session_start event.
@@ -47,7 +47,7 @@ class CommandBot(slixmpp.ClientXMPP):
data.
"""
self.send_presence()
- self.get_roster()
+ await self.get_roster()
# We add the command after session_start has fired
# to ensure that the correct full JID is used.
diff --git a/examples/adhoc_user.py b/examples/adhoc_user.py
index 8bdb675b..931ef71c 100755
--- a/examples/adhoc_user.py
+++ b/examples/adhoc_user.py
@@ -37,7 +37,7 @@ class CommandUserBot(slixmpp.ClientXMPP):
self.add_event_handler("session_start", self.start)
self.add_event_handler("message", self.message)
- def start(self, event):
+ async def start(self, event):
"""
Process the session_start event.
@@ -51,7 +51,7 @@ class CommandUserBot(slixmpp.ClientXMPP):
data.
"""
self.send_presence()
- self.get_roster()
+ await self.get_roster()
# We first create a session dictionary containing:
# 'next' -- the handler to execute on a successful response
@@ -176,4 +176,4 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
- xmpp.process()
+ xmpp.process(forever=False)
diff --git a/examples/admin_commands.py b/examples/admin_commands.py
index 72577f87..64c48913 100755
--- a/examples/admin_commands.py
+++ b/examples/admin_commands.py
@@ -30,7 +30,7 @@ class AdminCommands(slixmpp.ClientXMPP):
self.add_event_handler("session_start", self.start)
- def start(self, event):
+ async def start(self, event):
"""
Process the session_start event.
@@ -44,7 +44,7 @@ class AdminCommands(slixmpp.ClientXMPP):
data.
"""
self.send_presence()
- self.get_roster()
+ await self.get_roster()
def command_success(iq, session):
print('Command completed')
diff --git a/examples/disco_browser.py b/examples/disco_browser.py
index f0ece32d..02d51259 100755
--- a/examples/disco_browser.py
+++ b/examples/disco_browser.py
@@ -69,7 +69,7 @@ class Disco(slixmpp.ClientXMPP):
event does not provide any additional
data.
"""
- self.get_roster()
+ await self.get_roster()
self.send_presence()
try:
diff --git a/examples/download_avatars.py b/examples/download_avatars.py
index 02591e3e..37733b01 100755
--- a/examples/download_avatars.py
+++ b/examples/download_avatars.py
@@ -159,4 +159,4 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
- xmpp.process()
+ xmpp.process(forever=False)
diff --git a/examples/echo_client.py b/examples/echo_client.py
index 820ca014..2a3337a5 100755
--- a/examples/echo_client.py
+++ b/examples/echo_client.py
@@ -38,7 +38,7 @@ class EchoBot(slixmpp.ClientXMPP):
# MUC messages and error messages.
self.add_event_handler("message", self.message)
- def start(self, event):
+ async def start(self, event):
"""
Process the session_start event.
@@ -52,7 +52,7 @@ class EchoBot(slixmpp.ClientXMPP):
data.
"""
self.send_presence()
- self.get_roster()
+ await self.get_roster()
def message(self, msg):
"""
diff --git a/examples/gtalk_custom_domain.py b/examples/gtalk_custom_domain.py
index f055159b..c8d80e96 100755
--- a/examples/gtalk_custom_domain.py
+++ b/examples/gtalk_custom_domain.py
@@ -58,7 +58,7 @@ class GTalkBot(slixmpp.ClientXMPP):
logging.error(err.message)
self.disconnect()
- def start(self, event):
+ async def start(self, event):
"""
Process the session_start event.
@@ -72,7 +72,7 @@ class GTalkBot(slixmpp.ClientXMPP):
data.
"""
self.send_presence()
- self.get_roster()
+ await self.get_roster()
def message(self, msg):
"""
diff --git a/examples/markup.py b/examples/markup.py
index a72cbdb8..4c850ec8 100755
--- a/examples/markup.py
+++ b/examples/markup.py
@@ -39,7 +39,7 @@ class EchoBot(slixmpp.ClientXMPP):
# MUC messages and error messages.
self.add_event_handler("message", self.message)
- def start(self, event):
+ async def start(self, event):
"""
Process the session_start event.
@@ -53,7 +53,7 @@ class EchoBot(slixmpp.ClientXMPP):
data.
"""
self.send_presence()
- self.get_roster()
+ await self.get_roster()
def message(self, msg):
"""
diff --git a/examples/migrate_roster.py b/examples/migrate_roster.py
index d599b10c..be457fb3 100755
--- a/examples/migrate_roster.py
+++ b/examples/migrate_roster.py
@@ -104,4 +104,4 @@ def on_session2(event):
new_xmpp.add_event_handler('session_start', on_session2)
new_xmpp.connect()
-new_xmpp.process()
+new_xmpp.process(forever=False)
diff --git a/examples/muc.py b/examples/muc.py
index 469e8f49..e3433b8f 100755
--- a/examples/muc.py
+++ b/examples/muc.py
@@ -52,7 +52,7 @@ class MUCBot(slixmpp.ClientXMPP):
self.muc_online)
- def start(self, event):
+ async def start(self, event):
"""
Process the session_start event.
@@ -65,7 +65,7 @@ class MUCBot(slixmpp.ClientXMPP):
event does not provide any additional
data.
"""
- self.get_roster()
+ await self.get_roster()
self.send_presence()
self.plugin['xep_0045'].join_muc(self.room,
self.nick,
diff --git a/examples/ping.py b/examples/ping.py
index cb1bb968..7870715c 100755
--- a/examples/ping.py
+++ b/examples/ping.py
@@ -51,7 +51,7 @@ class PingTest(slixmpp.ClientXMPP):
data.
"""
self.send_presence()
- self.get_roster()
+ await self.get_roster()
try:
rtt = await self['xep_0199'].ping(self.pingjid,
@@ -109,4 +109,4 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
- xmpp.process()
+ xmpp.process(forever=False)
diff --git a/examples/proxy_echo_client.py b/examples/proxy_echo_client.py
index b149de31..566f5957 100755
--- a/examples/proxy_echo_client.py
+++ b/examples/proxy_echo_client.py
@@ -38,7 +38,7 @@ class EchoBot(slixmpp.ClientXMPP):
# MUC messages and error messages.
self.add_event_handler("message", self.message)
- def start(self, event):
+ async def start(self, event):
"""
Process the session_start event.
@@ -52,7 +52,7 @@ class EchoBot(slixmpp.ClientXMPP):
data.
"""
self.send_presence()
- self.get_roster()
+ await self.get_roster()
def message(self, msg):
"""
diff --git a/examples/pubsub_client.py b/examples/pubsub_client.py
index 480c7d89..e285dfc8 100755
--- a/examples/pubsub_client.py
+++ b/examples/pubsub_client.py
@@ -32,7 +32,7 @@ class PubsubClient(slixmpp.ClientXMPP):
self.add_event_handler('session_start', self.start)
async def start(self, event):
- self.get_roster()
+ await self.get_roster()
self.send_presence()
try:
diff --git a/examples/pubsub_events.py b/examples/pubsub_events.py
index e2fdc9cf..2b3a1e65 100755
--- a/examples/pubsub_events.py
+++ b/examples/pubsub_events.py
@@ -38,8 +38,8 @@ class PubsubEvents(slixmpp.ClientXMPP):
# self.add_event_handler('event_prefix_purge', handler)
# self.add_event_handler('event_prefix_delete', handler)
- def start(self, event):
- self.get_roster()
+ async def start(self, event):
+ await self.get_roster()
self.send_presence()
def _publish(self, msg):
diff --git a/examples/register_account.py b/examples/register_account.py
index 76b5fcfc..9ab0c664 100755
--- a/examples/register_account.py
+++ b/examples/register_account.py
@@ -47,7 +47,7 @@ class RegisterBot(slixmpp.ClientXMPP):
# for data forms and OOB links that will make that easier.
self.add_event_handler("register", self.register)
- def start(self, event):
+ async def start(self, event):
"""
Process the session_start event.
@@ -61,7 +61,7 @@ class RegisterBot(slixmpp.ClientXMPP):
data.
"""
self.send_presence()
- self.get_roster()
+ await self.get_roster()
# We're only concerned about registering, so nothing more to do here.
self.disconnect()
diff --git a/examples/roster_browser.py b/examples/roster_browser.py
index 6ad8b2a4..e9365d09 100755
--- a/examples/roster_browser.py
+++ b/examples/roster_browser.py
@@ -51,12 +51,8 @@ class RosterBrowser(slixmpp.ClientXMPP):
event does not provide any additional
data.
"""
- future = asyncio.Future()
- def callback(result):
- future.set_result(None)
try:
- self.get_roster(callback=callback)
- await future
+ await self.get_roster()
except IqError as err:
print('Error: %s' % err.iq['error']['condition'])
except IqTimeout:
@@ -138,4 +134,4 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
- xmpp.process()
+ xmpp.process(forever=False)
diff --git a/examples/send_client.py b/examples/send_client.py
index 6e3e5865..5d5fb810 100755
--- a/examples/send_client.py
+++ b/examples/send_client.py
@@ -38,7 +38,7 @@ class SendMsgBot(slixmpp.ClientXMPP):
# our roster.
self.add_event_handler("session_start", self.start)
- def start(self, event):
+ async def start(self, event):
"""
Process the session_start event.
@@ -52,7 +52,7 @@ class SendMsgBot(slixmpp.ClientXMPP):
data.
"""
self.send_presence()
- self.get_roster()
+ await self.get_roster()
self.send_message(mto=self.recipient,
mbody=self.msg,
@@ -107,4 +107,4 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
- xmpp.process()
+ xmpp.process(forever=False)
diff --git a/examples/set_avatar.py b/examples/set_avatar.py
index 6579b2e7..3188e9d8 100755
--- a/examples/set_avatar.py
+++ b/examples/set_avatar.py
@@ -46,7 +46,7 @@ class AvatarSetter(slixmpp.ClientXMPP):
data.
"""
self.send_presence()
- self.get_roster()
+ await self.get_roster()
avatar_file = None
try:
@@ -137,4 +137,4 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
- xmpp.process()
+ xmpp.process(forever=False)
diff --git a/examples/thirdparty_auth.py b/examples/thirdparty_auth.py
index 4129fa91..b2623972 100755
--- a/examples/thirdparty_auth.py
+++ b/examples/thirdparty_auth.py
@@ -60,7 +60,7 @@ class ThirdPartyAuthBot(slixmpp.ClientXMPP):
# MUC messages and error messages.
self.add_event_handler("message", self.message)
- def start(self, event):
+ async def start(self, event):
"""
Process the session_start event.
@@ -74,7 +74,7 @@ class ThirdPartyAuthBot(slixmpp.ClientXMPP):
data.
"""
self.send_presence()
- self.get_roster()
+ await self.get_roster()
def message(self, msg):
"""
diff --git a/examples/user_location.py b/examples/user_location.py
index 7edbeabb..2d473678 100755
--- a/examples/user_location.py
+++ b/examples/user_location.py
@@ -38,9 +38,9 @@ class LocationBot(ClientXMPP):
self.current_tune = None
- def start(self, event):
+ async def start(self, event):
self.send_presence()
- self.get_roster()
+ await self.get_roster()
self['xep_0115'].update_caps()
print("Using freegeoip.net to get geolocation.")
diff --git a/examples/user_tune.py b/examples/user_tune.py
index 6aa0da8e..2ce29c1d 100755
--- a/examples/user_tune.py
+++ b/examples/user_tune.py
@@ -35,9 +35,9 @@ class TuneBot(ClientXMPP):
self.current_tune = None
- def start(self, event):
+ async def start(self, event):
self.send_presence()
- self.get_roster()
+ await self.get_roster()
self['xep_0115'].update_caps()
def _update_tune(self):
diff --git a/setup.py b/setup.py
index 8cab570e..5b122388 100755
--- a/setup.py
+++ b/setup.py
@@ -28,9 +28,8 @@ CLASSIFIERS = [
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python',
- 'Programming Language :: Python :: 3.5',
- 'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
+ 'Programming Language :: Python :: 3.8',
'Topic :: Internet :: XMPP',
'Topic :: Software Development :: Libraries :: Python Modules',
]
diff --git a/slixmpp/plugins/xep_0009/stanza/RPC.py b/slixmpp/plugins/xep_0009/stanza/RPC.py
index f8cec481..542c839c 100644
--- a/slixmpp/plugins/xep_0009/stanza/RPC.py
+++ b/slixmpp/plugins/xep_0009/stanza/RPC.py
@@ -7,7 +7,7 @@
"""
from slixmpp.xmlstream.stanzabase import ElementBase
-from xml.etree import cElementTree as ET
+from xml.etree import ElementTree as ET
class RPCQuery(ElementBase):
diff --git a/slixmpp/plugins/xep_0045.py b/slixmpp/plugins/xep_0045.py
index 30769b5c..dfbb3b58 100644
--- a/slixmpp/plugins/xep_0045.py
+++ b/slixmpp/plugins/xep_0045.py
@@ -29,61 +29,61 @@ class MUCPresence(ElementBase):
affiliations = {'', }
roles = {'', }
- def get_xml_item(self):
+ def get_item_attr(self, attr, default):
+ item = self.xml.find('{http://jabber.org/protocol/muc#user}item')
+ if item is None:
+ return default
+ return item.get(attr)
+
+ def set_item_attr(self, attr, value):
item = self.xml.find('{http://jabber.org/protocol/muc#user}item')
if item is None:
item = ET.Element('{http://jabber.org/protocol/muc#user}item')
self.xml.append(item)
+ item.attrib[attr] = value
return item
+ def del_item_attr(self, attr):
+ item = self.xml.find('{http://jabber.org/protocol/muc#user}item')
+ if item is not None and attr in item.attrib:
+ del item.attrib[attr]
+
def get_affiliation(self):
- #TODO if no affilation, set it to the default and return default
- item = self.get_xml_item()
- return item.get('affiliation', '')
+ return self.get_item_attr('affiliation', '')
def set_affiliation(self, value):
- item = self.get_xml_item()
- #TODO check for valid affiliation
- item.attrib['affiliation'] = value
+ self.set_item_attr('affiliation', value)
return self
def del_affiliation(self):
- item = self.get_xml_item()
- #TODO set default affiliation
- if 'affiliation' in item.attrib: del item.attrib['affiliation']
+ # TODO: set default affiliation
+ self.del_item_attr('affiliation')
return self
def get_jid(self):
- item = self.get_xml_item()
- return JID(item.get('jid', ''))
+ return JID(self.get_item_attr('jid', ''))
def set_jid(self, value):
- item = self.get_xml_item()
if not isinstance(value, str):
value = str(value)
- item.attrib['jid'] = value
+ self.set_item_attr('jid', value)
return self
def del_jid(self):
- item = self.get_xml_item()
- if 'jid' in item.attrib: del item.attrib['jid']
+ self.del_item_attr('jid')
return self
def get_role(self):
- item = self.get_xml_item()
- #TODO get default role, set default role if none
- return item.get('role', '')
+ return self.get_item_attr('role', '')
def set_role(self, value):
- item = self.get_xml_item()
- #TODO check for valid role
- item.attrib['role'] = value
+ # TODO: check for valid role
+ self.set_item_attr('role', value)
return self
def del_role(self):
- item = self.get_xml_item()
- #TODO set default role
- if 'role' in item.attrib: del item.attrib['role']
+ # TODO: set default role
+ self.del_item_attr('role')
return self
def get_nick(self):
diff --git a/slixmpp/plugins/xep_0196/stanza.py b/slixmpp/plugins/xep_0196/stanza.py
index 79f5621e..756208b2 100644
--- a/slixmpp/plugins/xep_0196/stanza.py
+++ b/slixmpp/plugins/xep_0196/stanza.py
@@ -11,10 +11,9 @@ from slixmpp.xmlstream import ElementBase, ET
class UserGaming(ElementBase):
- name = 'gaming'
+ name = 'game'
namespace = 'urn:xmpp:gaming:0'
plugin_attrib = 'gaming'
interfaces = {'character_name', 'character_profile', 'name',
'level', 'server_address', 'server_name', 'uri'}
sub_interfaces = interfaces
-
diff --git a/slixmpp/plugins/xep_0198/stream_management.py b/slixmpp/plugins/xep_0198/stream_management.py
index 759e82e1..0200646a 100644
--- a/slixmpp/plugins/xep_0198/stream_management.py
+++ b/slixmpp/plugins/xep_0198/stream_management.py
@@ -71,7 +71,8 @@ class XEP_0198(BasePlugin):
self.window_counter = self.window
- self.enabled = False
+ self.enabled_in = False
+ self.enabled_out = False
self.unacked_queue = collections.deque()
register_stanza_plugin(StreamFeatures, stanza.StreamManagement)
@@ -82,10 +83,6 @@ class XEP_0198(BasePlugin):
self.xmpp.register_stanza(stanza.Ack)
self.xmpp.register_stanza(stanza.RequestAck)
- # Only end the session when a </stream> element is sent,
- # not just because the connection has died.
- self.xmpp.end_session_on_disconnect = False
-
# Register the feature twice because it may be ordered two
# different ways: enabling after binding and resumption
# before binding.
@@ -131,6 +128,7 @@ class XEP_0198(BasePlugin):
self.xmpp.add_filter('in', self._handle_incoming)
self.xmpp.add_filter('out_sync', self._handle_outgoing)
+ self.xmpp.add_event_handler('disconnected', self.disconnected)
self.xmpp.add_event_handler('session_end', self.session_end)
def plugin_end(self):
@@ -139,6 +137,7 @@ class XEP_0198(BasePlugin):
self.xmpp.unregister_feature('sm', self.order)
self.xmpp.unregister_feature('sm', self.resume_order)
+ self.xmpp.del_event_handler('disconnected', self.disconnected)
self.xmpp.del_event_handler('session_end', self.session_end)
self.xmpp.del_filter('in', self._handle_incoming)
self.xmpp.del_filter('out_sync', self._handle_outgoing)
@@ -154,9 +153,19 @@ class XEP_0198(BasePlugin):
self.xmpp.remove_stanza(stanza.Ack)
self.xmpp.remove_stanza(stanza.RequestAck)
+ def disconnected(self, event):
+ """Reset enabled state until we can resume/reenable."""
+ log.debug("disconnected, disabling SM")
+ self.xmpp.event('sm_disabled', event)
+ self.enabled_in = False
+ self.enabled_out = False
+
def session_end(self, event):
"""Reset stream management state."""
- self.enabled = False
+ log.debug("session_end, disabling SM")
+ self.xmpp.event('sm_disabled', event)
+ self.enabled_in = False
+ self.enabled_out = False
self.unacked_queue.clear()
self.sm_id = None
self.handled = 0
@@ -171,6 +180,7 @@ class XEP_0198(BasePlugin):
def request_ack(self, e=None):
"""Request an ack from the server."""
+ log.debug("requesting ack")
req = stanza.RequestAck(self.xmpp)
self.xmpp.send_raw(str(req))
@@ -193,9 +203,7 @@ class XEP_0198(BasePlugin):
enable = stanza.Enable(self.xmpp)
enable['resume'] = self.allow_resume
enable.send()
- self.enabled = True
- self.handled = 0
- self.unacked_queue.clear()
+ log.debug("enabling SM")
waiter = Waiter('enabled_or_failed',
MatchMany([
@@ -204,11 +212,11 @@ class XEP_0198(BasePlugin):
self.xmpp.register_handler(waiter)
result = await waiter.wait()
elif self.sm_id and self.allow_resume and 'bind' not in self.xmpp.features:
- self.enabled = True
resume = stanza.Resume(self.xmpp)
resume['h'] = self.handled
resume['previd'] = self.sm_id
resume.send()
+ log.debug("resuming SM")
# Wait for a response before allowing stream feature processing
# to continue. The actual result processing will be done in the
@@ -231,7 +239,10 @@ class XEP_0198(BasePlugin):
self.xmpp.features.add('stream_management')
if stanza['id']:
self.sm_id = stanza['id']
+ self.enabled_in = True
+ self.handled = 0
self.xmpp.event('sm_enabled', stanza)
+ self.xmpp.end_session_on_disconnect = False
def _handle_resumed(self, stanza):
"""Finish resuming a stream by resending unacked stanzas.
@@ -239,10 +250,12 @@ class XEP_0198(BasePlugin):
Raises a :term:`session_resumed` event.
"""
self.xmpp.features.add('stream_management')
+ self.enabled_in = True
self._handle_ack(stanza)
for id, stanza in self.unacked_queue:
self.xmpp.send(stanza, use_filters=False)
self.xmpp.event('session_resumed', stanza)
+ self.xmpp.end_session_on_disconnect = False
def _handle_failed(self, stanza):
"""
@@ -252,7 +265,8 @@ class XEP_0198(BasePlugin):
Raises an :term:`sm_failed` event.
"""
- self.enabled = False
+ self.enabled_in = False
+ self.enabled_out = False
self.unacked_queue.clear()
self.xmpp.event('sm_failed', stanza)
@@ -289,7 +303,7 @@ class XEP_0198(BasePlugin):
def _handle_incoming(self, stanza):
"""Increment the handled counter for each inbound stanza."""
- if not self.enabled:
+ if not self.enabled_in:
return stanza
if isinstance(stanza, (Message, Presence, Iq)):
@@ -299,7 +313,13 @@ class XEP_0198(BasePlugin):
def _handle_outgoing(self, stanza):
"""Store outgoing stanzas in a queue to be acked."""
- if not self.enabled:
+ from slixmpp.plugins.xep_0198 import stanza as st
+ if isinstance(stanza, (st.Enable, st.Resume)):
+ self.enabled_out = True
+ self.unacked_queue.clear()
+ log.debug("enabling outgoing SM: %s" % stanza)
+
+ if not self.enabled_out:
return stanza
if isinstance(stanza, (Message, Presence, Iq)):
diff --git a/slixmpp/plugins/xep_0323/stanza/sensordata.py b/slixmpp/plugins/xep_0323/stanza/sensordata.py
index c0906cac..7ab1e3ba 100644
--- a/slixmpp/plugins/xep_0323/stanza/sensordata.py
+++ b/slixmpp/plugins/xep_0323/stanza/sensordata.py
@@ -516,7 +516,7 @@ class Field(ElementBase):
:param value: string
"""
- pattern = re.compile("^\d+([|]\w+([.]\w+)*([|][^,]*)?)?(,\d+([|]\w+([.]\w+)*([|][^,]*)?)?)*$")
+ pattern = re.compile(r"^\d+([|]\w+([.]\w+)*([|][^,]*)?)?(,\d+([|]\w+([.]\w+)*([|][^,]*)?)?)*$")
if pattern.match(value) is not None:
self.xml.stringIds = value
else:
diff --git a/slixmpp/plugins/xep_0363/http_upload.py b/slixmpp/plugins/xep_0363/http_upload.py
index 266fc656..a833a9c9 100644
--- a/slixmpp/plugins/xep_0363/http_upload.py
+++ b/slixmpp/plugins/xep_0363/http_upload.py
@@ -28,7 +28,9 @@ class UploadServiceNotFound(FileUploadError):
pass
class FileTooBig(FileUploadError):
- pass
+ def __str__(self):
+ return 'File size too large: {} (max: {} bytes)' \
+ .format(self.args[0], self.args[1])
class HTTPError(FileUploadError):
def __str__(self):
@@ -116,7 +118,7 @@ class XEP_0363(BasePlugin):
except (TypeError, ValueError):
log.error('Invalid max size received from HTTP File Upload service')
self.max_file_size = float('+inf')
- break
+ break
if input_file is None:
input_file = open(filename, 'rb')
@@ -126,7 +128,7 @@ class XEP_0363(BasePlugin):
input_file.seek(0)
if size > self.max_file_size:
- raise FileTooBig()
+ raise FileTooBig(size, self.max_file_size)
if content_type is None:
content_type = guess_type(filename)[0]
diff --git a/slixmpp/thirdparty/mini_dateutil.py b/slixmpp/thirdparty/mini_dateutil.py
index e751a448..882a531f 100644
--- a/slixmpp/thirdparty/mini_dateutil.py
+++ b/slixmpp/thirdparty/mini_dateutil.py
@@ -160,7 +160,7 @@ except:
return _fixed_offset_tzs[offsetmins]
- _iso8601_parser = re.compile("""
+ _iso8601_parser = re.compile(r"""
^
(?P<year> [0-9]{4})?(?P<ymdsep>-?)?
(?P<month>[0-9]{2})?(?P=ymdsep)?
diff --git a/slixmpp/version.py b/slixmpp/version.py
index feb173e2..757b5473 100644
--- a/slixmpp/version.py
+++ b/slixmpp/version.py
@@ -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.2'
-__version_info__ = (1, 4, 2)
+__version__ = '1.5.2'
+__version_info__ = (1, 5, 2)
diff --git a/slixmpp/xmlstream/stanzabase.py b/slixmpp/xmlstream/stanzabase.py
index 3e45f613..f45e4b96 100644
--- a/slixmpp/xmlstream/stanzabase.py
+++ b/slixmpp/xmlstream/stanzabase.py
@@ -17,7 +17,7 @@ from __future__ import with_statement, unicode_literals
import copy
import logging
import weakref
-from xml.etree import cElementTree as ET
+from xml.etree import ElementTree as ET
from slixmpp.xmlstream import JID
from slixmpp.xmlstream.tostring import tostring
@@ -203,7 +203,7 @@ class ElementBase(object):
"""
The core of Slixmpp's stanza XML manipulation and handling is provided
- by ElementBase. ElementBase wraps XML cElementTree objects and enables
+ by ElementBase. ElementBase wraps XML ElementTree objects and enables
access to the XML contents through dictionary syntax, similar in style
to the Ruby XMPP library Blather's stanza implementation.
@@ -387,7 +387,7 @@ class ElementBase(object):
self._index = 0
#: The underlying XML object for the stanza. It is a standard
- #: :class:`xml.etree.cElementTree` object.
+ #: :class:`xml.etree.ElementTree` object.
self.xml = xml
#: An ordered dictionary of plugin stanzas, mapped by their
@@ -1031,14 +1031,19 @@ class ElementBase(object):
if not lang:
lang = default_lang
+ parent = self.xml
for level, _ in enumerate(path):
# Generate the paths to the target elements and their parent.
element_path = "/".join(path[:len(path) - level])
parent_path = "/".join(path[:len(path) - level - 1])
elements = self.xml.findall(element_path)
- parent = self.xml.find(parent_path)
-
+
+ if parent_path == '':
+ parent_path = None
+ if parent_path is not None:
+ parent = self.xml.find(parent_path)
+
if elements:
if parent is None:
parent = self.xml
diff --git a/slixmpp/xmlstream/xmlstream.py b/slixmpp/xmlstream/xmlstream.py
index dbf515ca..3aac8c8e 100644
--- a/slixmpp/xmlstream/xmlstream.py
+++ b/slixmpp/xmlstream/xmlstream.py
@@ -277,6 +277,7 @@ class XMLStream(asyncio.BaseProtocol):
)
self.disconnect_reason = None
self.cancel_connection_attempt()
+ self.connect_loop_wait = 0
if host and port:
self.address = (host, int(port))
try:
@@ -301,6 +302,10 @@ class XMLStream(asyncio.BaseProtocol):
async def _connect_routine(self):
self.event_when_connected = "connected"
+ if self.connect_loop_wait > 0:
+ self.event('reconnect_delay', self.connect_loop_wait)
+ await asyncio.sleep(self.connect_loop_wait, loop=self.loop)
+
record = await self.pick_dns_answer(self.default_domain)
if record is not None:
host, address, dns_port = record
@@ -317,7 +322,6 @@ class XMLStream(asyncio.BaseProtocol):
else:
ssl_context = None
- await asyncio.sleep(self.connect_loop_wait, loop=self.loop)
if self._current_connection_attempt is None:
return
try:
@@ -376,6 +380,7 @@ class XMLStream(asyncio.BaseProtocol):
"ssl_object",
default=self.transport.get_extra_info("socket")
)
+ self._current_connection_attempt = None
self.init_parser()
self.send_raw(self.stream_header)
self.dns_answers = None
@@ -434,6 +439,9 @@ class XMLStream(asyncio.BaseProtocol):
self.send(error)
self.disconnect()
+ def is_connecting(self):
+ return self._current_connection_attempt is not None
+
def is_connected(self):
return self.transport is not None
@@ -467,7 +475,7 @@ class XMLStream(asyncio.BaseProtocol):
self._current_connection_attempt.cancel()
self._current_connection_attempt = None
- def disconnect(self, wait: float = 2.0, reason: Optional[str] = None) -> None:
+ def disconnect(self, wait: float = 2.0, reason: Optional[str] = None, ignore_send_queue: bool = False) -> 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 server, or when the server
@@ -487,13 +495,31 @@ class XMLStream(asyncio.BaseProtocol):
if wait == True:
wait = 2.0
+ if self.transport:
+ if self.waiting_queue.empty() or ignore_send_queue:
+ self.disconnect_reason = reason
+ self.cancel_connection_attempt()
+ if wait > 0.0:
+ self.send_raw(self.stream_footer)
+ self.schedule('Disconnect wait', wait,
+ self.abort, repeat=False)
+ else:
+ asyncio.ensure_future(
+ self._consume_send_queue_before_disconnecting(reason, wait),
+ loop=self.loop,
+ )
+ else:
+ self.event("disconnected", reason)
+
+ async def _consume_send_queue_before_disconnecting(self, reason: Optional[str], wait: float):
+ """Wait until the send queue is empty before disconnecting"""
+ await self.waiting_queue.join()
self.disconnect_reason = reason
self.cancel_connection_attempt()
- if self.transport:
- if wait > 0.0:
- self.send_raw(self.stream_footer)
- self.schedule('Disconnect wait', wait,
- self.abort, repeat=False)
+ if wait > 0.0:
+ self.send_raw(self.stream_footer)
+ self.schedule('Disconnect wait', wait,
+ self.abort, repeat=False)
def abort(self):
"""
@@ -506,14 +532,15 @@ class XMLStream(asyncio.BaseProtocol):
self.event("killed")
self.disconnected.set_result(True)
self.disconnected = asyncio.Future()
+ self.event("disconnected", self.disconnect_reason)
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, reason)
self.add_event_handler('disconnected', lambda event: self.connect(), disposable=True)
+ self.disconnect(wait, reason)
def configure_socket(self):
"""Set timeout and other options for self.socket.
@@ -888,7 +915,9 @@ class XMLStream(asyncio.BaseProtocol):
Execute the callback and remove the handler for it.
"""
self._safe_cb_run(name, cb)
- del self.scheduled_events[name]
+ # workaround for specific events which unschedule themselves
+ if name in self.scheduled_events:
+ del self.scheduled_events[name]
def incoming_filter(self, xml):
"""Filter incoming XML objects before they are processed.