summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml10
-rw-r--r--README.rst12
-rw-r--r--docs/guide_xep_0030.rst2
-rwxr-xr-xexamples/IoT_TestDevice.py22
-rwxr-xr-xexamples/disco_browser.py2
-rw-r--r--examples/http_over_xmpp.py101
-rwxr-xr-xexamples/migrate_roster.py2
-rwxr-xr-xexamples/roster_browser.py2
-rwxr-xr-xexamples/set_avatar.py2
-rwxr-xr-xsetup.py3
-rw-r--r--sleekxmpp/basexmpp.py7
-rw-r--r--sleekxmpp/clientxmpp.py13
-rw-r--r--sleekxmpp/componentxmpp.py9
-rw-r--r--sleekxmpp/features/feature_mechanisms/mechanisms.py10
-rw-r--r--sleekxmpp/jid.py28
-rw-r--r--sleekxmpp/plugins/__init__.py2
-rw-r--r--sleekxmpp/plugins/google/auth/stanza.py2
-rw-r--r--sleekxmpp/plugins/google/gmail/notifications.py4
-rw-r--r--sleekxmpp/plugins/google/nosave/stanza.py2
-rw-r--r--sleekxmpp/plugins/google/settings/settings.py2
-rw-r--r--sleekxmpp/plugins/xep_0004/stanza/field.py4
-rw-r--r--sleekxmpp/plugins/xep_0004/stanza/form.py52
-rw-r--r--sleekxmpp/plugins/xep_0009/remote.py61
-rw-r--r--sleekxmpp/plugins/xep_0009/rpc.py4
-rw-r--r--sleekxmpp/plugins/xep_0030/disco.py9
-rw-r--r--sleekxmpp/plugins/xep_0045.py10
-rw-r--r--sleekxmpp/plugins/xep_0054/stanza.py3
-rw-r--r--sleekxmpp/plugins/xep_0065/proxy.py8
-rw-r--r--sleekxmpp/plugins/xep_0084/stanza.py9
-rw-r--r--sleekxmpp/plugins/xep_0096/file_transfer.py1
-rw-r--r--sleekxmpp/plugins/xep_0122/__init__.py11
-rw-r--r--sleekxmpp/plugins/xep_0122/data_validation.py19
-rw-r--r--sleekxmpp/plugins/xep_0122/stanza.py94
-rw-r--r--sleekxmpp/plugins/xep_0138.py148
-rw-r--r--sleekxmpp/plugins/xep_0202/time.py7
-rw-r--r--sleekxmpp/plugins/xep_0257/stanza.py2
-rw-r--r--sleekxmpp/plugins/xep_0323/device.py115
-rw-r--r--sleekxmpp/plugins/xep_0323/sensordata.py453
-rw-r--r--sleekxmpp/plugins/xep_0323/stanza/sensordata.py178
-rw-r--r--sleekxmpp/plugins/xep_0323/timerreset.py7
-rw-r--r--sleekxmpp/plugins/xep_0325/control.py377
-rw-r--r--sleekxmpp/plugins/xep_0325/device.py46
-rw-r--r--sleekxmpp/plugins/xep_0325/stanza/control.py123
-rw-r--r--sleekxmpp/plugins/xep_0332/__init__.py17
-rw-r--r--sleekxmpp/plugins/xep_0332/http.py159
-rw-r--r--sleekxmpp/plugins/xep_0332/stanza/__init__.py13
-rw-r--r--sleekxmpp/plugins/xep_0332/stanza/data.py30
-rw-r--r--sleekxmpp/plugins/xep_0332/stanza/request.py71
-rw-r--r--sleekxmpp/plugins/xep_0332/stanza/response.py66
-rw-r--r--sleekxmpp/roster/single.py6
-rw-r--r--sleekxmpp/stanza/atom.py25
-rw-r--r--sleekxmpp/stanza/rootstanza.py4
-rw-r--r--sleekxmpp/test/sleektest.py16
-rw-r--r--sleekxmpp/thirdparty/__init__.py1
-rw-r--r--sleekxmpp/thirdparty/orderedset.py89
-rw-r--r--sleekxmpp/thirdparty/socks.py17
-rw-r--r--sleekxmpp/thirdparty/statemachine.py12
-rw-r--r--sleekxmpp/util/__init__.py9
-rw-r--r--sleekxmpp/util/sasl/mechanisms.py19
-rw-r--r--sleekxmpp/version.py4
-rw-r--r--sleekxmpp/xmlstream/cert.py2
-rw-r--r--sleekxmpp/xmlstream/stanzabase.py14
-rw-r--r--sleekxmpp/xmlstream/xmlstream.py55
-rw-r--r--tests/test_stanza_element.py4
-rw-r--r--tests/test_stanza_xep_0004.py61
-rw-r--r--tests/test_stanza_xep_0122.py189
-rw-r--r--tests/test_stanza_xep_0323.py135
-rw-r--r--tests/test_stanza_xep_0325.py80
-rw-r--r--tests/test_stream_xep_0050.py8
-rw-r--r--tests/test_stream_xep_0323.py424
-rw-r--r--tests/test_stream_xep_0325.py110
-rw-r--r--tox.ini2
73 files changed, 2408 insertions, 1213 deletions
diff --git a/.gitignore b/.gitignore
index 602416e8..628d1337 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ sleekxmpp.egg-info/
*~
.baboon/
.DS_STORE
+.idea/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..22e3abf1
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,10 @@
+language: python
+python:
+ - "2.6"
+ - "2.7"
+ - "3.2"
+ - "3.3"
+ - "3.4"
+install:
+ - "pip install ."
+script: testall.py
diff --git a/README.rst b/README.rst
index 7c14ab5a..3c142c9b 100644
--- a/README.rst
+++ b/README.rst
@@ -3,7 +3,7 @@ SleekXMPP
SleekXMPP is an MIT licensed XMPP library for Python 2.6/3.1+,
and is featured in examples in
-`XMPP: The Definitive Guide <http://oreilly.com/catalog/9780596521271>`_
+`XMPP: The Definitive Guide <http://oreilly.com/catalog/9780596521271>`_
by Kevin Smith, Remko Tronçon, and Peter Saint-Andre. If you've arrived
here from reading the Definitive Guide, please see the notes on updating
the examples to the latest version of SleekXMPP.
@@ -52,7 +52,7 @@ The latest source code for SleekXMPP may be found on `Github
Installing DNSPython
----------------------
+--------------------
If you are using Python3 and wish to use dnspython, you will have to checkout and
install the ``python3`` branch::
@@ -144,7 +144,7 @@ SleekXMPP projects::
if __name__ == '__main__':
- # Ideally use optparse or argparse to get JID,
+ # Ideally use optparse or argparse to get JID,
# password, and log level.
logging.basicConfig(level=logging.DEBUG,
@@ -158,15 +158,15 @@ SleekXMPP projects::
Credits
-------
**Main Author:** Nathan Fritz
- `fritzy@netflint.net <xmpp:fritzy@netflint.net?message>`_,
+ `fritzy@netflint.net <xmpp:fritzy@netflint.net?message>`_,
`@fritzy <http://twitter.com/fritzy>`_
Nathan is also the author of XMPPHP and `Seesmic-AS3-XMPP
- <http://code.google.com/p/seesmic-as3-xmpp/>`_, and a former member of
+ <http://code.google.com/p/seesmic-as3-xmpp/>`_, and a former member of
the XMPP Council.
**Co-Author:** Lance Stout
- `lancestout@gmail.com <xmpp:lancestout@gmail.com?message>`_,
+ `lancestout@gmail.com <xmpp:lancestout@gmail.com?message>`_,
`@lancestout <http://twitter.com/lancestout>`_
**Contributors:**
diff --git a/docs/guide_xep_0030.rst b/docs/guide_xep_0030.rst
index cb8d7d25..857f7ff1 100644
--- a/docs/guide_xep_0030.rst
+++ b/docs/guide_xep_0030.rst
@@ -161,7 +161,7 @@ item itself, and the JID and node that will own the item.
In this case, the owning JID and node are provided with the
parameters ``ijid`` and ``node``.
-Peforming Disco Queries
+Performing Disco Queries
-----------------------
The methods ``get_info()`` and ``get_items()`` are used to query remote JIDs
and their nodes for disco information. Since these methods are wrappers for
diff --git a/examples/IoT_TestDevice.py b/examples/IoT_TestDevice.py
index 8105aaff..b85a0b7c 100755
--- a/examples/IoT_TestDevice.py
+++ b/examples/IoT_TestDevice.py
@@ -11,18 +11,10 @@
See the file LICENSE for copying permission.
"""
-import os
-import sys
-# This can be used when you are in a test environment and need to make paths right
-sys.path=['/Users/jocke/Dropbox/06_dev/SleekXMPP']+sys.path
-
+import getpass
import logging
-import unittest
-import distutils.core
-import datetime
+import sys
-from glob import glob
-from os.path import splitext, basename, join as pjoin
from optparse import OptionParser
from urllib import urlopen
@@ -39,8 +31,6 @@ else:
from sleekxmpp.plugins.xep_0323.device import Device
-#from sleekxmpp.exceptions import IqError, IqTimeout
-
class IoT_TestDevice(sleekxmpp.ClientXMPP):
"""
@@ -179,13 +169,13 @@ if __name__ == '__main__':
# node=opts.nodeid,
# jid=xmpp.boundjid.full)
- myDevice = TheDevice(opts.nodeid);
+ myDevice = TheDevice(opts.nodeid)
# myDevice._add_field(name="Relay", typename="numeric", unit="Bool");
- myDevice._add_field(name="Temperature", typename="numeric", unit="C");
+ myDevice._add_field(name="Temperature", typename="numeric", unit="C")
myDevice._set_momentary_timestamp("2013-03-07T16:24:30")
- myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"});
+ myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"})
- xmpp['xep_0323'].register_node(nodeId=opts.nodeid, device=myDevice, commTimeout=10);
+ xmpp['xep_0323'].register_node(nodeId=opts.nodeid, device=myDevice, commTimeout=10)
xmpp.beClientOrServer(server=True)
while not(xmpp.testForRelease()):
xmpp.connect()
diff --git a/examples/disco_browser.py b/examples/disco_browser.py
index aeb4fb5e..78626e7c 100755
--- a/examples/disco_browser.py
+++ b/examples/disco_browser.py
@@ -94,7 +94,7 @@ class Disco(sleekxmpp.ClientXMPP):
info = self['xep_0030'].get_info(jid=self.target_jid,
node=self.target_node,
block=True)
- elif self.get in self.items_types:
+ 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.
diff --git a/examples/http_over_xmpp.py b/examples/http_over_xmpp.py
new file mode 100644
index 00000000..a2fbf664
--- /dev/null
+++ b/examples/http_over_xmpp.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Implementation of HTTP over XMPP transport
+ http://xmpp.org/extensions/xep-0332.html
+ Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp import ClientXMPP
+
+from optparse import OptionParser
+import logging
+import getpass
+
+
+class HTTPOverXMPPClient(ClientXMPP):
+ def __init__(self, jid, password):
+ ClientXMPP.__init__(self, jid, password)
+ self.register_plugin('xep_0332') # HTTP over XMPP Transport
+ self.add_event_handler(
+ 'session_start', self.session_start, threaded=True
+ )
+ self.add_event_handler('http_request', self.http_request_received)
+ self.add_event_handler('http_response', self.http_response_received)
+
+ def http_request_received(self, iq):
+ pass
+
+ def http_response_received(self, iq):
+ print 'HTTP Response Received : ', iq
+ print 'From : ', iq['from']
+ print 'To : ', iq['to']
+ print 'Type : ', iq['type']
+ print 'Headers : ', iq['resp']['headers']
+ print 'Code : ', iq['resp']['code']
+ print 'Message : ', iq['resp']['message']
+ print 'Data : ', iq['resp']['data']
+
+ def session_start(self, event):
+ # TODO: Fill in the blanks
+ self['xep_0332'].send_request(
+ to='?', method='?', resource='?', headers={}
+ )
+ self.disconnect()
+
+
+if __name__ == '__main__':
+
+ #
+ # NOTE: To run this example, fill up the blanks in session_start() and
+ # use the following command.
+ #
+ # ./http_over_xmpp.py -J <jid> -P <pwd> -i <ip> -p <port> [-v]
+ #
+
+ parser = OptionParser()
+
+ # Output verbosity options.
+ parser.add_option(
+ '-v', '--verbose', help='set logging to DEBUG', action='store_const',
+ dest='loglevel', const=logging.DEBUG, default=logging.ERROR
+ )
+
+ # JID and password options.
+ parser.add_option('-J', '--jid', dest='jid', help='JID')
+ parser.add_option('-P', '--password', dest='password', help='Password')
+
+ # XMPP server ip and port options.
+ parser.add_option(
+ '-i', '--ipaddr', dest='ipaddr',
+ help='IP Address of the XMPP server', default=None
+ )
+ parser.add_option(
+ '-p', '--port', dest='port',
+ help='Port of the XMPP server', default=None
+ )
+
+ opts, args = parser.parse_args()
+
+ # Setup logging.
+ logging.basicConfig(level=opts.loglevel,
+ format='%(levelname)-8s %(message)s')
+
+ if opts.jid is None:
+ opts.jid = raw_input('Username: ')
+ if opts.password is None:
+ opts.password = getpass.getpass('Password: ')
+
+ xmpp = HTTPOverXMPPClient(opts.jid, opts.password)
+ if xmpp.connect((opts.ipaddr, int(opts.port))):
+ print 'Connected!'
+ xmpp.process(block=True)
+ else:
+ print 'Not connected!'
+ print 'Goodbye....'
+
diff --git a/examples/migrate_roster.py b/examples/migrate_roster.py
index 797e4f44..9f679523 100755
--- a/examples/migrate_roster.py
+++ b/examples/migrate_roster.py
@@ -113,7 +113,7 @@ def on_session2(event):
new_xmpp.update_roster(jid,
name = item['name'],
groups = item['groups'])
- new_xmpp.disconnect()
+ new_xmpp.disconnect()
new_xmpp.add_event_handler('session_start', on_session2)
if new_xmpp.connect():
diff --git a/examples/roster_browser.py b/examples/roster_browser.py
index 485ac941..a16de24c 100755
--- a/examples/roster_browser.py
+++ b/examples/roster_browser.py
@@ -68,7 +68,7 @@ class RosterBrowser(sleekxmpp.ClientXMPP):
try:
self.get_roster()
except IqError as err:
- print('Error: %' % err.iq['error']['condition'])
+ print('Error: %s' % err.iq['error']['condition'])
except IqTimeout:
print('Error: Request timed out')
self.send_presence()
diff --git a/examples/set_avatar.py b/examples/set_avatar.py
index cae93c99..08e0b664 100755
--- a/examples/set_avatar.py
+++ b/examples/set_avatar.py
@@ -63,7 +63,7 @@ class AvatarSetter(sleekxmpp.ClientXMPP):
avatar_file = None
try:
- avatar_file = open(os.path.expanduser(self.filepath))
+ avatar_file = open(os.path.expanduser(self.filepath), 'rb')
except IOError:
print('Could not find file: %s' % self.filepath)
return self.disconnect()
diff --git a/setup.py b/setup.py
index cc4fdd5b..1cd245aa 100755
--- a/setup.py
+++ b/setup.py
@@ -93,6 +93,7 @@ packages = [ 'sleekxmpp',
'sleekxmpp/plugins/xep_0108',
'sleekxmpp/plugins/xep_0115',
'sleekxmpp/plugins/xep_0118',
+ 'sleekxmpp/plugins/xep_0122',
'sleekxmpp/plugins/xep_0128',
'sleekxmpp/plugins/xep_0131',
'sleekxmpp/plugins/xep_0152',
@@ -123,6 +124,8 @@ packages = [ 'sleekxmpp',
'sleekxmpp/plugins/xep_0323/stanza',
'sleekxmpp/plugins/xep_0325',
'sleekxmpp/plugins/xep_0325/stanza',
+ 'sleekxmpp/plugins/xep_0332',
+ 'sleekxmpp/plugins/xep_0332/stanza',
'sleekxmpp/plugins/google',
'sleekxmpp/plugins/google/gmail',
'sleekxmpp/plugins/google/auth',
diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py
index bf0ae4df..cb72b9bd 100644
--- a/sleekxmpp/basexmpp.py
+++ b/sleekxmpp/basexmpp.py
@@ -25,7 +25,6 @@ from sleekxmpp.exceptions import IqError, IqTimeout
from sleekxmpp.stanza import Message, Presence, Iq, StreamError
from sleekxmpp.stanza.roster import Roster
from sleekxmpp.stanza.nick import Nick
-from sleekxmpp.stanza.htmlim import HTMLIM
from sleekxmpp.xmlstream import XMLStream, JID
from sleekxmpp.xmlstream import ET, register_stanza_plugin
@@ -56,8 +55,8 @@ class BaseXMPP(XMLStream):
is used during initialization.
"""
- def __init__(self, jid='', default_ns='jabber:client'):
- XMLStream.__init__(self)
+ def __init__(self, jid='', default_ns='jabber:client', **kwargs):
+ XMLStream.__init__(self, **kwargs)
self.default_ns = default_ns
self.stream_ns = 'http://etherx.jabber.org/streams'
@@ -245,7 +244,7 @@ class BaseXMPP(XMLStream):
self.plugin[name].post_inited = True
return XMLStream.process(self, *args, **kwargs)
- def register_plugin(self, plugin, pconfig={}, module=None):
+ def register_plugin(self, plugin, pconfig=None, module=None):
"""Register and configure a plugin for use in this stream.
:param plugin: The name of the plugin class. Plugin names must
diff --git a/sleekxmpp/clientxmpp.py b/sleekxmpp/clientxmpp.py
index f837c0f2..31a5a70b 100644
--- a/sleekxmpp/clientxmpp.py
+++ b/sleekxmpp/clientxmpp.py
@@ -52,7 +52,6 @@ class ClientXMPP(BaseXMPP):
:param jid: The JID of the XMPP user account.
:param password: The password for the XMPP user account.
- :param ssl: **Deprecated.**
:param plugin_config: A dictionary of plugin configurations.
:param plugin_whitelist: A list of approved plugins that
will be loaded when calling
@@ -60,9 +59,15 @@ class ClientXMPP(BaseXMPP):
:param escape_quotes: **Deprecated.**
"""
- def __init__(self, jid, password, plugin_config={}, plugin_whitelist=[],
- escape_quotes=True, sasl_mech=None, lang='en'):
- BaseXMPP.__init__(self, jid, 'jabber:client')
+ def __init__(self, jid, password, plugin_config=None,
+ plugin_whitelist=None, escape_quotes=True, sasl_mech=None,
+ lang='en', **kwargs):
+ if not plugin_whitelist:
+ plugin_whitelist = []
+ if not plugin_config:
+ plugin_config = {}
+
+ BaseXMPP.__init__(self, jid, 'jabber:client', **kwargs)
self.escape_quotes = escape_quotes
self.plugin_config = plugin_config
diff --git a/sleekxmpp/componentxmpp.py b/sleekxmpp/componentxmpp.py
index bac455e2..4b229a6f 100644
--- a/sleekxmpp/componentxmpp.py
+++ b/sleekxmpp/componentxmpp.py
@@ -49,8 +49,13 @@ class ComponentXMPP(BaseXMPP):
Defaults to ``False``.
"""
- def __init__(self, jid, secret, host=None, port=None,
- plugin_config={}, plugin_whitelist=[], use_jc_ns=False):
+ def __init__(self, jid, secret, host=None, port=None, plugin_config=None, plugin_whitelist=None, use_jc_ns=False):
+
+ if not plugin_whitelist:
+ plugin_whitelist = []
+ if not plugin_config:
+ plugin_config = {}
+
if use_jc_ns:
default_ns = 'jabber:client'
else:
diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py
index 17ad5ed0..1d8f8798 100644
--- a/sleekxmpp/features/feature_mechanisms/mechanisms.py
+++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py
@@ -187,14 +187,14 @@ class FeatureMechanisms(BasePlugin):
except sasl.SASLCancelled:
self.attempted_mechs.add(self.mech.name)
self._send_auth()
- except sasl.SASLFailed:
- self.attempted_mechs.add(self.mech.name)
- self._send_auth()
except sasl.SASLMutualAuthFailed:
log.error("Mutual authentication failed! " + \
"A security breach is possible.")
self.attempted_mechs.add(self.mech.name)
self.xmpp.disconnect()
+ except sasl.SASLFailed:
+ self.attempted_mechs.add(self.mech.name)
+ self._send_auth()
else:
resp.send(now=True)
@@ -207,13 +207,13 @@ class FeatureMechanisms(BasePlugin):
resp['value'] = self.mech.process(stanza['value'])
except sasl.SASLCancelled:
self.stanza.Abort(self.xmpp).send()
- except sasl.SASLFailed:
- self.stanza.Abort(self.xmpp).send()
except sasl.SASLMutualAuthFailed:
log.error("Mutual authentication failed! " + \
"A security breach is possible.")
self.attempted_mechs.add(self.mech.name)
self.xmpp.disconnect()
+ except sasl.SASLFailed:
+ self.stanza.Abort(self.xmpp).send()
else:
if resp.get_value() == '':
resp.del_value()
diff --git a/sleekxmpp/jid.py b/sleekxmpp/jid.py
index ac5ba30d..754a3d3a 100644
--- a/sleekxmpp/jid.py
+++ b/sleekxmpp/jid.py
@@ -72,19 +72,18 @@ JID_CACHE_LOCK = threading.Lock()
JID_CACHE_MAX_SIZE = 1024
def _cache(key, parts, locked):
- JID_CACHE[key] = (parts, locked)
- if len(JID_CACHE) > JID_CACHE_MAX_SIZE:
- with JID_CACHE_LOCK:
- while len(JID_CACHE) > JID_CACHE_MAX_SIZE:
- found = None
- for key, item in JID_CACHE.items():
- if not item[1]: # if not locked
- found = key
- break
- if not found: # more than MAX_SIZE locked
- # warn?
+ with JID_CACHE_LOCK:
+ JID_CACHE[key] = (parts, locked)
+ while len(JID_CACHE) > JID_CACHE_MAX_SIZE:
+ found = None
+ for key, item in JID_CACHE.items():
+ if not item[1]: # if not locked
+ found = key
break
- del JID_CACHE[found]
+ if not found: # more than MAX_SIZE locked
+ # warn?
+ break
+ del JID_CACHE[found]
# pylint: disable=c0103
#: The nodeprep profile of stringprep used to validate the local,
@@ -529,10 +528,6 @@ class JID(object):
return self._jid[0] or ''
@property
- def bare(self):
- return _format_jid(self._jid[0], self._jid[1])
-
- @property
def server(self):
return self._jid[1] or ''
@@ -556,7 +551,6 @@ class JID(object):
def bare(self):
return _format_jid(self._jid[0], self._jid[1])
-
@resource.setter
def resource(self, value):
self._jid = JID(self, resource=value)._jid
diff --git a/sleekxmpp/plugins/__init__.py b/sleekxmpp/plugins/__init__.py
index 951f31eb..f501687b 100644
--- a/sleekxmpp/plugins/__init__.py
+++ b/sleekxmpp/plugins/__init__.py
@@ -47,6 +47,7 @@ __all__ = [
'xep_0108', # User Activity
'xep_0115', # Entity Capabilities
'xep_0118', # User Tune
+ 'xep_0122', # Data Forms Validation
'xep_0128', # Extended Service Discovery
'xep_0131', # Standard Headers and Internet Metadata
'xep_0133', # Service Administration
@@ -83,4 +84,5 @@ __all__ = [
'xep_0319', # Last User Interaction in Presence
'xep_0323', # IoT Systems Sensor Data
'xep_0325', # IoT Systems Control
+ 'xep_0332', # HTTP Over XMPP Transport
]
diff --git a/sleekxmpp/plugins/google/auth/stanza.py b/sleekxmpp/plugins/google/auth/stanza.py
index 49c5cba7..2d13f85a 100644
--- a/sleekxmpp/plugins/google/auth/stanza.py
+++ b/sleekxmpp/plugins/google/auth/stanza.py
@@ -24,7 +24,7 @@ class GoogleAuth(ElementBase):
print('setting up google extension')
def get_client_uses_full_bind_result(self):
- return self.parent()._get_attr(self.disovery_attr) == 'true'
+ return self.parent()._get_attr(self.discovery_attr) == 'true'
def set_client_uses_full_bind_result(self, value):
print('>>>', value)
diff --git a/sleekxmpp/plugins/google/gmail/notifications.py b/sleekxmpp/plugins/google/gmail/notifications.py
index 509a95fd..e65b2ca7 100644
--- a/sleekxmpp/plugins/google/gmail/notifications.py
+++ b/sleekxmpp/plugins/google/gmail/notifications.py
@@ -74,8 +74,8 @@ class Gmail(BasePlugin):
return resp
def _update_last_results(self, iq, callback=None):
- self._last_result_time = data['gmail_messages']['result_time']
- threads = data['gmail_messages']['threads']
+ self._last_result_time = iq['gmail_messages']['result_time']
+ threads = iq['gmail_messages']['threads']
if threads:
self._last_result_tid = threads[0]['tid']
if callback:
diff --git a/sleekxmpp/plugins/google/nosave/stanza.py b/sleekxmpp/plugins/google/nosave/stanza.py
index d8701322..791d4b0c 100644
--- a/sleekxmpp/plugins/google/nosave/stanza.py
+++ b/sleekxmpp/plugins/google/nosave/stanza.py
@@ -52,7 +52,7 @@ class Item(ElementBase):
def get_source(self):
return JID(self._get_attr('source', ''))
- def set_source(self):
+ def set_source(self, value):
self._set_attr('source', str(value))
diff --git a/sleekxmpp/plugins/google/settings/settings.py b/sleekxmpp/plugins/google/settings/settings.py
index 7122ff56..591956fc 100644
--- a/sleekxmpp/plugins/google/settings/settings.py
+++ b/sleekxmpp/plugins/google/settings/settings.py
@@ -6,8 +6,6 @@
See the file LICENSE for copying permission.
"""
-import logging
-
from sleekxmpp.stanza import Iq
from sleekxmpp.xmlstream.handler import Callback
from sleekxmpp.xmlstream.matcher import StanzaPath
diff --git a/sleekxmpp/plugins/xep_0004/stanza/field.py b/sleekxmpp/plugins/xep_0004/stanza/field.py
index 51f85995..73e48758 100644
--- a/sleekxmpp/plugins/xep_0004/stanza/field.py
+++ b/sleekxmpp/plugins/xep_0004/stanza/field.py
@@ -13,8 +13,9 @@ class FormField(ElementBase):
namespace = 'jabber:x:data'
name = 'field'
plugin_attrib = 'field'
+ plugin_multi_attrib = 'fields'
interfaces = set(('answer', 'desc', 'required', 'value',
- 'options', 'label', 'type', 'var'))
+ 'label', 'type', 'var'))
sub_interfaces = set(('desc',))
plugin_tag_map = {}
plugin_attrib_map = {}
@@ -165,6 +166,7 @@ class FieldOption(ElementBase):
plugin_attrib = 'option'
interfaces = set(('label', 'value'))
sub_interfaces = set(('value',))
+ plugin_multi_attrib = 'options'
FormField.addOption = FormField.add_option
diff --git a/sleekxmpp/plugins/xep_0004/stanza/form.py b/sleekxmpp/plugins/xep_0004/stanza/form.py
index bbd8540f..3dcc7821 100644
--- a/sleekxmpp/plugins/xep_0004/stanza/form.py
+++ b/sleekxmpp/plugins/xep_0004/stanza/form.py
@@ -9,7 +9,7 @@
import copy
import logging
-from sleekxmpp.thirdparty import OrderedDict
+from sleekxmpp.thirdparty import OrderedDict, OrderedSet
from sleekxmpp.xmlstream import ElementBase, ET
from sleekxmpp.plugins.xep_0004.stanza import FormField
@@ -22,8 +22,7 @@ class Form(ElementBase):
namespace = 'jabber:x:data'
name = 'x'
plugin_attrib = 'form'
- interfaces = set(('fields', 'instructions', 'items',
- 'reported', 'title', 'type', 'values'))
+ interfaces = OrderedSet(('instructions', 'reported', 'title', 'type', 'items', ))
sub_interfaces = set(('title',))
form_types = set(('cancel', 'form', 'result', 'submit'))
@@ -43,12 +42,12 @@ class Form(ElementBase):
@property
def field(self):
- return self['fields']
+ return self.get_fields()
def set_type(self, ftype):
self._set_attr('type', ftype)
if ftype == 'submit':
- fields = self['fields']
+ fields = self.get_fields()
for var in fields:
field = fields[var]
del field['type']
@@ -74,7 +73,8 @@ class Form(ElementBase):
field['desc'] = desc
field['required'] = required
if options is not None:
- field['options'] = options
+ for option in options:
+ field.add_option(**option)
else:
del field['type']
self.append(field)
@@ -151,7 +151,6 @@ class Form(ElementBase):
return fields
def get_instructions(self):
- instructions = ''
instsXML = self.xml.findall('{%s}instructions' % self.namespace)
return "\n".join([instXML.text for instXML in instsXML])
@@ -170,7 +169,7 @@ class Form(ElementBase):
def get_reported(self):
fields = OrderedDict()
xml = self.xml.findall('{%s}reported/{%s}field' % (self.namespace,
- FormField.namespace))
+ FormField.namespace))
for field in xml:
field = FormField(xml=field)
fields[field['var']] = field
@@ -178,7 +177,7 @@ class Form(ElementBase):
def get_values(self):
values = OrderedDict()
- fields = self['fields']
+ fields = self.get_fields()
for var in fields:
values[var] = fields[var]['value']
return values
@@ -195,7 +194,14 @@ class Form(ElementBase):
fields = fields.items()
for var, field in fields:
field['var'] = var
- self.add_field(**field)
+ self.add_field(
+ var = field.get('var'),
+ label = field.get('label'),
+ desc = field.get('desc'),
+ required = field.get('required'),
+ value = field.get('value'),
+ options = field.get('options'),
+ type = field.get('type'))
def set_instructions(self, instructions):
del self['instructions']
@@ -213,17 +219,33 @@ class Form(ElementBase):
self.add_item(item)
def set_reported(self, reported):
+ """
+ This either needs a dictionary or dictionaries or a dictionary of form fields.
+ :param reported:
+ :return:
+ """
for var in reported:
field = reported[var]
- field['var'] = var
- self.add_reported(var, **field)
+
+ if isinstance(field, dict):
+ self.add_reported(**field)
+ else:
+ reported = self.xml.find('{%s}reported' % self.namespace)
+ if reported is None:
+ reported = ET.Element('{%s}reported' % self.namespace)
+ self.xml.append(reported)
+
+ fieldXML = ET.Element('{%s}field' % FormField.namespace)
+ reported.append(fieldXML)
+ new_field = FormField(xml=fieldXML)
+ new_field.values = field.values
def set_values(self, values):
- fields = self['fields']
+ fields = self.get_fields()
for field in values:
- if field not in fields:
+ if field not in self.get_fields():
fields[field] = self.add_field(var=field)
- fields[field]['value'] = values[field]
+ self.get_fields()[field]['value'] = values[field]
def merge(self, other):
new = copy.copy(self)
diff --git a/sleekxmpp/plugins/xep_0009/remote.py b/sleekxmpp/plugins/xep_0009/remote.py
index 8c08e8f3..b02f587e 100644
--- a/sleekxmpp/plugins/xep_0009/remote.py
+++ b/sleekxmpp/plugins/xep_0009/remote.py
@@ -6,7 +6,7 @@
See the file LICENSE for copying permission.
"""
-from binding import py2xml, xml2py, xml2fault, fault2xml
+from sleekxmpp.plugins.xep_0009.binding import py2xml, xml2py, xml2fault, fault2xml
from threading import RLock
import abc
import inspect
@@ -18,6 +18,45 @@ import traceback
log = logging.getLogger(__name__)
+# Define a function _isstr() to check if an object is a string in a way
+# compatible with Python 2 and Python 3 (basestring does not exists in Python 3).
+try:
+ basestring # This evaluation will throw an exception if basestring does not exists (Python 3).
+ def _isstr(obj):
+ return isinstance(obj, basestring)
+except NameError:
+ def _isstr(obj):
+ return isinstance(obj, str)
+
+
+# Class decorator to declare a metaclass to a class in a way compatible with Python 2 and 3.
+# This decorator is copied from 'six' (https://bitbucket.org/gutworth/six):
+#
+# Copyright (c) 2010-2015 Benjamin Peterson
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+def _add_metaclass(metaclass):
+ def wrapper(cls):
+ orig_vars = cls.__dict__.copy()
+ slots = orig_vars.get('__slots__')
+ if slots is not None:
+ if isinstance(slots, str):
+ slots = [slots]
+ for slots_var in slots:
+ orig_vars.pop(slots_var)
+ orig_vars.pop('__dict__', None)
+ orig_vars.pop('__weakref__', None)
+ return metaclass(cls.__name__, cls.__bases__, orig_vars)
+ return wrapper
+
def _intercept(method, name, public):
def _resolver(instance, *args, **kwargs):
log.debug("Locally calling %s.%s with arguments %s.", instance.FQN(), method.__name__, args)
@@ -68,7 +107,7 @@ def remote(function_argument, public = True):
if hasattr(function_argument, '__call__'):
return _intercept(function_argument, None, public)
else:
- if not isinstance(function_argument, basestring):
+ if not _isstr(function_argument):
if not isinstance(function_argument, bool):
raise Exception('Expected an RPC method name or visibility modifier!')
else:
@@ -222,12 +261,11 @@ class TimeoutException(Exception):
pass
+@_add_metaclass(abc.ABCMeta)
class Callback(object):
'''
A base class for callback handlers.
'''
- __metaclass__ = abc.ABCMeta
-
@abc.abstractproperty
def set_value(self, value):
@@ -291,7 +329,7 @@ class Future(Callback):
self._event.set()
-
+@_add_metaclass(abc.ABCMeta)
class Endpoint(object):
'''
The Endpoint class is an abstract base class for all objects
@@ -303,8 +341,6 @@ class Endpoint(object):
which specifies which object an RPC call refers to. It is the
first part in a RPC method name '<fqn>.<method>'.
'''
- __metaclass__ = abc.ABCMeta
-
def __init__(self, session, target_jid):
'''
@@ -491,7 +527,7 @@ class RemoteSession(object):
def _find_key(self, dict, value):
"""return the key of dictionary dic given the value"""
- search = [k for k, v in dict.iteritems() if v == value]
+ search = [k for k, v in dict.items() if v == value]
if len(search) == 0:
return None
else:
@@ -547,7 +583,7 @@ class RemoteSession(object):
result = handler_cls(*args, **kwargs)
Endpoint.__init__(result, self, self._client.boundjid.full)
method_dict = result.get_methods()
- for method_name, method in method_dict.iteritems():
+ for method_name, method in method_dict.items():
#!!! self._client.plugin['xep_0009'].register_call(result.FQN(), method, method_name)
self._register_call(result.FQN(), method, method_name)
self._register_acl(result.FQN(), acl)
@@ -569,11 +605,11 @@ class RemoteSession(object):
self._register_callback(pid, callback)
iq.send()
- def close(self):
+ def close(self, wait=False):
'''
Closes this session.
'''
- self._client.disconnect(False)
+ self._client.disconnect(wait=wait)
self._session_close_callback()
def _on_jabber_rpc_method_call(self, iq):
@@ -697,7 +733,8 @@ class Remote(object):
if(client.boundjid.bare in cls._sessions):
raise RemoteException("There already is a session associated with these credentials!")
else:
- cls._sessions[client.boundjid.bare] = client;
+ cls._sessions[client.boundjid.bare] = client
+
def _session_close_callback():
with Remote._lock:
del cls._sessions[client.boundjid.bare]
diff --git a/sleekxmpp/plugins/xep_0009/rpc.py b/sleekxmpp/plugins/xep_0009/rpc.py
index 3378c650..6179355e 100644
--- a/sleekxmpp/plugins/xep_0009/rpc.py
+++ b/sleekxmpp/plugins/xep_0009/rpc.py
@@ -61,7 +61,7 @@ class XEP_0009(BasePlugin):
iq.enable('rpc_query')
iq['rpc_query']['method_call']['method_name'] = pmethod
iq['rpc_query']['method_call']['params'] = params
- return iq;
+ return iq
def make_iq_method_response(self, pid, pto, params):
iq = self.xmpp.makeIqResult(pid)
@@ -93,7 +93,7 @@ class XEP_0009(BasePlugin):
def _item_not_found(self, iq):
payload = iq.get_payload()
- iq.reply().error().set_payload(payload);
+ iq.reply().error().set_payload(payload)
iq['error']['code'] = '404'
iq['error']['type'] = 'cancel'
iq['error']['condition'] = 'item-not-found'
diff --git a/sleekxmpp/plugins/xep_0030/disco.py b/sleekxmpp/plugins/xep_0030/disco.py
index 8a397923..721f73f6 100644
--- a/sleekxmpp/plugins/xep_0030/disco.py
+++ b/sleekxmpp/plugins/xep_0030/disco.py
@@ -324,7 +324,7 @@ class XEP_0030(BasePlugin):
callback -- Optional callback to execute when a reply is
received instead of blocking and waiting for
the reply.
- timeout_callback -- Optional callback to execute when no result
+ timeout_callback -- Optional callback to execute when no result
has been received in timeout seconds.
"""
if local is None:
@@ -408,7 +408,7 @@ class XEP_0030(BasePlugin):
iterator -- If True, return a result set iterator using
the XEP-0059 plugin, if the plugin is loaded.
Otherwise the parameter is ignored.
- timeout_callback -- Optional callback to execute when no result
+ timeout_callback -- Optional callback to execute when no result
has been received in timeout seconds.
"""
if local or local is None and jid is None:
@@ -604,7 +604,7 @@ class XEP_0030(BasePlugin):
"""
self.api['del_features'](jid, node, None, kwargs)
- def _run_node_handler(self, htype, jid, node=None, ifrom=None, data={}):
+ def _run_node_handler(self, htype, jid, node=None, ifrom=None, data=None):
"""
Execute the most specific node handler for the given
JID/node combination.
@@ -615,6 +615,9 @@ class XEP_0030(BasePlugin):
node -- The node requested.
data -- Optional, custom data to pass to the handler.
"""
+ if not data:
+ data = {}
+
return self.api[htype](jid, node, ifrom, data)
def _handle_disco_info(self, iq):
diff --git a/sleekxmpp/plugins/xep_0045.py b/sleekxmpp/plugins/xep_0045.py
index ca5ed1ef..cc96d66e 100644
--- a/sleekxmpp/plugins/xep_0045.py
+++ b/sleekxmpp/plugins/xep_0045.py
@@ -397,6 +397,16 @@ class XEP_0045(BasePlugin):
return None
return self.rooms[room].keys()
+ def getUsersByAffiliation(cls, room, affiliation='member', ifrom=None):
+ if affiliation not in ('outcast', 'member', 'admin', 'owner', 'none'):
+ raise TypeError
+ query = ET.Element('{http://jabber.org/protocol/muc#admin}query')
+ item = ET.Element('{http://jabber.org/protocol/muc#admin}item', {'affiliation': affiliation})
+ query.append(item)
+ iq = cls.xmpp.Iq(sto=room, sfrom=ifrom, stype='get')
+ iq.append(query)
+ return iq.send()
+
xep_0045 = XEP_0045
register_plugin(XEP_0045)
diff --git a/sleekxmpp/plugins/xep_0054/stanza.py b/sleekxmpp/plugins/xep_0054/stanza.py
index 72da0b51..2d017d6e 100644
--- a/sleekxmpp/plugins/xep_0054/stanza.py
+++ b/sleekxmpp/plugins/xep_0054/stanza.py
@@ -128,7 +128,8 @@ class Telephone(ElementBase):
def setup(self, xml=None):
super(Telephone, self).setup(xml=xml)
- self._set_sub_text('NUMBER', '', keep=True)
+ ## this blanks out numbers received from server
+ ##self._set_sub_text('NUMBER', '', keep=True)
def set_number(self, value):
self._set_sub_text('NUMBER', value, keep=True)
diff --git a/sleekxmpp/plugins/xep_0065/proxy.py b/sleekxmpp/plugins/xep_0065/proxy.py
index fdd9f97e..d890b57a 100644
--- a/sleekxmpp/plugins/xep_0065/proxy.py
+++ b/sleekxmpp/plugins/xep_0065/proxy.py
@@ -206,7 +206,7 @@ class XEP_0065(base_plugin):
# Though this should not be neccessary remove the closed session anyway
with self._sessions_lock:
if sid in self._sessions:
- log.warn(('SOCKS5 session with sid = "%s" was not ' +
+ log.warn(('SOCKS5 session with sid = "%s" was not ' +
'removed from _sessions by sock.close()') % sid)
del self._sessions[sid]
@@ -240,9 +240,9 @@ class XEP_0065(base_plugin):
# The hostname MUST be SHA1(SID + Requester JID + Target JID)
# where the output is hexadecimal-encoded (not binary).
digest = sha1()
- digest.update(sid)
- digest.update(str(requester))
- digest.update(str(target))
+ digest.update(sid.encode('utf-8'))
+ digest.update(str(requester).encode('utf-8'))
+ digest.update(str(target).encode('utf-8'))
dest = digest.hexdigest()
diff --git a/sleekxmpp/plugins/xep_0084/stanza.py b/sleekxmpp/plugins/xep_0084/stanza.py
index 22f11b72..fd21e6f1 100644
--- a/sleekxmpp/plugins/xep_0084/stanza.py
+++ b/sleekxmpp/plugins/xep_0084/stanza.py
@@ -8,7 +8,7 @@
from base64 import b64encode, b64decode
-from sleekxmpp.util import bytes
+from sleekxmpp.util import bytes as sbytes
from sleekxmpp.xmlstream import ET, ElementBase, register_stanza_plugin
@@ -20,12 +20,15 @@ class Data(ElementBase):
def get_value(self):
if self.xml.text:
- return b64decode(bytes(self.xml.text))
+ return b64decode(sbytes(self.xml.text))
return ''
def set_value(self, value):
if value:
- self.xml.text = b64encode(bytes(value))
+ self.xml.text = b64encode(sbytes(value))
+ # Python3 base64 encoded is bytes and needs to be decoded to string
+ if isinstance(self.xml.text, bytes):
+ self.xml.text = self.xml.text.decode()
else:
self.xml.text = ''
diff --git a/sleekxmpp/plugins/xep_0096/file_transfer.py b/sleekxmpp/plugins/xep_0096/file_transfer.py
index 6873c7f5..52ba2f27 100644
--- a/sleekxmpp/plugins/xep_0096/file_transfer.py
+++ b/sleekxmpp/plugins/xep_0096/file_transfer.py
@@ -47,6 +47,7 @@ class XEP_0096(BasePlugin):
data['size'] = size
data['date'] = date
data['desc'] = desc
+ data['hash'] = hash
if allow_ranged:
data.enable('range')
diff --git a/sleekxmpp/plugins/xep_0122/__init__.py b/sleekxmpp/plugins/xep_0122/__init__.py
new file mode 100644
index 00000000..4b3e9483
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0122/__init__.py
@@ -0,0 +1,11 @@
+
+from sleekxmpp.plugins.base import register_plugin
+from sleekxmpp.plugins.xep_0122.stanza import FormValidation
+from sleekxmpp.plugins.xep_0122.data_validation import XEP_0122
+
+
+register_plugin(XEP_0122)
+
+
+# Retain some backwards compatibility
+xep_0122 = XEP_0122
diff --git a/sleekxmpp/plugins/xep_0122/data_validation.py b/sleekxmpp/plugins/xep_0122/data_validation.py
new file mode 100644
index 00000000..ec2cdfcc
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0122/data_validation.py
@@ -0,0 +1,19 @@
+from sleekxmpp.xmlstream import register_stanza_plugin
+from sleekxmpp.plugins import BasePlugin
+from sleekxmpp.plugins.xep_0004 import stanza
+from sleekxmpp.plugins.xep_0004.stanza import FormField
+from sleekxmpp.plugins.xep_0122.stanza import FormValidation
+
+
+class XEP_0122(BasePlugin):
+ """
+ XEP-0004: Data Forms
+ """
+
+ name = 'xep_0122'
+ description = 'XEP-0122: Data Forms Validation'
+ dependencies = set(['xep_0004'])
+ stanza = stanza
+
+ def plugin_init(self):
+ register_stanza_plugin(FormField, FormValidation)
diff --git a/sleekxmpp/plugins/xep_0122/stanza.py b/sleekxmpp/plugins/xep_0122/stanza.py
new file mode 100644
index 00000000..bc3c177a
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0122/stanza.py
@@ -0,0 +1,94 @@
+
+from sleekxmpp.xmlstream import ElementBase, ET
+
+
+class FormValidation(ElementBase):
+ """
+ Validation values for form fields.
+
+ Example:
+
+ <field var='evt.date' type='text-single' label='Event Date/Time'>
+ <validate xmlns='http://jabber.org/protocol/xdata-validate'
+ datatype='xs:dateTime'/>
+ <value>2003-10-06T11:22:00-07:00</value>
+ </field>
+
+ Questions:
+ Should this look at the datatype value and convert the range values as appropriate?
+ Should this stanza provide a pass/fail for a value from the field, or convert field value to datatype?
+ """
+
+ namespace = 'http://jabber.org/protocol/xdata-validate'
+ name = 'validate'
+ plugin_attrib = 'validate'
+ interfaces = {'datatype', 'basic', 'open', 'range', 'regex', }
+ sub_interfaces = {'basic', 'open', 'range', 'regex', }
+ plugin_attrib_map = {}
+ plugin_tag_map = {}
+
+ def _add_field(self, name):
+ self.remove_all()
+ item_xml = ET.Element('{%s}%s' % (self.namespace, name))
+ self.xml.append(item_xml)
+ return item_xml
+
+ def set_basic(self, value):
+ if value:
+ self._add_field('basic')
+ else:
+ del self['basic']
+
+ def set_open(self, value):
+ if value:
+ self._add_field('open')
+ else:
+ del self['open']
+
+ def set_regex(self, regex):
+ if regex:
+ _regex = self._add_field('regex')
+ _regex.text = regex
+ else:
+ del self['regex']
+
+ def set_range(self, value, minimum=None, maximum=None):
+ if value:
+ _range = self._add_field('range')
+ _range.attrib['min'] = str(minimum)
+ _range.attrib['max'] = str(maximum)
+ else:
+ del self['range']
+
+ def remove_all(self, except_tag=None):
+ for a in self.sub_interfaces:
+ if a != except_tag:
+ del self[a]
+
+ def get_basic(self):
+ present = self.xml.find('{%s}basic' % self.namespace)
+ return present is not None
+
+ def get_open(self):
+ present = self.xml.find('{%s}open' % self.namespace)
+ return present is not None
+
+ def get_regex(self):
+ present = self.xml.find('{%s}regex' % self.namespace)
+ if present is not None:
+ return present.text
+
+ return False
+
+ def get_range(self):
+ present = self.xml.find('{%s}range' % self.namespace)
+ if present is not None:
+ attributes = present.attrib
+ return_value = dict()
+ if 'min' in attributes:
+ return_value['minimum'] = attributes['min']
+ if 'max' in attributes:
+ return_value['maximum'] = attributes['max']
+ return return_value
+
+ return False
diff --git a/sleekxmpp/plugins/xep_0138.py b/sleekxmpp/plugins/xep_0138.py
new file mode 100644
index 00000000..c5d8f06f
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0138.py
@@ -0,0 +1,148 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2011 Nathanael C. Fritz
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+import logging
+import socket
+import zlib
+
+from sleekxmpp.thirdparty.suelta.util import bytes
+
+
+from sleekxmpp.stanza import StreamFeatures
+from sleekxmpp.xmlstream import RestartStream, register_stanza_plugin, ElementBase, StanzaBase
+from sleekxmpp.xmlstream.matcher import *
+from sleekxmpp.xmlstream.handler import *
+from sleekxmpp.plugins import BasePlugin, register_plugin
+
+log = logging.getLogger(__name__)
+
+
+class Compression(ElementBase):
+ name = 'compression'
+ namespace = 'http://jabber.org/features/compress'
+ interfaces = set(('methods',))
+ plugin_attrib = 'compression'
+ plugin_tag_map = {}
+ plugin_attrib_map = {}
+
+ def get_methods(self):
+ methods = []
+ for method in self.xml.findall('{%s}method' % self.namespace):
+ methods.append(method.text)
+ return methods
+
+
+class Compress(StanzaBase):
+ name = 'compress'
+ namespace = 'http://jabber.org/protocol/compress'
+ interfaces = set(('method',))
+ sub_interfaces = interfaces
+ plugin_attrib = 'compress'
+ plugin_tag_map = {}
+ plugin_attrib_map = {}
+
+ def setup(self, xml):
+ StanzaBase.setup(self, xml)
+ self.xml.tag = self.tag_name()
+
+
+class Compressed(StanzaBase):
+ name = 'compressed'
+ namespace = 'http://jabber.org/protocol/compress'
+ interfaces = set()
+ plugin_tag_map = {}
+ plugin_attrib_map = {}
+
+ def setup(self, xml):
+ StanzaBase.setup(self, xml)
+ self.xml.tag = self.tag_name()
+
+
+
+
+class ZlibSocket(object):
+
+ def __init__(self, socketobj):
+ self.__socket = socketobj
+ self.compressor = zlib.compressobj()
+ self.decompressor = zlib.decompressobj(zlib.MAX_WBITS)
+
+ def __getattr__(self, name):
+ return getattr(self.__socket, name)
+
+ def send(self, data):
+ sentlen = len(data)
+ data = self.compressor.compress(data)
+ data += self.compressor.flush(zlib.Z_SYNC_FLUSH)
+ log.debug(b'>>> (compressed)' + (data.encode("hex")))
+ #return self.__socket.send(data)
+ sentactuallen = self.__socket.send(data)
+ assert(sentactuallen == len(data))
+
+ return sentlen
+
+ def recv(self, *args, **kwargs):
+ data = self.__socket.recv(*args, **kwargs)
+ log.debug(b'<<< (compressed)' + data.encode("hex"))
+ return self.decompressor.decompress(self.decompressor.unconsumed_tail + data)
+
+
+class XEP_0138(BasePlugin):
+ """
+ XEP-0138: Compression
+ """
+ name = "xep_0138"
+ description = "XEP-0138: Compression"
+ dependencies = set(["xep_0030"])
+
+ def plugin_init(self):
+ self.xep = '0138'
+ self.description = 'Stream Compression (Generic)'
+
+ self.compression_methods = {'zlib': True}
+
+ register_stanza_plugin(StreamFeatures, Compression)
+ self.xmpp.register_stanza(Compress)
+ self.xmpp.register_stanza(Compressed)
+
+ self.xmpp.register_handler(
+ Callback('Compressed',
+ StanzaPath('compressed'),
+ self._handle_compressed,
+ instream=True))
+
+ self.xmpp.register_feature('compression',
+ self._handle_compression,
+ restart=True,
+ order=self.config.get('order', 5))
+
+ def register_compression_method(self, name, handler):
+ self.compression_methods[name] = handler
+
+ def _handle_compression(self, features):
+ for method in features['compression']['methods']:
+ if method in self.compression_methods:
+ log.info('Attempting to use %s compression' % method)
+ c = Compress(self.xmpp)
+ c['method'] = method
+ c.send(now=True)
+ return True
+ return False
+
+ def _handle_compressed(self, stanza):
+ self.xmpp.features.add('compression')
+ log.debug('Stream Compressed!')
+ compressed_socket = ZlibSocket(self.xmpp.socket)
+ self.xmpp.set_socket(compressed_socket)
+ raise RestartStream()
+
+ def _handle_failure(self, stanza):
+ pass
+
+xep_0138 = XEP_0138
+register_plugin(XEP_0138)
diff --git a/sleekxmpp/plugins/xep_0202/time.py b/sleekxmpp/plugins/xep_0202/time.py
index d5b3af37..4e48eae8 100644
--- a/sleekxmpp/plugins/xep_0202/time.py
+++ b/sleekxmpp/plugins/xep_0202/time.py
@@ -72,9 +72,10 @@ class XEP_0202(BasePlugin):
Arguments:
iq -- The Iq time request stanza.
"""
- iq.reply()
- iq['entity_time']['time'] = self.local_time(iq['to'])
- iq.send()
+ if iq['type'] == 'get':
+ iq.reply()
+ iq['entity_time']['time'] = self.local_time(iq['to'])
+ iq.send()
def get_entity_time(self, to, ifrom=None, **iqargs):
"""
diff --git a/sleekxmpp/plugins/xep_0257/stanza.py b/sleekxmpp/plugins/xep_0257/stanza.py
index 17e20136..c3c41db2 100644
--- a/sleekxmpp/plugins/xep_0257/stanza.py
+++ b/sleekxmpp/plugins/xep_0257/stanza.py
@@ -10,7 +10,7 @@ from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin
class Certs(ElementBase):
- name = 'query'
+ name = 'items'
namespace = 'urn:xmpp:saslcert:1'
plugin_attrib = 'sasl_certs'
interfaces = set()
diff --git a/sleekxmpp/plugins/xep_0323/device.py b/sleekxmpp/plugins/xep_0323/device.py
index 0bc20327..80e6fd95 100644
--- a/sleekxmpp/plugins/xep_0323/device.py
+++ b/sleekxmpp/plugins/xep_0323/device.py
@@ -13,15 +13,18 @@ import logging
class Device(object):
"""
- Example implementation of a device readout object.
+ Example implementation of a device readout object.
Is registered in the XEP_0323.register_node call
- The device object may be any custom implementation to support
+ The device object may be any custom implementation to support
specific devices, but it must implement the functions:
has_field
request_fields
"""
- def __init__(self, nodeId, fields={}):
+ def __init__(self, nodeId, fields=None):
+ if not fields:
+ fields = {}
+
self.nodeId = nodeId
self.fields = fields # see fields described below
# {'type':'numeric',
@@ -38,19 +41,19 @@ class Device(object):
Returns true if the supplied field name exists in this device.
Arguments:
- field -- The field name
+ field -- The field name
"""
if field in self.fields.keys():
- return True;
- return False;
-
+ return True
+ return False
+
def refresh(self, fields):
"""
override method to do the refresh work
refresh values from hardware or other
"""
pass
-
+
def request_fields(self, fields, flags, session, callback):
"""
@@ -65,7 +68,7 @@ class Device(object):
Formatted as a dictionary like { "flag name": "flag value" ... }
session -- Session id, only used in the callback as identifier
callback -- Callback function to call when data is available.
-
+
The callback function must support the following arguments:
session -- Session id, as supplied in the request_fields call
@@ -73,11 +76,11 @@ class Device(object):
result -- The current result status of the readout. Valid values are:
"error" - Readout failed.
"fields" - Contains readout data.
- "done" - Indicates that the readout is complete. May contain
+ "done" - Indicates that the readout is complete. May contain
readout data.
- timestamp_block -- [optional] Only applies when result != "error"
+ timestamp_block -- [optional] Only applies when result != "error"
The readout data. Structured as a dictionary:
- {
+ {
timestamp: timestamp for this datablock,
fields: list of field dictionary (one per readout field).
readout field dictionary format:
@@ -89,10 +92,10 @@ class Device(object):
dataType: The datatype of the field. Only applies to type enum.
flags: [optional] data classifier flags for the field, e.g. momentary
Formatted as a dictionary like { "flag name": "flag value" ... }
- }
+ }
}
error_msg -- [optional] Only applies when result == "error".
- Error details when a request failed.
+ Error details when a request failed.
"""
logging.debug("request_fields called looking for fields %s",fields)
@@ -101,10 +104,10 @@ class Device(object):
for f in fields:
if f not in self.fields.keys():
self._send_reject(session, callback)
- return False;
+ return False
else:
# Request all fields
- fields = self.fields.keys();
+ fields = self.fields.keys()
# Refresh data from device
@@ -114,27 +117,27 @@ class Device(object):
if "momentary" in flags and flags['momentary'] == "true" or \
"all" in flags and flags['all'] == "true":
- ts_block = {};
- timestamp = "";
+ ts_block = {}
+ timestamp = ""
if len(self.momentary_timestamp) > 0:
- timestamp = self.momentary_timestamp;
+ timestamp = self.momentary_timestamp
else:
- timestamp = self._get_timestamp();
+ timestamp = self._get_timestamp()
- field_block = [];
+ field_block = []
for f in self.momentary_data:
if f in fields:
- field_block.append({"name": f,
- "type": self.fields[f]["type"],
+ field_block.append({"name": f,
+ "type": self.fields[f]["type"],
"unit": self.fields[f]["unit"],
"dataType": self.fields[f]["dataType"],
- "value": self.momentary_data[f]["value"],
- "flags": self.momentary_data[f]["flags"]});
- ts_block["timestamp"] = timestamp;
- ts_block["fields"] = field_block;
+ "value": self.momentary_data[f]["value"],
+ "flags": self.momentary_data[f]["flags"]})
+ ts_block["timestamp"] = timestamp
+ ts_block["fields"] = field_block
- callback(session, result="done", nodeId=self.nodeId, timestamp_block=ts_block);
+ callback(session, result="done", nodeId=self.nodeId, timestamp_block=ts_block)
return
from_flag = self._datetime_flag_parser(flags, 'from')
@@ -142,36 +145,36 @@ class Device(object):
for ts in sorted(self.timestamp_data.keys()):
tsdt = datetime.datetime.strptime(ts, "%Y-%m-%dT%H:%M:%S")
- if not from_flag is None:
- if tsdt < from_flag:
+ if not from_flag is None:
+ if tsdt < from_flag:
#print (str(tsdt) + " < " + str(from_flag))
continue
- if not to_flag is None:
- if tsdt > to_flag:
+ if not to_flag is None:
+ if tsdt > to_flag:
#print (str(tsdt) + " > " + str(to_flag))
continue
-
- ts_block = {};
- field_block = [];
+
+ ts_block = {}
+ field_block = []
for f in self.timestamp_data[ts]:
if f in fields:
- field_block.append({"name": f,
- "type": self.fields[f]["type"],
+ field_block.append({"name": f,
+ "type": self.fields[f]["type"],
"unit": self.fields[f]["unit"],
"dataType": self.fields[f]["dataType"],
- "value": self.timestamp_data[ts][f]["value"],
- "flags": self.timestamp_data[ts][f]["flags"]});
+ "value": self.timestamp_data[ts][f]["value"],
+ "flags": self.timestamp_data[ts][f]["flags"]})
- ts_block["timestamp"] = ts;
- ts_block["fields"] = field_block;
- callback(session, result="fields", nodeId=self.nodeId, timestamp_block=ts_block);
- callback(session, result="done", nodeId=self.nodeId, timestamp_block=None);
+ ts_block["timestamp"] = ts
+ ts_block["fields"] = field_block
+ callback(session, result="fields", nodeId=self.nodeId, timestamp_block=ts_block)
+ callback(session, result="done", nodeId=self.nodeId, timestamp_block=None)
def _datetime_flag_parser(self, flags, flagname):
if not flagname in flags:
return None
-
+
dt = None
try:
dt = datetime.datetime.strptime(flags[flagname], "%Y-%m-%dT%H:%M:%S")
@@ -195,7 +198,7 @@ class Device(object):
session -- Session id, see definition in request_fields function
callback -- Callback function, see definition in request_fields function
"""
- callback(session, result="error", nodeId=self.nodeId, timestamp_block=None, error_msg="Reject");
+ callback(session, result="error", nodeId=self.nodeId, timestamp_block=None, error_msg="Reject")
def _add_field(self, name, typename, unit=None, dataType=None):
"""
@@ -207,7 +210,7 @@ class Device(object):
unit -- [optional] only applies to "numeric". Unit for the field.
dataType -- [optional] only applies to "enum". Datatype for the field.
"""
- self.fields[name] = {"type": typename, "unit": unit, "dataType": dataType};
+ self.fields[name] = {"type": typename, "unit": unit, "dataType": dataType}
def _add_field_timestamp_data(self, name, timestamp, value, flags=None):
"""
@@ -221,12 +224,12 @@ class Device(object):
Formatted as a dictionary like { "flag name": "flag value" ... }
"""
if not name in self.fields.keys():
- return False;
+ return False
if not timestamp in self.timestamp_data:
- self.timestamp_data[timestamp] = {};
+ self.timestamp_data[timestamp] = {}
- self.timestamp_data[timestamp][name] = {"value": value, "flags": flags};
- return True;
+ self.timestamp_data[timestamp][name] = {"value": value, "flags": flags}
+ return True
def _add_field_momentary_data(self, name, value, flags=None):
"""
@@ -239,17 +242,17 @@ class Device(object):
Formatted as a dictionary like { "flag name": "flag value" ... }
"""
if name not in self.fields:
- return False;
+ return False
if flags is None:
- flags = {};
-
+ flags = {}
+
flags["momentary"] = "true"
- self.momentary_data[name] = {"value": value, "flags": flags};
- return True;
+ self.momentary_data[name] = {"value": value, "flags": flags}
+ return True
def _set_momentary_timestamp(self, timestamp):
"""
This function is only for unit testing to produce predictable results.
"""
- self.momentary_timestamp = timestamp;
+ self.momentary_timestamp = timestamp
diff --git a/sleekxmpp/plugins/xep_0323/sensordata.py b/sleekxmpp/plugins/xep_0323/sensordata.py
index 2e2f2470..a3d4cf34 100644
--- a/sleekxmpp/plugins/xep_0323/sensordata.py
+++ b/sleekxmpp/plugins/xep_0323/sensordata.py
@@ -15,7 +15,6 @@ from threading import Thread, Lock, Timer
from sleekxmpp.plugins.xep_0323.timerreset import TimerReset
-from sleekxmpp.xmlstream import JID
from sleekxmpp.xmlstream.handler import Callback
from sleekxmpp.xmlstream.matcher import StanzaPath
from sleekxmpp.plugins.base import BasePlugin
@@ -29,12 +28,12 @@ log = logging.getLogger(__name__)
class XEP_0323(BasePlugin):
"""
- XEP-0323: IoT Sensor Data
+ XEP-0323: IoT Sensor Data
This XEP provides the underlying architecture, basic operations and data
structures for sensor data communication over XMPP networks. It includes
- a hardware abstraction model, removing any technical detail implemented
+ a hardware abstraction model, removing any technical detail implemented
in underlying technologies.
Also see <http://xmpp.org/extensions/xep-0323.html>
@@ -55,10 +54,10 @@ class XEP_0323(BasePlugin):
Sensordata Event:Rejected -- Received a reject from sensor for a request
Sensordata Event:Cancelled -- Received a cancel confirm from sensor
Sensordata Event:Fields -- Received fields from sensor for a request
- This may be triggered multiple times since
+ This may be triggered multiple times since
the sensor can split up its response in
multiple messages.
- Sensordata Event:Failure -- Received a failure indication from sensor
+ Sensordata Event:Failure -- Received a failure indication from sensor
for a request. Typically a comm timeout.
Attributes:
@@ -69,7 +68,7 @@ class XEP_0323(BasePlugin):
relevant to a request's session. This dictionary is used
both by the client and sensor side. On client side, seqnr
is used as key, while on sensor side, a session_id is used
- as key. This ensures that the two will not collide, so
+ as key. This ensures that the two will not collide, so
one instance can be both client and sensor.
Sensor side
-----------
@@ -89,12 +88,12 @@ class XEP_0323(BasePlugin):
Sensor side
-----------
- register_node -- Register a sensor as available from this XMPP
+ register_node -- Register a sensor as available from this XMPP
instance.
Client side
-----------
- request_data -- Initiates a request for data from one or more
+ request_data -- Initiates a request for data from one or more
sensors. Non-blocking, a callback function will
be called when data is available.
@@ -102,13 +101,12 @@ class XEP_0323(BasePlugin):
name = 'xep_0323'
description = 'XEP-0323 Internet of Things - Sensor Data'
- dependencies = set(['xep_0030'])
+ dependencies = set(['xep_0030'])
stanza = stanza
default_config = {
'threaded': True
-# 'session_db': None
}
def plugin_init(self):
@@ -155,17 +153,17 @@ class XEP_0323(BasePlugin):
self._handle_event_started))
# Server side dicts
- self.nodes = {};
- self.sessions = {};
+ self.nodes = {}
+ self.sessions = {}
- self.last_seqnr = 0;
- self.seqnr_lock = Lock();
+ self.last_seqnr = 0
+ self.seqnr_lock = Lock()
- ## For testning only
+ ## For testing only
self.test_authenticated_from = ""
def post_init(self):
- """ Init complete. Register our features in Serivce discovery. """
+ """ Init complete. Register our features in Service discovery. """
BasePlugin.post_init(self)
self.xmpp['xep_0030'].add_feature(Sensordata.namespace)
self.xmpp['xep_0030'].set_items(node=Sensordata.namespace, items=tuple())
@@ -182,7 +180,7 @@ class XEP_0323(BasePlugin):
def plugin_end(self):
""" Stop the XEP-0323 plugin """
- self.sessions.clear();
+ self.sessions.clear()
self.xmpp.remove_handler('Sensordata Event:Req')
self.xmpp.remove_handler('Sensordata Event:Accepted')
self.xmpp.remove_handler('Sensordata Event:Rejected')
@@ -198,9 +196,9 @@ class XEP_0323(BasePlugin):
def register_node(self, nodeId, device, commTimeout, sourceId=None, cacheType=None):
"""
Register a sensor/device as available for serving of data through this XMPP
- instance.
+ instance.
- The device object may by any custom implementation to support
+ The device object may by any custom implementation to support
specific devices, but it must implement the functions:
has_field
request_fields
@@ -212,25 +210,25 @@ class XEP_0323(BasePlugin):
commTimeout -- Time in seconds to wait between each callback from device during
a data readout. Float.
sourceId -- [optional] identifying the data source controlling the device
- cacheType -- [optional] narrowing down the search to a specific kind of node
+ cacheType -- [optional] narrowing down the search to a specific kind of node
"""
- self.nodes[nodeId] = {"device": device,
+ self.nodes[nodeId] = {"device": device,
"commTimeout": commTimeout,
- "sourceId": sourceId,
- "cacheType": cacheType};
+ "sourceId": sourceId,
+ "cacheType": cacheType}
def _set_authenticated(self, auth=''):
""" Internal testing function """
- self.test_authenticated_from = auth;
+ self.test_authenticated_from = auth
def _handle_event_req(self, iq):
"""
Event handler for reception of an Iq with req - this is a request.
- Verifies that
+ Verifies that
- all the requested nodes are available
- - at least one of the requested fields is available from at least
+ - at least one of the requested fields is available from at least
one of the nodes
If the request passes verification, an accept response is sent, and
@@ -238,42 +236,42 @@ class XEP_0323(BasePlugin):
If the verification fails, a reject message is sent.
"""
- seqnr = iq['req']['seqnr'];
- error_msg = '';
- req_ok = True;
+ seqnr = iq['req']['seqnr']
+ error_msg = ''
+ req_ok = True
# Authentication
if len(self.test_authenticated_from) > 0 and not iq['from'] == self.test_authenticated_from:
# Invalid authentication
- req_ok = False;
- error_msg = "Access denied";
+ req_ok = False
+ error_msg = "Access denied"
# Nodes
- process_nodes = [];
+ process_nodes = []
if len(iq['req']['nodes']) > 0:
for n in iq['req']['nodes']:
if not n['nodeId'] in self.nodes:
- req_ok = False;
- error_msg = "Invalid nodeId " + n['nodeId'];
- process_nodes = [n['nodeId'] for n in iq['req']['nodes']];
+ req_ok = False
+ error_msg = "Invalid nodeId " + n['nodeId']
+ process_nodes = [n['nodeId'] for n in iq['req']['nodes']]
else:
- process_nodes = self.nodes.keys();
+ process_nodes = self.nodes.keys()
# Fields - if we just find one we are happy, otherwise we reject
- process_fields = [];
+ process_fields = []
if len(iq['req']['fields']) > 0:
found = False
for f in iq['req']['fields']:
for node in self.nodes:
if self.nodes[node]["device"].has_field(f['name']):
- found = True;
- break;
+ found = True
+ break
if not found:
- req_ok = False;
- error_msg = "Invalid field " + f['name'];
- process_fields = [f['name'] for n in iq['req']['fields']];
+ req_ok = False
+ error_msg = "Invalid field " + f['name']
+ process_fields = [f['name'] for n in iq['req']['fields']]
- req_flags = iq['req']._get_flags();
+ req_flags = iq['req']._get_flags()
request_delay_sec = None
if 'when' in req_flags:
@@ -283,7 +281,7 @@ class XEP_0323(BasePlugin):
try:
dt = datetime.datetime.strptime(req_flags['when'], "%Y-%m-%dT%H:%M:%S")
except ValueError:
- req_ok = False;
+ req_ok = False
error_msg = "Invalid datetime in 'when' flag, please use ISO format (i.e. 2013-04-05T15:00:03)."
if not dt is None:
@@ -292,51 +290,47 @@ class XEP_0323(BasePlugin):
dtdiff = dt - dtnow
request_delay_sec = dtdiff.seconds + dtdiff.days * 24 * 3600
if request_delay_sec <= 0:
- req_ok = False;
- error_msg = "Invalid datetime in 'when' flag, cannot set a time in the past. Current time: " + dtnow.isoformat();
+ req_ok = False
+ error_msg = "Invalid datetime in 'when' flag, cannot set a time in the past. Current time: " + dtnow.isoformat()
if req_ok:
- session = self._new_session();
- self.sessions[session] = {"from": iq['from'], "to": iq['to'], "seqnr": seqnr};
- self.sessions[session]["commTimers"] = {};
- self.sessions[session]["nodeDone"] = {};
+ session = self._new_session()
+ self.sessions[session] = {"from": iq['from'], "to": iq['to'], "seqnr": seqnr}
+ self.sessions[session]["commTimers"] = {}
+ self.sessions[session]["nodeDone"] = {}
- #print("added session: " + str(self.sessions))
-
- iq.reply();
- iq['accepted']['seqnr'] = seqnr;
+ iq.reply()
+ iq['accepted']['seqnr'] = seqnr
if not request_delay_sec is None:
iq['accepted']['queued'] = "true"
- iq.send(block=False);
+ iq.send(block=False)
- self.sessions[session]["node_list"] = process_nodes;
+ self.sessions[session]["node_list"] = process_nodes
if not request_delay_sec is None:
# Delay request to requested time
timer = Timer(request_delay_sec, self._event_delayed_req, args=(session, process_fields, req_flags))
- self.sessions[session]["commTimers"]["delaytimer"] = timer;
- timer.start();
+ self.sessions[session]["commTimers"]["delaytimer"] = timer
+ timer.start()
return
if self.threaded:
- #print("starting thread")
tr_req = Thread(target=self._threaded_node_request, args=(session, process_fields, req_flags))
tr_req.start()
- #print("started thread")
else:
- self._threaded_node_request(session, process_fields, req_flags);
+ self._threaded_node_request(session, process_fields, req_flags)
else:
- iq.reply();
- iq['type'] = 'error';
- iq['rejected']['seqnr'] = seqnr;
- iq['rejected']['error'] = error_msg;
- iq.send(block=False);
+ iq.reply()
+ iq['type'] = 'error'
+ iq['rejected']['seqnr'] = seqnr
+ iq['rejected']['error'] = error_msg
+ iq.send(block=False)
def _threaded_node_request(self, session, process_fields, flags):
- """
+ """
Helper function to handle the device readouts in a separate thread.
-
+
Arguments:
session -- The request session id
process_fields -- The fields to request from the devices
@@ -344,41 +338,39 @@ class XEP_0323(BasePlugin):
Formatted as a dictionary like { "flag name": "flag value" ... }
"""
for node in self.sessions[session]["node_list"]:
- self.sessions[session]["nodeDone"][node] = False;
+ self.sessions[session]["nodeDone"][node] = False
for node in self.sessions[session]["node_list"]:
- timer = TimerReset(self.nodes[node]['commTimeout'], self._event_comm_timeout, args=(session, node));
- self.sessions[session]["commTimers"][node] = timer;
- #print("Starting timer " + str(timer) + ", timeout: " + str(self.nodes[node]['commTimeout']))
- timer.start();
- self.nodes[node]['device'].request_fields(process_fields, flags=flags, session=session, callback=self._device_field_request_callback);
+ timer = TimerReset(self.nodes[node]['commTimeout'], self._event_comm_timeout, args=(session, node))
+ self.sessions[session]["commTimers"][node] = timer
+ timer.start()
+ self.nodes[node]['device'].request_fields(process_fields, flags=flags, session=session, callback=self._device_field_request_callback)
def _event_comm_timeout(self, session, nodeId):
- """
+ """
Triggered if any of the readout operations timeout.
Sends a failure message back to the client, stops communicating
with the failing device.
-
+
Arguments:
session -- The request session id
nodeId -- The id of the device which timed out
"""
- msg = self.xmpp.Message();
- msg['from'] = self.sessions[session]['to'];
- msg['to'] = self.sessions[session]['from'];
- msg['failure']['seqnr'] = self.sessions[session]['seqnr'];
- msg['failure']['error']['text'] = "Timeout";
- msg['failure']['error']['nodeId'] = nodeId;
- msg['failure']['error']['timestamp'] = datetime.datetime.now().replace(microsecond=0).isoformat();
+ msg = self.xmpp.Message()
+ msg['from'] = self.sessions[session]['to']
+ msg['to'] = self.sessions[session]['from']
+ msg['failure']['seqnr'] = self.sessions[session]['seqnr']
+ msg['failure']['error']['text'] = "Timeout"
+ msg['failure']['error']['nodeId'] = nodeId
+ msg['failure']['error']['timestamp'] = datetime.datetime.now().replace(microsecond=0).isoformat()
# Drop communication with this device and check if we are done
- self.sessions[session]["nodeDone"][nodeId] = True;
+ self.sessions[session]["nodeDone"][nodeId] = True
if (self._all_nodes_done(session)):
- msg['failure']['done'] = 'true';
- msg.send();
+ msg['failure']['done'] = 'true'
+ msg.send()
# The session is complete, delete it
- #print("del session " + session + " due to timeout")
- del self.sessions[session];
+ del self.sessions[session]
def _event_delayed_req(self, session, process_fields, req_flags):
"""
@@ -390,47 +382,47 @@ class XEP_0323(BasePlugin):
flags -- [optional] flags to pass to the devices, e.g. momentary
Formatted as a dictionary like { "flag name": "flag value" ... }
"""
- msg = self.xmpp.Message();
- msg['from'] = self.sessions[session]['to'];
- msg['to'] = self.sessions[session]['from'];
- msg['started']['seqnr'] = self.sessions[session]['seqnr'];
- msg.send();
+ msg = self.xmpp.Message()
+ msg['from'] = self.sessions[session]['to']
+ msg['to'] = self.sessions[session]['from']
+ msg['started']['seqnr'] = self.sessions[session]['seqnr']
+ msg.send()
if self.threaded:
tr_req = Thread(target=self._threaded_node_request, args=(session, process_fields, req_flags))
tr_req.start()
else:
- self._threaded_node_request(session, process_fields, req_flags);
+ self._threaded_node_request(session, process_fields, req_flags)
def _all_nodes_done(self, session):
- """
- Checks wheter all devices are done replying to the readout.
-
+ """
+ Checks whether all devices are done replying to the readout.
+
Arguments:
session -- The request session id
"""
for n in self.sessions[session]["nodeDone"]:
if not self.sessions[session]["nodeDone"][n]:
- return False;
- return True;
+ return False
+ return True
def _device_field_request_callback(self, session, nodeId, result, timestamp_block, error_msg=None):
- """
+ """
Callback function called by the devices when they have any additional data.
- Composes a message with the data and sends it back to the client, and resets
+ Composes a message with the data and sends it back to the client, and resets
the timeout timer for the device.
-
+
Arguments:
session -- The request session id
nodeId -- The device id which initiated the callback
result -- The current result status of the readout. Valid values are:
"error" - Readout failed.
"fields" - Contains readout data.
- "done" - Indicates that the readout is complete. May contain
+ "done" - Indicates that the readout is complete. May contain
readout data.
- timestamp_block -- [optional] Only applies when result != "error"
+ timestamp_block -- [optional] Only applies when result != "error"
The readout data. Structured as a dictionary:
- {
+ {
timestamp: timestamp for this datablock,
fields: list of field dictionary (one per readout field).
readout field dictionary format:
@@ -442,109 +434,107 @@ class XEP_0323(BasePlugin):
dataType: The datatype of the field. Only applies to type enum.
flags: [optional] data classifier flags for the field, e.g. momentary
Formatted as a dictionary like { "flag name": "flag value" ... }
- }
+ }
}
error_msg -- [optional] Only applies when result == "error".
Error details when a request failed.
"""
if not session in self.sessions:
- # This can happend if a session was deleted, like in a cancellation. Just drop the data.
+ # This can happen if a session was deleted, like in a cancellation. Just drop the data.
return
if result == "error":
- self.sessions[session]["commTimers"][nodeId].cancel();
+ self.sessions[session]["commTimers"][nodeId].cancel()
- msg = self.xmpp.Message();
- msg['from'] = self.sessions[session]['to'];
- msg['to'] = self.sessions[session]['from'];
- msg['failure']['seqnr'] = self.sessions[session]['seqnr'];
- msg['failure']['error']['text'] = error_msg;
- msg['failure']['error']['nodeId'] = nodeId;
- msg['failure']['error']['timestamp'] = datetime.datetime.now().replace(microsecond=0).isoformat();
+ msg = self.xmpp.Message()
+ msg['from'] = self.sessions[session]['to']
+ msg['to'] = self.sessions[session]['from']
+ msg['failure']['seqnr'] = self.sessions[session]['seqnr']
+ msg['failure']['error']['text'] = error_msg
+ msg['failure']['error']['nodeId'] = nodeId
+ msg['failure']['error']['timestamp'] = datetime.datetime.now().replace(microsecond=0).isoformat()
# Drop communication with this device and check if we are done
- self.sessions[session]["nodeDone"][nodeId] = True;
+ self.sessions[session]["nodeDone"][nodeId] = True
if (self._all_nodes_done(session)):
- msg['failure']['done'] = 'true';
+ msg['failure']['done'] = 'true'
# The session is complete, delete it
- # print("del session " + session + " due to error")
- del self.sessions[session];
- msg.send();
+ del self.sessions[session]
+ msg.send()
else:
- msg = self.xmpp.Message();
- msg['from'] = self.sessions[session]['to'];
- msg['to'] = self.sessions[session]['from'];
- msg['fields']['seqnr'] = self.sessions[session]['seqnr'];
+ msg = self.xmpp.Message()
+ msg['from'] = self.sessions[session]['to']
+ msg['to'] = self.sessions[session]['from']
+ msg['fields']['seqnr'] = self.sessions[session]['seqnr']
if timestamp_block is not None and len(timestamp_block) > 0:
- node = msg['fields'].add_node(nodeId);
- ts = node.add_timestamp(timestamp_block["timestamp"]);
+ node = msg['fields'].add_node(nodeId)
+ ts = node.add_timestamp(timestamp_block["timestamp"])
for f in timestamp_block["fields"]:
- data = ts.add_data( typename=f['type'],
- name=f['name'],
- value=f['value'],
- unit=f['unit'],
- dataType=f['dataType'],
- flags=f['flags']);
+ data = ts.add_data( typename=f['type'],
+ name=f['name'],
+ value=f['value'],
+ unit=f['unit'],
+ dataType=f['dataType'],
+ flags=f['flags'])
if result == "done":
- self.sessions[session]["commTimers"][nodeId].cancel();
- self.sessions[session]["nodeDone"][nodeId] = True;
- msg['fields']['done'] = 'true';
+ self.sessions[session]["commTimers"][nodeId].cancel()
+ self.sessions[session]["nodeDone"][nodeId] = True
if (self._all_nodes_done(session)):
# The session is complete, delete it
- # print("del session " + session + " due to complete")
- del self.sessions[session];
+ del self.sessions[session]
+ msg['fields']['done'] = 'true'
else:
# Restart comm timer
- self.sessions[session]["commTimers"][nodeId].reset();
+ self.sessions[session]["commTimers"][nodeId].reset()
- msg.send();
+ msg.send()
def _handle_event_cancel(self, iq):
- """ Received Iq with cancel - this is a cancel request.
+ """ Received Iq with cancel - this is a cancel request.
Delete the session and confirm. """
- seqnr = iq['cancel']['seqnr'];
+ seqnr = iq['cancel']['seqnr']
# Find the session
for s in self.sessions:
if self.sessions[s]['from'] == iq['from'] and self.sessions[s]['to'] == iq['to'] and self.sessions[s]['seqnr'] == seqnr:
# found it. Cancel all timers
for n in self.sessions[s]["commTimers"]:
- self.sessions[s]["commTimers"][n].cancel();
+ self.sessions[s]["commTimers"][n].cancel()
# Confirm
- iq.reply();
- iq['type'] = 'result';
- iq['cancelled']['seqnr'] = seqnr;
- iq.send(block=False);
-
+ iq.reply()
+ iq['type'] = 'result'
+ iq['cancelled']['seqnr'] = seqnr
+ iq.send(block=False)
+
# Delete session
del self.sessions[s]
return
# Could not find session, send reject
- iq.reply();
- iq['type'] = 'error';
- iq['rejected']['seqnr'] = seqnr;
- iq['rejected']['error'] = "Cancel request received, no matching request is active.";
- iq.send(block=False);
+ iq.reply()
+ iq['type'] = 'error'
+ iq['rejected']['seqnr'] = seqnr
+ iq['rejected']['error'] = "Cancel request received, no matching request is active."
+ iq.send(block=False)
- # =================================================================
+ # =================================================================
# Client side (data retriever) API
def request_data(self, from_jid, to_jid, callback, nodeIds=None, fields=None, flags=None):
- """
- Called on the client side to initiade a data readout.
+ """
+ Called on the client side to initiate a data readout.
Composes a message with the request and sends it to the device(s).
Does not block, the callback will be called when data is available.
-
+
Arguments:
from_jid -- The jid of the requester
to_jid -- The jid of the device(s)
- callback -- The callback function to call when data is availble.
-
+ callback -- The callback function to call when data is available.
+
The callback function must support the following arguments:
from_jid -- The jid of the responding device(s)
@@ -565,7 +555,7 @@ class XEP_0323(BasePlugin):
The timestamp of data in this callback. One callback will only
contain data from one timestamp.
fields -- [optional] Mandatory when result == "fields".
- List of field dictionaries representing the readout data.
+ List of field dictionaries representing the readout data.
Dictionary format:
{
typename: The field type (numeric, boolean, dateTime, timeSpan, string, enum)
@@ -575,11 +565,11 @@ class XEP_0323(BasePlugin):
dataType: The datatype of the field. Only applies to type enum.
flags: [optional] data classifier flags for the field, e.g. momentary.
Formatted as a dictionary like { "flag name": "flag value" ... }
- }
+ }
error_msg -- [optional] Mandatory when result == "rejected" or "failure".
- Details about why the request is rejected or failed.
- "rejected" means that the request is stopped, but note that the
+ Details about why the request is rejected or failed.
+ "rejected" means that the request is stopped, but note that the
request will continue even after a "failure". "failure" only means
that communication was stopped to that specific device, other
device(s) (if any) will continue their readout.
@@ -593,131 +583,130 @@ class XEP_0323(BasePlugin):
session -- Session identifier. Client can use this as a reference to cancel
the request.
"""
- iq = self.xmpp.Iq();
- iq['from'] = from_jid;
- iq['to'] = to_jid;
- iq['type'] = "get";
- seqnr = self._get_new_seqnr();
- iq['id'] = seqnr;
- iq['req']['seqnr'] = seqnr;
+ iq = self.xmpp.Iq()
+ iq['from'] = from_jid
+ iq['to'] = to_jid
+ iq['type'] = "get"
+ seqnr = self._get_new_seqnr()
+ iq['id'] = seqnr
+ iq['req']['seqnr'] = seqnr
if nodeIds is not None:
for nodeId in nodeIds:
- iq['req'].add_node(nodeId);
+ iq['req'].add_node(nodeId)
if fields is not None:
for field in fields:
- iq['req'].add_field(field);
+ iq['req'].add_field(field)
- iq['req']._set_flags(flags);
+ iq['req']._set_flags(flags)
- self.sessions[seqnr] = {"from": iq['from'], "to": iq['to'], "seqnr": seqnr, "callback": callback};
- iq.send(block=False);
+ self.sessions[seqnr] = {"from": iq['from'], "to": iq['to'], "seqnr": seqnr, "callback": callback}
+ iq.send(block=False)
- return seqnr;
+ return seqnr
def cancel_request(self, session):
- """
+ """
Called on the client side to cancel a request for data readout.
Composes a message with the cancellation and sends it to the device(s).
- Does not block, the callback will be called when cancellation is
+ Does not block, the callback will be called when cancellation is
confirmed.
-
+
Arguments:
session -- The session id of the request to cancel
"""
seqnr = session
- iq = self.xmpp.Iq();
+ iq = self.xmpp.Iq()
iq['from'] = self.sessions[seqnr]['from']
- iq['to'] = self.sessions[seqnr]['to'];
- iq['type'] = "get";
- iq['id'] = seqnr;
- iq['cancel']['seqnr'] = seqnr;
- iq.send(block=False);
+ iq['to'] = self.sessions[seqnr]['to']
+ iq['type'] = "get"
+ iq['id'] = seqnr
+ iq['cancel']['seqnr'] = seqnr
+ iq.send(block=False)
def _get_new_seqnr(self):
""" Returns a unique sequence number (unique across threads) """
- self.seqnr_lock.acquire();
- self.last_seqnr = self.last_seqnr + 1;
- self.seqnr_lock.release();
- return str(self.last_seqnr);
+ self.seqnr_lock.acquire()
+ self.last_seqnr += 1
+ self.seqnr_lock.release()
+ return str(self.last_seqnr)
def _handle_event_accepted(self, iq):
""" Received Iq with accepted - request was accepted """
- seqnr = iq['accepted']['seqnr'];
+ seqnr = iq['accepted']['seqnr']
result = "accepted"
if iq['accepted']['queued'] == 'true':
result = "queued"
- callback = self.sessions[seqnr]["callback"];
- callback(from_jid=iq['from'], result=result);
+ callback = self.sessions[seqnr]["callback"]
+ callback(from_jid=iq['from'], result=result)
def _handle_event_rejected(self, iq):
- """ Received Iq with rejected - this is a reject.
+ """ Received Iq with rejected - this is a reject.
Delete the session. """
- seqnr = iq['rejected']['seqnr'];
- callback = self.sessions[seqnr]["callback"];
- callback(from_jid=iq['from'], result="rejected", error_msg=iq['rejected']['error']);
+ seqnr = iq['rejected']['seqnr']
+ callback = self.sessions[seqnr]["callback"]
+ callback(from_jid=iq['from'], result="rejected", error_msg=iq['rejected']['error'])
# Session terminated
- del self.sessions[seqnr];
+ del self.sessions[seqnr]
def _handle_event_cancelled(self, iq):
- """
- Received Iq with cancelled - this is a cancel confirm.
- Delete the session.
"""
- #print("Got cancelled")
- seqnr = iq['cancelled']['seqnr'];
- callback = self.sessions[seqnr]["callback"];
- callback(from_jid=iq['from'], result="cancelled");
+ Received Iq with cancelled - this is a cancel confirm.
+ Delete the session.
+ """
+ seqnr = iq['cancelled']['seqnr']
+ callback = self.sessions[seqnr]["callback"]
+ callback(from_jid=iq['from'], result="cancelled")
# Session cancelled
- del self.sessions[seqnr];
+ del self.sessions[seqnr]
def _handle_event_fields(self, msg):
- """
- Received Msg with fields - this is a data reponse to a request.
+ """
+ Received Msg with fields - this is a data response to a request.
If this is the last data block, issue a "done" callback.
"""
- seqnr = msg['fields']['seqnr'];
- callback = self.sessions[seqnr]["callback"];
+ seqnr = msg['fields']['seqnr']
+ callback = self.sessions[seqnr]["callback"]
for node in msg['fields']['nodes']:
for ts in node['timestamps']:
- fields = [];
+ fields = []
for d in ts['datas']:
- field_block = {};
- field_block["name"] = d['name'];
- field_block["typename"] = d._get_typename();
- field_block["value"] = d['value'];
+ field_block = {}
+ field_block["name"] = d['name']
+ field_block["typename"] = d._get_typename()
+ field_block["value"] = d['value']
if not d['unit'] == "": field_block["unit"] = d['unit'];
if not d['dataType'] == "": field_block["dataType"] = d['dataType'];
- flags = d._get_flags();
+ flags = d._get_flags()
if not len(flags) == 0:
- field_block["flags"] = flags;
- fields.append(field_block);
+ field_block["flags"] = flags
+ fields.append(field_block)
+
+ callback(from_jid=msg['from'], result="fields", nodeId=node['nodeId'], timestamp=ts['value'], fields=fields)
- callback(from_jid=msg['from'], result="fields", nodeId=node['nodeId'], timestamp=ts['value'], fields=fields);
-
if msg['fields']['done'] == "true":
- callback(from_jid=msg['from'], result="done");
+ callback(from_jid=msg['from'], result="done")
# Session done
- del self.sessions[seqnr];
+ del self.sessions[seqnr]
def _handle_event_failure(self, msg):
- """
+ """
Received Msg with failure - our request failed
- Delete the session.
+ Delete the session.
"""
- seqnr = msg['failure']['seqnr'];
- callback = self.sessions[seqnr]["callback"];
- callback(from_jid=msg['from'], result="failure", nodeId=msg['failure']['error']['nodeId'], timestamp=msg['failure']['error']['timestamp'], error_msg=msg['failure']['error']['text']);
+ seqnr = msg['failure']['seqnr']
+ callback = self.sessions[seqnr]["callback"]
+ callback(from_jid=msg['from'], result="failure", nodeId=msg['failure']['error']['nodeId'], timestamp=msg['failure']['error']['timestamp'], error_msg=msg['failure']['error']['text'])
# Session failed
- del self.sessions[seqnr];
+ del self.sessions[seqnr]
def _handle_event_started(self, msg):
- """
- Received Msg with started - our request was queued and is now started.
"""
- seqnr = msg['started']['seqnr'];
- callback = self.sessions[seqnr]["callback"];
- callback(from_jid=msg['from'], result="started");
-
+ Received Msg with started - our request was queued and is now started.
+ """
+ seqnr = msg['started']['seqnr']
+ callback = self.sessions[seqnr]["callback"]
+ callback(from_jid=msg['from'], result="started")
+
diff --git a/sleekxmpp/plugins/xep_0323/stanza/sensordata.py b/sleekxmpp/plugins/xep_0323/stanza/sensordata.py
index a11c3e94..e8718161 100644
--- a/sleekxmpp/plugins/xep_0323/stanza/sensordata.py
+++ b/sleekxmpp/plugins/xep_0323/stanza/sensordata.py
@@ -20,14 +20,14 @@ class Sensordata(ElementBase):
interfaces = set(tuple())
class FieldTypes():
- """
+ """
All field types are optional booleans that default to False
"""
field_types = set([ 'momentary','peak','status','computed','identity','historicalSecond','historicalMinute','historicalHour', \
'historicalDay','historicalWeek','historicalMonth','historicalQuarter','historicalYear','historicalOther'])
class FieldStatus():
- """
+ """
All field statuses are optional booleans that default to False
"""
field_status = set([ 'missing','automaticEstimate','manualEstimate','manualReadout','automaticReadout','timeOffset','warning','error', \
@@ -38,12 +38,12 @@ class Request(ElementBase):
name = 'req'
plugin_attrib = name
interfaces = set(['seqnr','nodes','fields','serviceToken','deviceToken','userToken','from','to','when','historical','all'])
- interfaces.update(FieldTypes.field_types);
- _flags = set(['serviceToken','deviceToken','userToken','from','to','when','historical','all']);
- _flags.update(FieldTypes.field_types);
-
+ interfaces.update(FieldTypes.field_types)
+ _flags = set(['serviceToken','deviceToken','userToken','from','to','when','historical','all'])
+ _flags.update(FieldTypes.field_types)
+
def __init__(self, xml=None, parent=None):
- ElementBase.__init__(self, xml, parent);
+ ElementBase.__init__(self, xml, parent)
self._nodes = set()
self._fields = set()
@@ -64,27 +64,27 @@ class Request(ElementBase):
def _get_flags(self):
"""
- Helper function for getting of flags. Returns all flags in
- dictionary format: { "flag name": "flag value" ... }
+ Helper function for getting of flags. Returns all flags in
+ dictionary format: { "flag name": "flag value" ... }
"""
- flags = {};
+ flags = {}
for f in self._flags:
if not self[f] == "":
- flags[f] = self[f];
- return flags;
+ flags[f] = self[f]
+ return flags
def _set_flags(self, flags):
"""
- Helper function for setting of flags.
+ Helper function for setting of flags.
Arguments:
- flags -- Flags in dictionary format: { "flag name": "flag value" ... }
+ flags -- Flags in dictionary format: { "flag name": "flag value" ... }
"""
for f in self._flags:
if flags is not None and f in flags:
- self[f] = flags[f];
+ self[f] = flags[f]
else:
- self[f] = None;
+ self[f] = None
def add_node(self, nodeId, sourceId=None, cacheType=None):
"""
@@ -94,7 +94,7 @@ class Request(ElementBase):
Arguments:
nodeId -- The ID for the node.
sourceId -- [optional] identifying the data source controlling the device
- cacheType -- [optional] narrowing down the search to a specific kind of node
+ cacheType -- [optional] narrowing down the search to a specific kind of node
"""
if nodeId not in self._nodes:
self._nodes.add((nodeId))
@@ -269,7 +269,7 @@ class Error(ElementBase):
:param value: string
"""
- self.xml.text = value;
+ self.xml.text = value
return self
def del_text(self):
@@ -292,7 +292,7 @@ class Fields(ElementBase):
interfaces = set(['seqnr','done','nodes'])
def __init__(self, xml=None, parent=None):
- ElementBase.__init__(self, xml, parent);
+ ElementBase.__init__(self, xml, parent)
self._nodes = set()
def setup(self, xml=None):
@@ -318,7 +318,7 @@ class Fields(ElementBase):
Arguments:
nodeId -- The ID for the node.
sourceId -- [optional] identifying the data source controlling the device
- cacheType -- [optional] narrowing down the search to a specific kind of node
+ cacheType -- [optional] narrowing down the search to a specific kind of node
"""
if nodeId not in self._nodes:
self._nodes.add((nodeId))
@@ -392,7 +392,7 @@ class FieldsNode(ElementBase):
interfaces = set(['nodeId','sourceId','cacheType','timestamps'])
def __init__(self, xml=None, parent=None):
- ElementBase.__init__(self, xml, parent);
+ ElementBase.__init__(self, xml, parent)
self._timestamps = set()
def setup(self, xml=None):
@@ -411,7 +411,7 @@ class FieldsNode(ElementBase):
def add_timestamp(self, timestamp, substanzas=None):
"""
- Add a new timestamp element.
+ Add a new timestamp element.
Arguments:
timestamp -- The timestamp in ISO format.
@@ -423,7 +423,7 @@ class FieldsNode(ElementBase):
ts = Timestamp(parent=self)
ts['value'] = timestamp
if not substanzas is None:
- ts.set_datas(substanzas);
+ ts.set_datas(substanzas)
#print("add_timestamp with substanzas: " + str(substanzas))
self.iterables.append(ts)
#print(str(id(self)) + " added_timestamp: " + str(id(ts)))
@@ -485,7 +485,7 @@ class FieldsNode(ElementBase):
self.iterables.remove(timestamp)
class Field(ElementBase):
- """
+ """
Field element in response Timestamp. This is a base class,
all instances of fields added to Timestamp must be of types:
DataNumeric
@@ -494,17 +494,17 @@ class Field(ElementBase):
DataDateTime
DataTimeSpan
DataEnum
- """
+ """
namespace = 'urn:xmpp:iot:sensordata'
name = 'field'
plugin_attrib = name
- interfaces = set(['name','module','stringIds']);
- interfaces.update(FieldTypes.field_types);
- interfaces.update(FieldStatus.field_status);
+ interfaces = set(['name','module','stringIds'])
+ interfaces.update(FieldTypes.field_types)
+ interfaces.update(FieldStatus.field_status)
- _flags = set();
- _flags.update(FieldTypes.field_types);
- _flags.update(FieldStatus.field_status);
+ _flags = set()
+ _flags.update(FieldTypes.field_types)
+ _flags.update(FieldStatus.field_status)
def set_stringIds(self, value):
"""Verifies stringIds according to regexp from specification XMPP-0323.
@@ -514,7 +514,7 @@ class Field(ElementBase):
pattern = re.compile("^\d+([|]\w+([.]\w+)*([|][^,]*)?)?(,\d+([|]\w+([.]\w+)*([|][^,]*)?)?)*$")
if pattern.match(value) is not None:
- self.xml.stringIds = value;
+ self.xml.stringIds = value
else:
# Bad content, add nothing
pass
@@ -523,30 +523,30 @@ class Field(ElementBase):
def _get_flags(self):
"""
- Helper function for getting of flags. Returns all flags in
- dictionary format: { "flag name": "flag value" ... }
+ Helper function for getting of flags. Returns all flags in
+ dictionary format: { "flag name": "flag value" ... }
"""
- flags = {};
+ flags = {}
for f in self._flags:
if not self[f] == "":
- flags[f] = self[f];
- return flags;
+ flags[f] = self[f]
+ return flags
def _set_flags(self, flags):
"""
- Helper function for setting of flags.
+ Helper function for setting of flags.
Arguments:
- flags -- Flags in dictionary format: { "flag name": "flag value" ... }
+ flags -- Flags in dictionary format: { "flag name": "flag value" ... }
"""
for f in self._flags:
if flags is not None and f in flags:
- self[f] = flags[f];
+ self[f] = flags[f]
else:
- self[f] = None;
+ self[f] = None
def _get_typename(self):
- return "invalid type, use subclasses!";
+ return "invalid type, use subclasses!"
class Timestamp(ElementBase):
@@ -557,7 +557,7 @@ class Timestamp(ElementBase):
interfaces = set(['value','datas'])
def __init__(self, xml=None, parent=None):
- ElementBase.__init__(self, xml, parent);
+ ElementBase.__init__(self, xml, parent)
self._datas = set()
def setup(self, xml=None):
@@ -576,7 +576,7 @@ class Timestamp(ElementBase):
def add_data(self, typename, name, value, module=None, stringIds=None, unit=None, dataType=None, flags=None):
"""
- Add a new data element.
+ Add a new data element.
Arguments:
typename -- The type of data element (numeric, string, boolean, dateTime, timeSpan or enum)
@@ -587,29 +587,29 @@ class Timestamp(ElementBase):
dataType -- [optional] The dataType. Only applicable for type enum
"""
if name not in self._datas:
- dataObj = None;
+ dataObj = None
if typename == "numeric":
- dataObj = DataNumeric(parent=self);
- dataObj['unit'] = unit;
+ dataObj = DataNumeric(parent=self)
+ dataObj['unit'] = unit
elif typename == "string":
- dataObj = DataString(parent=self);
+ dataObj = DataString(parent=self)
elif typename == "boolean":
- dataObj = DataBoolean(parent=self);
+ dataObj = DataBoolean(parent=self)
elif typename == "dateTime":
- dataObj = DataDateTime(parent=self);
+ dataObj = DataDateTime(parent=self)
elif typename == "timeSpan":
- dataObj = DataTimeSpan(parent=self);
+ dataObj = DataTimeSpan(parent=self)
elif typename == "enum":
- dataObj = DataEnum(parent=self);
- dataObj['dataType'] = dataType;
+ dataObj = DataEnum(parent=self)
+ dataObj['dataType'] = dataType
- dataObj['name'] = name;
- dataObj['value'] = value;
- dataObj['module'] = module;
- dataObj['stringIds'] = stringIds;
+ dataObj['name'] = name
+ dataObj['value'] = value
+ dataObj['module'] = module
+ dataObj['stringIds'] = stringIds
if flags is not None:
- dataObj._set_flags(flags);
+ dataObj._set_flags(flags)
self._datas.add(name)
self.iterables.append(dataObj)
@@ -661,87 +661,87 @@ class Timestamp(ElementBase):
self.iterables.remove(data)
class DataNumeric(Field):
- """
- Field data of type numeric.
- Note that the value is expressed as a string.
+ """
+ Field data of type numeric.
+ Note that the value is expressed as a string.
"""
namespace = 'urn:xmpp:iot:sensordata'
name = 'numeric'
plugin_attrib = name
- interfaces = set(['value', 'unit']);
- interfaces.update(Field.interfaces);
+ interfaces = set(['value', 'unit'])
+ interfaces.update(Field.interfaces)
def _get_typename(self):
- return "numeric"
+ return "numeric"
class DataString(Field):
- """
- Field data of type string
+ """
+ Field data of type string
"""
namespace = 'urn:xmpp:iot:sensordata'
name = 'string'
plugin_attrib = name
- interfaces = set(['value']);
- interfaces.update(Field.interfaces);
+ interfaces = set(['value'])
+ interfaces.update(Field.interfaces)
def _get_typename(self):
- return "string"
+ return "string"
class DataBoolean(Field):
- """
+ """
Field data of type boolean.
- Note that the value is expressed as a string.
+ Note that the value is expressed as a string.
"""
namespace = 'urn:xmpp:iot:sensordata'
name = 'boolean'
plugin_attrib = name
- interfaces = set(['value']);
- interfaces.update(Field.interfaces);
+ interfaces = set(['value'])
+ interfaces.update(Field.interfaces)
def _get_typename(self):
- return "boolean"
+ return "boolean"
class DataDateTime(Field):
- """
+ """
Field data of type dateTime.
- Note that the value is expressed as a string.
+ Note that the value is expressed as a string.
"""
namespace = 'urn:xmpp:iot:sensordata'
name = 'dateTime'
plugin_attrib = name
- interfaces = set(['value']);
- interfaces.update(Field.interfaces);
+ interfaces = set(['value'])
+ interfaces.update(Field.interfaces)
def _get_typename(self):
- return "dateTime"
+ return "dateTime"
class DataTimeSpan(Field):
- """
+ """
Field data of type timeSpan.
- Note that the value is expressed as a string.
+ Note that the value is expressed as a string.
"""
namespace = 'urn:xmpp:iot:sensordata'
name = 'timeSpan'
plugin_attrib = name
- interfaces = set(['value']);
- interfaces.update(Field.interfaces);
+ interfaces = set(['value'])
+ interfaces.update(Field.interfaces)
def _get_typename(self):
- return "timeSpan"
+ return "timeSpan"
class DataEnum(Field):
- """
+ """
Field data of type enum.
- Note that the value is expressed as a string.
+ Note that the value is expressed as a string.
"""
namespace = 'urn:xmpp:iot:sensordata'
name = 'enum'
plugin_attrib = name
- interfaces = set(['value', 'dataType']);
- interfaces.update(Field.interfaces);
+ interfaces = set(['value', 'dataType'])
+ interfaces.update(Field.interfaces)
def _get_typename(self):
- return "enum"
+ return "enum"
class Done(ElementBase):
""" Done element used to signal that all data has been transferred """
diff --git a/sleekxmpp/plugins/xep_0323/timerreset.py b/sleekxmpp/plugins/xep_0323/timerreset.py
index 578f1efe..398b47c1 100644
--- a/sleekxmpp/plugins/xep_0323/timerreset.py
+++ b/sleekxmpp/plugins/xep_0323/timerreset.py
@@ -23,7 +23,12 @@ class _TimerReset(Thread):
t.cancel() # stop the timer's action if it's still waiting
"""
- def __init__(self, interval, function, args=[], kwargs={}):
+ def __init__(self, interval, function, args=None, kwargs=None):
+ if not kwargs:
+ kwargs = {}
+ if not args:
+ args = []
+
Thread.__init__(self)
self.interval = interval
self.function = function
diff --git a/sleekxmpp/plugins/xep_0325/control.py b/sleekxmpp/plugins/xep_0325/control.py
index e34eb2c2..11e7a045 100644
--- a/sleekxmpp/plugins/xep_0325/control.py
+++ b/sleekxmpp/plugins/xep_0325/control.py
@@ -12,7 +12,6 @@ import logging
import time
from threading import Thread, Timer, Lock
-from sleekxmpp.xmlstream import JID
from sleekxmpp.xmlstream.handler import Callback
from sleekxmpp.xmlstream.matcher import StanzaPath
from sleekxmpp.plugins.base import BasePlugin
@@ -26,16 +25,16 @@ log = logging.getLogger(__name__)
class XEP_0325(BasePlugin):
"""
- XEP-0325: IoT Control
+ XEP-0325: IoT Control
- Actuators are devices in sensor networks that can be controlled through
- the network and act with the outside world. In sensor networks and
- Internet of Things applications, actuators make it possible to automate
- real-world processes.
- This plugin implements a mechanism whereby actuators can be controlled
- in XMPP-based sensor networks, making it possible to integrate sensors
- and actuators of different brands, makes and models into larger
+ Actuators are devices in sensor networks that can be controlled through
+ the network and act with the outside world. In sensor networks and
+ Internet of Things applications, actuators make it possible to automate
+ real-world processes.
+ This plugin implements a mechanism whereby actuators can be controlled
+ in XMPP-based sensor networks, making it possible to integrate sensors
+ and actuators of different brands, makes and models into larger
Internet of Things applications.
Also see <http://xmpp.org/extensions/xep-0325.html>
@@ -52,9 +51,9 @@ class XEP_0325(BasePlugin):
Client side
-----------
- Control Event:SetResponse -- Received a response to a
+ Control Event:SetResponse -- Received a response to a
control request, type result
- Control Event:SetResponseError -- Received a response to a
+ Control Event:SetResponseError -- Received a response to a
control request, type error
Attributes:
@@ -65,7 +64,7 @@ class XEP_0325(BasePlugin):
relevant to a request's session. This dictionary is used
both by the client and sensor side. On client side, seqnr
is used as key, while on sensor side, a session_id is used
- as key. This ensures that the two will not collide, so
+ as key. This ensures that the two will not collide, so
one instance can be both client and sensor.
Sensor side
-----------
@@ -85,15 +84,15 @@ class XEP_0325(BasePlugin):
Sensor side
-----------
- register_node -- Register a sensor as available from this XMPP
+ register_node -- Register a sensor as available from this XMPP
instance.
Client side
-----------
- set_request -- Initiates a control request to modify data in
+ set_request -- Initiates a control request to modify data in
sensor(s). Non-blocking, a callback function will
be called when the sensor has responded.
- set_command -- Initiates a control command to modify data in
+ set_command -- Initiates a control command to modify data in
sensor(s). Non-blocking. The sensor(s) will not
respond regardless of the result of the command,
so no callback is made.
@@ -102,7 +101,7 @@ class XEP_0325(BasePlugin):
name = 'xep_0325'
description = 'XEP-0325 Internet of Things - Control'
- dependencies = set(['xep_0030'])
+ dependencies = set(['xep_0030'])
stanza = stanza
@@ -135,11 +134,11 @@ class XEP_0325(BasePlugin):
self._handle_set_response))
# Server side dicts
- self.nodes = {};
- self.sessions = {};
+ self.nodes = {}
+ self.sessions = {}
- self.last_seqnr = 0;
- self.seqnr_lock = Lock();
+ self.last_seqnr = 0
+ self.seqnr_lock = Lock()
## For testning only
self.test_authenticated_from = ""
@@ -156,13 +155,13 @@ class XEP_0325(BasePlugin):
def plugin_end(self):
""" Stop the XEP-0325 plugin """
- self.sessions.clear();
+ self.sessions.clear()
self.xmpp.remove_handler('Control Event:DirectSet')
self.xmpp.remove_handler('Control Event:SetReq')
self.xmpp.remove_handler('Control Event:SetResponse')
self.xmpp.remove_handler('Control Event:SetResponseError')
self.xmpp['xep_0030'].del_feature(feature=Control.namespace)
- self.xmpp['xep_0030'].set_items(node=Control.namespace, items=tuple());
+ self.xmpp['xep_0030'].set_items(node=Control.namespace, items=tuple())
# =================================================================
@@ -170,10 +169,10 @@ class XEP_0325(BasePlugin):
def register_node(self, nodeId, device, commTimeout, sourceId=None, cacheType=None):
"""
- Register a sensor/device as available for control requests/commands
- through this XMPP instance.
+ Register a sensor/device as available for control requests/commands
+ through this XMPP instance.
- The device object may by any custom implementation to support
+ The device object may by any custom implementation to support
specific devices, but it must implement the functions:
has_control_field
set_control_fields
@@ -185,30 +184,30 @@ class XEP_0325(BasePlugin):
commTimeout -- Time in seconds to wait between each callback from device during
a data readout. Float.
sourceId -- [optional] identifying the data source controlling the device
- cacheType -- [optional] narrowing down the search to a specific kind of node
+ cacheType -- [optional] narrowing down the search to a specific kind of node
"""
- self.nodes[nodeId] = {"device": device,
+ self.nodes[nodeId] = {"device": device,
"commTimeout": commTimeout,
- "sourceId": sourceId,
- "cacheType": cacheType};
+ "sourceId": sourceId,
+ "cacheType": cacheType}
def _set_authenticated(self, auth=''):
""" Internal testing function """
- self.test_authenticated_from = auth;
+ self.test_authenticated_from = auth
def _get_new_seqnr(self):
""" Returns a unique sequence number (unique across threads) """
- self.seqnr_lock.acquire();
- self.last_seqnr = self.last_seqnr + 1;
- self.seqnr_lock.release();
- return str(self.last_seqnr);
+ self.seqnr_lock.acquire()
+ self.last_seqnr += 1
+ self.seqnr_lock.release()
+ return str(self.last_seqnr)
def _handle_set_req(self, iq):
"""
- Event handler for reception of an Iq with set req - this is a
+ Event handler for reception of an Iq with set req - this is a
control request.
- Verifies that
+ Verifies that
- all the requested nodes are available
(if no nodes are specified in the request, assume all nodes)
- all the control fields are available from all requested nodes
@@ -216,80 +215,79 @@ class XEP_0325(BasePlugin):
If the request passes verification, the control request is passed
to the devices (in a separate thread).
- If the verification fails, a setResponse with error indication
+ If the verification fails, a setResponse with error indication
is sent.
"""
- error_msg = '';
- req_ok = True;
- missing_node = None;
- missing_field = None;
+ error_msg = ''
+ req_ok = True
+ missing_node = None
+ missing_field = None
# Authentication
if len(self.test_authenticated_from) > 0 and not iq['from'] == self.test_authenticated_from:
# Invalid authentication
- req_ok = False;
- error_msg = "Access denied";
+ req_ok = False
+ error_msg = "Access denied"
# Nodes
- process_nodes = [];
if len(iq['set']['nodes']) > 0:
for n in iq['set']['nodes']:
if not n['nodeId'] in self.nodes:
- req_ok = False;
- missing_node = n['nodeId'];
- error_msg = "Invalid nodeId " + n['nodeId'];
- process_nodes = [n['nodeId'] for n in iq['set']['nodes']];
+ req_ok = False
+ missing_node = n['nodeId']
+ error_msg = "Invalid nodeId " + n['nodeId']
+ process_nodes = [n['nodeId'] for n in iq['set']['nodes']]
else:
- process_nodes = self.nodes.keys();
+ process_nodes = self.nodes.keys()
# Fields - for control we need to find all in all devices, otherwise we reject
- process_fields = [];
+ process_fields = []
if len(iq['set']['datas']) > 0:
for f in iq['set']['datas']:
for node in self.nodes:
if not self.nodes[node]["device"].has_control_field(f['name'], f._get_typename()):
- req_ok = False;
- missing_field = f['name'];
- error_msg = "Invalid field " + f['name'];
- break;
- process_fields = [(f['name'], f._get_typename(), f['value']) for f in iq['set']['datas']];
+ req_ok = False
+ missing_field = f['name']
+ error_msg = "Invalid field " + f['name']
+ break
+ process_fields = [(f['name'], f._get_typename(), f['value']) for f in iq['set']['datas']]
if req_ok:
- session = self._new_session();
- self.sessions[session] = {"from": iq['from'], "to": iq['to'], "seqnr": iq['id']};
- self.sessions[session]["commTimers"] = {};
- self.sessions[session]["nodeDone"] = {};
+ session = self._new_session()
+ self.sessions[session] = {"from": iq['from'], "to": iq['to'], "seqnr": iq['id']}
+ self.sessions[session]["commTimers"] = {}
+ self.sessions[session]["nodeDone"] = {}
# Flag that a reply is exected when we are done
- self.sessions[session]["reply"] = True;
+ self.sessions[session]["reply"] = True
- self.sessions[session]["node_list"] = process_nodes;
+ self.sessions[session]["node_list"] = process_nodes
if self.threaded:
#print("starting thread")
tr_req = Thread(target=self._threaded_node_request, args=(session, process_fields))
tr_req.start()
#print("started thread")
else:
- self._threaded_node_request(session, process_fields);
+ self._threaded_node_request(session, process_fields)
else:
- iq.reply();
- iq['type'] = 'error';
- iq['setResponse']['responseCode'] = "NotFound";
+ iq.reply()
+ iq['type'] = 'error'
+ iq['setResponse']['responseCode'] = "NotFound"
if missing_node is not None:
- iq['setResponse'].add_node(missing_node);
+ iq['setResponse'].add_node(missing_node)
if missing_field is not None:
- iq['setResponse'].add_data(missing_field);
- iq['setResponse']['error']['var'] = "Output";
- iq['setResponse']['error']['text'] = error_msg;
- iq.send(block=False);
+ iq['setResponse'].add_data(missing_field)
+ iq['setResponse']['error']['var'] = "Output"
+ iq['setResponse']['error']['text'] = error_msg
+ iq.send(block=False)
def _handle_direct_set(self, msg):
"""
- Event handler for reception of a Message with set command - this is a
+ Event handler for reception of a Message with set command - this is a
direct control command.
- Verifies that
+ Verifies that
- all the requested nodes are available
(if no nodes are specified in the request, assume all nodes)
- all the control fields are available from all requested nodes
@@ -299,73 +297,72 @@ class XEP_0325(BasePlugin):
to the devices (in a separate thread).
If the verification fails, do nothing.
"""
- req_ok = True;
+ req_ok = True
# Nodes
- process_nodes = [];
if len(msg['set']['nodes']) > 0:
for n in msg['set']['nodes']:
if not n['nodeId'] in self.nodes:
- req_ok = False;
- error_msg = "Invalid nodeId " + n['nodeId'];
- process_nodes = [n['nodeId'] for n in msg['set']['nodes']];
+ req_ok = False
+ error_msg = "Invalid nodeId " + n['nodeId']
+ process_nodes = [n['nodeId'] for n in msg['set']['nodes']]
else:
- process_nodes = self.nodes.keys();
+ process_nodes = self.nodes.keys()
# Fields - for control we need to find all in all devices, otherwise we reject
- process_fields = [];
+ process_fields = []
if len(msg['set']['datas']) > 0:
for f in msg['set']['datas']:
for node in self.nodes:
if not self.nodes[node]["device"].has_control_field(f['name'], f._get_typename()):
- req_ok = False;
- missing_field = f['name'];
- error_msg = "Invalid field " + f['name'];
- break;
- process_fields = [(f['name'], f._get_typename(), f['value']) for f in msg['set']['datas']];
+ req_ok = False
+ missing_field = f['name']
+ error_msg = "Invalid field " + f['name']
+ break
+ process_fields = [(f['name'], f._get_typename(), f['value']) for f in msg['set']['datas']]
if req_ok:
- session = self._new_session();
- self.sessions[session] = {"from": msg['from'], "to": msg['to']};
- self.sessions[session]["commTimers"] = {};
- self.sessions[session]["nodeDone"] = {};
- self.sessions[session]["reply"] = False;
+ session = self._new_session()
+ self.sessions[session] = {"from": msg['from'], "to": msg['to']}
+ self.sessions[session]["commTimers"] = {}
+ self.sessions[session]["nodeDone"] = {}
+ self.sessions[session]["reply"] = False
- self.sessions[session]["node_list"] = process_nodes;
+ self.sessions[session]["node_list"] = process_nodes
if self.threaded:
#print("starting thread")
tr_req = Thread(target=self._threaded_node_request, args=(session, process_fields))
tr_req.start()
#print("started thread")
else:
- self._threaded_node_request(session, process_fields);
+ self._threaded_node_request(session, process_fields)
def _threaded_node_request(self, session, process_fields):
- """
+ """
Helper function to handle the device control in a separate thread.
-
+
Arguments:
session -- The request session id
process_fields -- The fields to set in the devices. List of tuple format:
(name, datatype, value)
"""
for node in self.sessions[session]["node_list"]:
- self.sessions[session]["nodeDone"][node] = False;
+ self.sessions[session]["nodeDone"][node] = False
for node in self.sessions[session]["node_list"]:
- timer = Timer(self.nodes[node]['commTimeout'], self._event_comm_timeout, args=(session, node));
- self.sessions[session]["commTimers"][node] = timer;
- timer.start();
- self.nodes[node]['device'].set_control_fields(process_fields, session=session, callback=self._device_set_command_callback);
+ timer = Timer(self.nodes[node]['commTimeout'], self._event_comm_timeout, args=(session, node))
+ self.sessions[session]["commTimers"][node] = timer
+ timer.start()
+ self.nodes[node]['device'].set_control_fields(process_fields, session=session, callback=self._device_set_command_callback)
def _event_comm_timeout(self, session, nodeId):
- """
+ """
Triggered if any of the control operations timeout.
Stop communicating with the failing device.
- If the control command was an Iq request, sends a failure
- message back to the client.
-
+ If the control command was an Iq request, sends a failure
+ message back to the client.
+
Arguments:
session -- The request session id
nodeId -- The id of the device which timed out
@@ -373,51 +370,51 @@ class XEP_0325(BasePlugin):
if self.sessions[session]["reply"]:
# Reply is exected when we are done
- iq = self.xmpp.Iq();
- iq['from'] = self.sessions[session]['to'];
- iq['to'] = self.sessions[session]['from'];
- iq['type'] = "error";
- iq['id'] = self.sessions[session]['seqnr'];
- iq['setResponse']['responseCode'] = "OtherError";
- iq['setResponse'].add_node(nodeId);
- iq['setResponse']['error']['var'] = "Output";
- iq['setResponse']['error']['text'] = "Timeout.";
- iq.send(block=False);
+ iq = self.xmpp.Iq()
+ iq['from'] = self.sessions[session]['to']
+ iq['to'] = self.sessions[session]['from']
+ iq['type'] = "error"
+ iq['id'] = self.sessions[session]['seqnr']
+ iq['setResponse']['responseCode'] = "OtherError"
+ iq['setResponse'].add_node(nodeId)
+ iq['setResponse']['error']['var'] = "Output"
+ iq['setResponse']['error']['text'] = "Timeout."
+ iq.send(block=False)
## TODO - should we send one timeout per node??
# Drop communication with this device and check if we are done
- self.sessions[session]["nodeDone"][nodeId] = True;
+ self.sessions[session]["nodeDone"][nodeId] = True
if (self._all_nodes_done(session)):
# The session is complete, delete it
- del self.sessions[session];
+ del self.sessions[session]
def _all_nodes_done(self, session):
- """
+ """
Checks wheter all devices are done replying to the control command.
-
+
Arguments:
session -- The request session id
"""
for n in self.sessions[session]["nodeDone"]:
if not self.sessions[session]["nodeDone"][n]:
- return False;
- return True;
+ return False
+ return True
def _device_set_command_callback(self, session, nodeId, result, error_field=None, error_msg=None):
- """
- Callback function called by the devices when the control command is
+ """
+ Callback function called by the devices when the control command is
complete or failed.
- If needed, composes a message with the result and sends it back to the
+ If needed, composes a message with the result and sends it back to the
client.
-
+
Arguments:
session -- The request session id
nodeId -- The device id which initiated the callback
result -- The current result status of the control command. Valid values are:
"error" - Set fields failed.
"ok" - All fields were set.
- error_field -- [optional] Only applies when result == "error"
+ error_field -- [optional] Only applies when result == "error"
The field name that failed (usually means it is missing)
error_msg -- [optional] Only applies when result == "error".
Error details when a request failed.
@@ -428,62 +425,62 @@ class XEP_0325(BasePlugin):
return
if result == "error":
- self.sessions[session]["commTimers"][nodeId].cancel();
+ self.sessions[session]["commTimers"][nodeId].cancel()
if self.sessions[session]["reply"]:
# Reply is exected when we are done
- iq = self.xmpp.Iq();
- iq['from'] = self.sessions[session]['to'];
- iq['to'] = self.sessions[session]['from'];
- iq['type'] = "error";
- iq['id'] = self.sessions[session]['seqnr'];
- iq['setResponse']['responseCode'] = "OtherError";
- iq['setResponse'].add_node(nodeId);
+ iq = self.xmpp.Iq()
+ iq['from'] = self.sessions[session]['to']
+ iq['to'] = self.sessions[session]['from']
+ iq['type'] = "error"
+ iq['id'] = self.sessions[session]['seqnr']
+ iq['setResponse']['responseCode'] = "OtherError"
+ iq['setResponse'].add_node(nodeId)
if error_field is not None:
- iq['setResponse'].add_data(error_field);
- iq['setResponse']['error']['var'] = error_field;
- iq['setResponse']['error']['text'] = error_msg;
- iq.send(block=False);
+ iq['setResponse'].add_data(error_field)
+ iq['setResponse']['error']['var'] = error_field
+ iq['setResponse']['error']['text'] = error_msg
+ iq.send(block=False)
# Drop communication with this device and check if we are done
- self.sessions[session]["nodeDone"][nodeId] = True;
+ self.sessions[session]["nodeDone"][nodeId] = True
if (self._all_nodes_done(session)):
# The session is complete, delete it
- del self.sessions[session];
+ del self.sessions[session]
else:
- self.sessions[session]["commTimers"][nodeId].cancel();
+ self.sessions[session]["commTimers"][nodeId].cancel()
- self.sessions[session]["nodeDone"][nodeId] = True;
+ self.sessions[session]["nodeDone"][nodeId] = True
if (self._all_nodes_done(session)):
if self.sessions[session]["reply"]:
# Reply is exected when we are done
- iq = self.xmpp.Iq();
- iq['from'] = self.sessions[session]['to'];
- iq['to'] = self.sessions[session]['from'];
- iq['type'] = "result";
- iq['id'] = self.sessions[session]['seqnr'];
- iq['setResponse']['responseCode'] = "OK";
- iq.send(block=False);
+ iq = self.xmpp.Iq()
+ iq['from'] = self.sessions[session]['to']
+ iq['to'] = self.sessions[session]['from']
+ iq['type'] = "result"
+ iq['id'] = self.sessions[session]['seqnr']
+ iq['setResponse']['responseCode'] = "OK"
+ iq.send(block=False)
# The session is complete, delete it
- del self.sessions[session];
+ del self.sessions[session]
# =================================================================
# Client side (data controller) API
def set_request(self, from_jid, to_jid, callback, fields, nodeIds=None):
- """
+ """
Called on the client side to initiade a control request.
Composes a message with the request and sends it to the device(s).
- Does not block, the callback will be called when the device(s)
+ Does not block, the callback will be called when the device(s)
has responded.
-
+
Arguments:
from_jid -- The jid of the requester
to_jid -- The jid of the device(s)
- callback -- The callback function to call when data is availble.
-
+ callback -- The callback function to call when data is availble.
+
The callback function must support the following arguments:
from_jid -- The jid of the responding device(s)
@@ -494,46 +491,46 @@ class XEP_0325(BasePlugin):
"Locked" - Field(s) is locked and cannot
be changed at the moment.
"NotImplemented" - Request feature not implemented.
- "FormError" - Error while setting with
+ "FormError" - Error while setting with
a form (not implemented).
- "OtherError" - Indicates other types of
- errors, such as timeout.
+ "OtherError" - Indicates other types of
+ errors, such as timeout.
Details in the error_msg.
-
- nodeId -- [optional] Only applicable when result == "error"
- List of node Ids of failing device(s).
- fields -- [optional] Only applicable when result == "error"
+ nodeId -- [optional] Only applicable when result == "error"
+ List of node Ids of failing device(s).
+
+ fields -- [optional] Only applicable when result == "error"
List of fields that failed.[optional] Mandatory when result == "rejected" or "failure".
-
- error_msg -- Details about why the request failed.
+
+ error_msg -- Details about why the request failed.
fields -- Fields to set. List of tuple format: (name, typename, value).
nodeIds -- [optional] Limits the request to the node Ids in this list.
"""
- iq = self.xmpp.Iq();
- iq['from'] = from_jid;
- iq['to'] = to_jid;
- seqnr = self._get_new_seqnr();
- iq['id'] = seqnr;
- iq['type'] = "set";
+ iq = self.xmpp.Iq()
+ iq['from'] = from_jid
+ iq['to'] = to_jid
+ seqnr = self._get_new_seqnr()
+ iq['id'] = seqnr
+ iq['type'] = "set"
if nodeIds is not None:
for nodeId in nodeIds:
- iq['set'].add_node(nodeId);
+ iq['set'].add_node(nodeId)
if fields is not None:
for name, typename, value in fields:
- iq['set'].add_data(name=name, typename=typename, value=value);
+ iq['set'].add_data(name=name, typename=typename, value=value)
- self.sessions[seqnr] = {"from": iq['from'], "to": iq['to'], "callback": callback};
- iq.send(block=False);
+ self.sessions[seqnr] = {"from": iq['from'], "to": iq['to'], "callback": callback}
+ iq.send(block=False)
def set_command(self, from_jid, to_jid, fields, nodeIds=None):
- """
+ """
Called on the client side to initiade a control command.
Composes a message with the set commandand sends it to the device(s).
Does not block. Device(s) will not respond, regardless of result.
-
+
Arguments:
from_jid -- The jid of the requester
to_jid -- The jid of the device(s)
@@ -541,34 +538,32 @@ class XEP_0325(BasePlugin):
fields -- Fields to set. List of tuple format: (name, typename, value).
nodeIds -- [optional] Limits the request to the node Ids in this list.
"""
- msg = self.xmpp.Message();
- msg['from'] = from_jid;
- msg['to'] = to_jid;
- msg['type'] = "set";
+ msg = self.xmpp.Message()
+ msg['from'] = from_jid
+ msg['to'] = to_jid
+ msg['type'] = "set"
if nodeIds is not None:
for nodeId in nodeIds:
- msg['set'].add_node(nodeId);
+ msg['set'].add_node(nodeId)
if fields is not None:
for name, typename, value in fields:
- msg['set'].add_data(name, typename, value);
+ msg['set'].add_data(name, typename, value)
# We won't get any reply, so don't create a session
- msg.send();
+ msg.send()
def _handle_set_response(self, iq):
""" Received response from device(s) """
#print("ooh")
- seqnr = iq['id'];
- from_jid = str(iq['from']);
- result = iq['setResponse']['responseCode'];
- nodeIds = [n['name'] for n in iq['setResponse']['nodes']];
- fields = [f['name'] for f in iq['setResponse']['datas']];
- error_msg = None;
+ seqnr = iq['id']
+ from_jid = str(iq['from'])
+ result = iq['setResponse']['responseCode']
+ nodeIds = [n['name'] for n in iq['setResponse']['nodes']]
+ fields = [f['name'] for f in iq['setResponse']['datas']]
+ error_msg = None
if not iq['setResponse'].find('error') is None and not iq['setResponse']['error']['text'] == "":
- error_msg = iq['setResponse']['error']['text'];
-
- callback = self.sessions[seqnr]["callback"];
- callback(from_jid=from_jid, result=result, nodeIds=nodeIds, fields=fields, error_msg=error_msg);
+ error_msg = iq['setResponse']['error']['text']
-
+ callback = self.sessions[seqnr]["callback"]
+ callback(from_jid=from_jid, result=result, nodeIds=nodeIds, fields=fields, error_msg=error_msg)
diff --git a/sleekxmpp/plugins/xep_0325/device.py b/sleekxmpp/plugins/xep_0325/device.py
index a60d5f9a..f1ed0733 100644
--- a/sleekxmpp/plugins/xep_0325/device.py
+++ b/sleekxmpp/plugins/xep_0325/device.py
@@ -13,16 +13,16 @@ import datetime
class Device(object):
"""
Example implementation of a device control object.
-
- The device object may by any custom implementation to support
+
+ The device object may by any custom implementation to support
specific devices, but it must implement the functions:
has_control_field
set_control_fields
"""
def __init__(self, nodeId):
- self.nodeId = nodeId;
- self.control_fields = {};
+ self.nodeId = nodeId
+ self.control_fields = {}
def has_control_field(self, field, typename):
"""
@@ -30,12 +30,12 @@ class Device(object):
and the type matches for control in this device.
Arguments:
- field -- The field name
+ field -- The field name
typename -- The expected type
"""
if field in self.control_fields and self.control_fields[field]["type"] == typename:
- return True;
- return False;
+ return True
+ return False
def set_control_fields(self, fields, session, callback):
"""
@@ -43,22 +43,22 @@ class Device(object):
sets the data and (if needed) and calls the callback.
Arguments:
- fields -- List of control fields in tuple format:
+ fields -- List of control fields in tuple format:
(name, typename, value)
session -- Session id, only used in the callback as identifier
callback -- Callback function to call when control set is complete.
The callback function must support the following arguments:
- session -- Session id, as supplied in the
+ session -- Session id, as supplied in the
request_fields call
nodeId -- Identifier for this device
- result -- The current result status of the readout.
+ result -- The current result status of the readout.
Valid values are:
"error" - Set fields failed.
"ok" - All fields were set.
- error_field -- [optional] Only applies when result == "error"
- The field name that failed
+ error_field -- [optional] Only applies when result == "error"
+ The field name that failed
(usually means it is missing)
error_msg -- [optional] Only applies when result == "error".
Error details when a request failed.
@@ -69,12 +69,12 @@ class Device(object):
for name, typename, value in fields:
if not self.has_control_field(name, typename):
self._send_control_reject(session, name, "NotFound", callback)
- return False;
+ return False
for name, typename, value in fields:
self._set_field_value(name, value)
- callback(session, result="ok", nodeId=self.nodeId);
+ callback(session, result="ok", nodeId=self.nodeId)
return True
def _send_control_reject(self, session, field, message, callback):
@@ -82,12 +82,12 @@ class Device(object):
Sends a reject to the caller
Arguments:
- session -- Session id, see definition in
+ session -- Session id, see definition in
set_control_fields function
- callback -- Callback function, see definition in
+ callback -- Callback function, see definition in
set_control_fields function
"""
- callback(session, result="error", nodeId=self.nodeId, error_field=field, error_msg=message);
+ callback(session, result="error", nodeId=self.nodeId, error_field=field, error_msg=message)
def _add_control_field(self, name, typename, value):
"""
@@ -95,12 +95,12 @@ class Device(object):
Arguments:
name -- Name of the field
- typename -- Type of the field, one of:
- (boolean, color, string, date, dateTime,
+ typename -- Type of the field, one of:
+ (boolean, color, string, date, dateTime,
double, duration, int, long, time)
value -- Field value
"""
- self.control_fields[name] = {"type": typename, "value": value};
+ self.control_fields[name] = {"type": typename, "value": value}
def _set_field_value(self, name, value):
"""
@@ -111,7 +111,7 @@ class Device(object):
value -- New value for the field
"""
if name in self.control_fields:
- self.control_fields[name]["value"] = value;
+ self.control_fields[name]["value"] = value
def _get_field_value(self, name):
"""
@@ -121,5 +121,5 @@ class Device(object):
name -- Name of the field
"""
if name in self.control_fields:
- return self.control_fields[name]["value"];
- return None;
+ return self.control_fields[name]["value"]
+ return None
diff --git a/sleekxmpp/plugins/xep_0325/stanza/control.py b/sleekxmpp/plugins/xep_0325/stanza/control.py
index 67107ecb..1fd5c35d 100644
--- a/sleekxmpp/plugins/xep_0325/stanza/control.py
+++ b/sleekxmpp/plugins/xep_0325/stanza/control.py
@@ -26,7 +26,7 @@ class ControlSet(ElementBase):
interfaces = set(['nodes','datas'])
def __init__(self, xml=None, parent=None):
- ElementBase.__init__(self, xml, parent);
+ ElementBase.__init__(self, xml, parent)
self._nodes = set()
self._datas = set()
@@ -53,7 +53,7 @@ class ControlSet(ElementBase):
Arguments:
nodeId -- The ID for the node.
sourceId -- [optional] identifying the data source controlling the device
- cacheType -- [optional] narrowing down the search to a specific kind of node
+ cacheType -- [optional] narrowing down the search to a specific kind of node
"""
if nodeId not in self._nodes:
self._nodes.add((nodeId))
@@ -117,40 +117,40 @@ class ControlSet(ElementBase):
def add_data(self, name, typename, value):
"""
- Add a new data element.
+ Add a new data element.
Arguments:
name -- The name of the data element
- typename -- The type of data element
- (boolean, color, string, date, dateTime,
+ typename -- The type of data element
+ (boolean, color, string, date, dateTime,
double, duration, int, long, time)
value -- The value of the data element
"""
if name not in self._datas:
- dataObj = None;
+ dataObj = None
if typename == "boolean":
- dataObj = BooleanParameter(parent=self);
+ dataObj = BooleanParameter(parent=self)
elif typename == "color":
- dataObj = ColorParameter(parent=self);
+ dataObj = ColorParameter(parent=self)
elif typename == "string":
- dataObj = StringParameter(parent=self);
+ dataObj = StringParameter(parent=self)
elif typename == "date":
- dataObj = DateParameter(parent=self);
+ dataObj = DateParameter(parent=self)
elif typename == "dateTime":
- dataObj = DateTimeParameter(parent=self);
+ dataObj = DateTimeParameter(parent=self)
elif typename == "double":
- dataObj = DoubleParameter(parent=self);
+ dataObj = DoubleParameter(parent=self)
elif typename == "duration":
- dataObj = DurationParameter(parent=self);
+ dataObj = DurationParameter(parent=self)
elif typename == "int":
- dataObj = IntParameter(parent=self);
+ dataObj = IntParameter(parent=self)
elif typename == "long":
- dataObj = LongParameter(parent=self);
+ dataObj = LongParameter(parent=self)
elif typename == "time":
- dataObj = TimeParameter(parent=self);
+ dataObj = TimeParameter(parent=self)
- dataObj['name'] = name;
- dataObj['value'] = value;
+ dataObj['name'] = name
+ dataObj['value'] = value
self._datas.add(name)
self.iterables.append(dataObj)
@@ -217,7 +217,7 @@ class ControlSetResponse(ElementBase):
interfaces = set(['responseCode'])
def __init__(self, xml=None, parent=None):
- ElementBase.__init__(self, xml, parent);
+ ElementBase.__init__(self, xml, parent)
self._nodes = set()
self._datas = set()
@@ -244,7 +244,7 @@ class ControlSetResponse(ElementBase):
Arguments:
nodeId -- The ID for the node.
sourceId -- [optional] identifying the data source controlling the device
- cacheType -- [optional] narrowing down the search to a specific kind of node
+ cacheType -- [optional] narrowing down the search to a specific kind of node
"""
if nodeId not in self._nodes:
self._nodes.add(nodeId)
@@ -308,7 +308,7 @@ class ControlSetResponse(ElementBase):
def add_data(self, name):
"""
- Add a new ResponseParameter element.
+ Add a new ResponseParameter element.
Arguments:
name -- Name of the parameter
@@ -316,7 +316,7 @@ class ControlSetResponse(ElementBase):
if name not in self._datas:
self._datas.add(name)
data = ResponseParameter(parent=self)
- data['name'] = name;
+ data['name'] = name
self.iterables.append(data)
return data
return None
@@ -383,26 +383,26 @@ class Error(ElementBase):
value -- string
"""
- self.xml.text = value;
+ self.xml.text = value
return self
def del_text(self):
"""Remove the contents inside the XML tag."""
self.xml.text = ""
- return self
+ return self
class ResponseParameter(ElementBase):
- """
- Parameter element in ControlSetResponse.
- """
+ """
+ Parameter element in ControlSetResponse.
+ """
namespace = 'urn:xmpp:iot:control'
name = 'parameter'
plugin_attrib = name
- interfaces = set(['name']);
+ interfaces = set(['name'])
class BaseParameter(ElementBase):
- """
+ """
Parameter element in SetCommand. This is a base class,
all instances of parameters added to SetCommand must be of types:
BooleanParameter
@@ -415,90 +415,91 @@ class BaseParameter(ElementBase):
IntParameter
LongParameter
TimeParameter
- """
+ """
namespace = 'urn:xmpp:iot:control'
name = 'baseParameter'
plugin_attrib = name
- interfaces = set(['name','value']);
+ interfaces = set(['name','value'])
def _get_typename(self):
- return self.name;
+ return self.name
+
class BooleanParameter(BaseParameter):
- """
- Field data of type boolean.
- Note that the value is expressed as a string.
+ """
+ Field data of type boolean.
+ Note that the value is expressed as a string.
"""
name = 'boolean'
plugin_attrib = name
class ColorParameter(BaseParameter):
- """
- Field data of type color.
- Note that the value is expressed as a string.
+ """
+ Field data of type color.
+ Note that the value is expressed as a string.
"""
name = 'color'
plugin_attrib = name
class StringParameter(BaseParameter):
- """
- Field data of type string.
+ """
+ Field data of type string.
"""
name = 'string'
plugin_attrib = name
class DateParameter(BaseParameter):
- """
- Field data of type date.
- Note that the value is expressed as a string.
+ """
+ Field data of type date.
+ Note that the value is expressed as a string.
"""
name = 'date'
plugin_attrib = name
class DateTimeParameter(BaseParameter):
- """
- Field data of type dateTime.
- Note that the value is expressed as a string.
+ """
+ Field data of type dateTime.
+ Note that the value is expressed as a string.
"""
name = 'dateTime'
plugin_attrib = name
class DoubleParameter(BaseParameter):
- """
- Field data of type double.
- Note that the value is expressed as a string.
+ """
+ Field data of type double.
+ Note that the value is expressed as a string.
"""
name = 'double'
plugin_attrib = name
class DurationParameter(BaseParameter):
- """
- Field data of type duration.
- Note that the value is expressed as a string.
+ """
+ Field data of type duration.
+ Note that the value is expressed as a string.
"""
name = 'duration'
plugin_attrib = name
class IntParameter(BaseParameter):
- """
- Field data of type int.
- Note that the value is expressed as a string.
+ """
+ Field data of type int.
+ Note that the value is expressed as a string.
"""
name = 'int'
plugin_attrib = name
class LongParameter(BaseParameter):
- """
- Field data of type long (64-bit int).
- Note that the value is expressed as a string.
+ """
+ Field data of type long (64-bit int).
+ Note that the value is expressed as a string.
"""
name = 'long'
plugin_attrib = name
class TimeParameter(BaseParameter):
- """
- Field data of type time.
- Note that the value is expressed as a string.
+ """
+ Field data of type time.
+ Note that the value is expressed as a string.
"""
name = 'time'
plugin_attrib = name
diff --git a/sleekxmpp/plugins/xep_0332/__init__.py b/sleekxmpp/plugins/xep_0332/__init__.py
new file mode 100644
index 00000000..27755faa
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0332/__init__.py
@@ -0,0 +1,17 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Implementation of HTTP over XMPP transport
+ http://xmpp.org/extensions/xep-0332.html
+ Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.plugins.base import register_plugin
+
+from sleekxmpp.plugins.xep_0332 import stanza
+from sleekxmpp.plugins.xep_0332.http import XEP_0332
+
+
+register_plugin(XEP_0332)
diff --git a/sleekxmpp/plugins/xep_0332/http.py b/sleekxmpp/plugins/xep_0332/http.py
new file mode 100644
index 00000000..70bcafa6
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0332/http.py
@@ -0,0 +1,159 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Implementation of HTTP over XMPP transport
+ http://xmpp.org/extensions/xep-0332.html
+ Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+import logging
+
+from sleekxmpp import Iq
+
+from sleekxmpp.xmlstream import register_stanza_plugin
+from sleekxmpp.xmlstream.handler import Callback
+from sleekxmpp.xmlstream.matcher import StanzaPath
+
+from sleekxmpp.plugins.base import BasePlugin
+from sleekxmpp.plugins.xep_0332.stanza import (
+ HTTPRequest, HTTPResponse, HTTPData
+)
+from sleekxmpp.plugins.xep_0131.stanza import Headers
+
+
+log = logging.getLogger(__name__)
+
+
+class XEP_0332(BasePlugin):
+ """
+ XEP-0332: HTTP over XMPP transport
+ """
+
+ name = 'xep_0332'
+ description = 'XEP-0332: HTTP over XMPP transport'
+
+ #: xep_0047 not included.
+ #: xep_0001, 0137 and 0166 are missing
+ dependencies = set(['xep_0030', 'xep_0131'])
+
+ #: TODO: Do we really need to mention the supported_headers?!
+ default_config = {
+ 'supported_headers': set([
+ 'Content-Length', 'Transfer-Encoding', 'DateTime',
+ 'Accept-Charset', 'Location', 'Content-ID', 'Description',
+ 'Content-Language', 'Content-Transfer-Encoding', 'Timestamp',
+ 'Expires', 'User-Agent', 'Host', 'Proxy-Authorization', 'Date',
+ 'WWW-Authenticate', 'Accept-Encoding', 'Server', 'Error-Info',
+ 'Identifier', 'Content-Location', 'Content-Encoding', 'Distribute',
+ 'Accept', 'Proxy-Authenticate', 'ETag', 'Expect', 'Content-Type'
+ ])
+ }
+
+ def plugin_init(self):
+ self.xmpp.register_handler(
+ Callback(
+ 'HTTP Request',
+ StanzaPath('iq/http-req'),
+ self._handle_request
+ )
+ )
+ self.xmpp.register_handler(
+ Callback(
+ 'HTTP Response',
+ StanzaPath('iq/http-resp'),
+ self._handle_response
+ )
+ )
+ register_stanza_plugin(Iq, HTTPRequest, iterable=True)
+ register_stanza_plugin(Iq, HTTPResponse, iterable=True)
+ register_stanza_plugin(HTTPRequest, Headers, iterable=True)
+ register_stanza_plugin(HTTPRequest, HTTPData, iterable=True)
+ register_stanza_plugin(HTTPResponse, Headers, iterable=True)
+ register_stanza_plugin(HTTPResponse, HTTPData, iterable=True)
+ # TODO: Should we register any api's here? self.api.register()
+
+ def plugin_end(self):
+ self.xmpp.remove_handler('HTTP Request')
+ self.xmpp.remove_handler('HTTP Response')
+ self.xmpp['xep_0030'].del_feature('urn:xmpp:http')
+ for header in self.supported_headers:
+ self.xmpp['xep_0030'].del_feature(
+ feature='%s#%s' % (Headers.namespace, header)
+ )
+
+ def session_bind(self, jid):
+ self.xmpp['xep_0030'].add_feature('urn:xmpp:http')
+ for header in self.supported_headers:
+ self.xmpp['xep_0030'].add_feature(
+ '%s#%s' % (Headers.namespace, header)
+ )
+ # TODO: Do we need to add the supported headers to xep_0131?
+ # self.xmpp['xep_0131'].supported_headers.add(header)
+
+ def _handle_request(self, iq):
+ self.xmpp.event('http_request', iq)
+
+ def _handle_response(self, iq):
+ self.xmpp.event('http_response', iq)
+
+ def send_request(self, to=None, method=None, resource=None, headers=None,
+ data=None, **kwargs):
+ iq = self.xmpp.Iq()
+ iq['from'] = self.xmpp.boundjid
+ iq['to'] = to
+ iq['type'] = 'set'
+ iq['http-req']['headers'] = headers
+ iq['http-req']['method'] = method
+ iq['http-req']['resource'] = resource
+ iq['http-req']['version'] = '1.1' # TODO: set this implicitly
+ if 'id' in kwargs:
+ iq['id'] = kwargs["id"]
+ if data is not None:
+ iq['http-req']['data'] = data
+ return iq.send(
+ timeout=kwargs.get('timeout', None),
+ block=kwargs.get('block', True),
+ callback=kwargs.get('callback', None),
+ timeout_callback=kwargs.get('timeout_callback', None)
+ )
+
+ def send_response(self, to=None, code=None, message=None, headers=None,
+ data=None, **kwargs):
+ iq = self.xmpp.Iq()
+ iq['from'] = self.xmpp.boundjid
+ iq['to'] = to
+ iq['type'] = 'result'
+ iq['http-resp']['headers'] = headers
+ iq['http-resp']['code'] = code
+ iq['http-resp']['message'] = message
+ iq['http-resp']['version'] = '1.1' # TODO: set this implicitly
+ if 'id' in kwargs:
+ iq['id'] = kwargs["id"]
+ if data is not None:
+ iq['http-resp']['data'] = data
+ return iq.send(
+ timeout=kwargs.get('timeout', None),
+ block=kwargs.get('block', True),
+ callback=kwargs.get('callback', None),
+ timeout_callback=kwargs.get('timeout_callback', None)
+ )
+
+ def send_error(self, to=None, ecode='500', etype='wait',
+ econd='internal-server-error', **kwargs):
+ iq = self.xmpp.Iq()
+ iq['from'] = self.xmpp.boundjid
+ iq['to'] = to
+ iq['type'] = 'error'
+ iq['error']['code'] = ecode
+ iq['error']['type'] = etype
+ iq['error']['condition'] = econd
+ if 'id' in kwargs:
+ iq['id'] = kwargs["id"]
+ return iq.send(
+ timeout=kwargs.get('timeout', None),
+ block=kwargs.get('block', True),
+ callback=kwargs.get('callback', None),
+ timeout_callback=kwargs.get('timeout_callback', None)
+ )
diff --git a/sleekxmpp/plugins/xep_0332/stanza/__init__.py b/sleekxmpp/plugins/xep_0332/stanza/__init__.py
new file mode 100644
index 00000000..201824b7
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0332/stanza/__init__.py
@@ -0,0 +1,13 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Implementation of HTTP over XMPP transport
+ http://xmpp.org/extensions/xep-0332.html
+ Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.plugins.xep_0332.stanza.request import HTTPRequest
+from sleekxmpp.plugins.xep_0332.stanza.response import HTTPResponse
+from sleekxmpp.plugins.xep_0332.stanza.data import HTTPData
diff --git a/sleekxmpp/plugins/xep_0332/stanza/data.py b/sleekxmpp/plugins/xep_0332/stanza/data.py
new file mode 100644
index 00000000..a3678038
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0332/stanza/data.py
@@ -0,0 +1,30 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Implementation of HTTP over XMPP transport
+ http://xmpp.org/extensions/xep-0332.html
+ Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.xmlstream import ElementBase
+
+
+class HTTPData(ElementBase):
+ """
+ The data element.
+ """
+ name = 'data'
+ namespace = 'urn:xmpp:http'
+ interfaces = set(['data'])
+ plugin_attrib = 'data'
+ is_extension = True
+
+ def get_data(self, encoding='text'):
+ data = self._get_sub_text(encoding, None)
+ return str(data) if data is not None else data
+
+ def set_data(self, data, encoding='text'):
+ self._set_sub_text(encoding, text=data)
+
diff --git a/sleekxmpp/plugins/xep_0332/stanza/request.py b/sleekxmpp/plugins/xep_0332/stanza/request.py
new file mode 100644
index 00000000..9a298e57
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0332/stanza/request.py
@@ -0,0 +1,71 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Implementation of HTTP over XMPP transport
+ http://xmpp.org/extensions/xep-0332.html
+ Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.xmlstream import ElementBase
+
+
+class HTTPRequest(ElementBase):
+
+ """
+ All HTTP communication is done using the `Request`/`Response` paradigm.
+ Each HTTP Request is made sending an `iq` stanza containing a `req`
+ element to the server. Each `iq` stanza sent is of type `set`.
+
+ Examples:
+ <iq type='set' from='a@b.com/browser' to='x@y.com' id='1'>
+ <req xmlns='urn:xmpp:http'
+ method='GET'
+ resource='/api/users'
+ version='1.1'>
+ <headers xmlns='http://jabber.org/protocol/shim'>
+ <header name='Host'>b.com</header>
+ </headers>
+ </req>
+ </iq>
+
+ <iq type='set' from='a@b.com/browser' to='x@y.com' id='2'>
+ <req xmlns='urn:xmpp:http'
+ method='PUT'
+ resource='/api/users'
+ version='1.1'>
+ <headers xmlns='http://jabber.org/protocol/shim'>
+ <header name='Host'>b.com</header>
+ <header name='Content-Type'>text/html</header>
+ <header name='Content-Length'>...</header>
+ </headers>
+ <data>
+ <text>...</text>
+ </data>
+ </req>
+ </iq>
+ """
+
+ name = 'request'
+ namespace = 'urn:xmpp:http'
+ interfaces = set(['method', 'resource', 'version'])
+ plugin_attrib = 'http-req'
+
+ def get_method(self):
+ return self._get_attr('method', None)
+
+ def set_method(self, method):
+ self._set_attr('method', method)
+
+ def get_resource(self):
+ return self._get_attr('resource', None)
+
+ def set_resource(self, resource):
+ self._set_attr('resource', resource)
+
+ def get_version(self):
+ return self._get_attr('version', None)
+
+ def set_version(self, version='1.1'):
+ self._set_attr('version', version)
diff --git a/sleekxmpp/plugins/xep_0332/stanza/response.py b/sleekxmpp/plugins/xep_0332/stanza/response.py
new file mode 100644
index 00000000..6804ade9
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0332/stanza/response.py
@@ -0,0 +1,66 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Implementation of HTTP over XMPP transport
+ http://xmpp.org/extensions/xep-0332.html
+ Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.xmlstream import ElementBase
+
+
+class HTTPResponse(ElementBase):
+
+ """
+ When the HTTP Server responds, it does so by sending an `iq` stanza
+ response (type=`result`) back to the client containing the `resp` element.
+ Since response are asynchronous, and since multiple requests may be active
+ at the same time, responses may be returned in a different order than the
+ in which the original requests were made.
+
+ Examples:
+ <iq type='result'
+ from='httpserver@clayster.com'
+ to='httpclient@clayster.com/browser' id='2'>
+ <resp xmlns='urn:xmpp:http'
+ version='1.1'
+ statusCode='200'
+ statusMessage='OK'>
+ <headers xmlns='http://jabber.org/protocol/shim'>
+ <header name='Date'>Fri, 03 May 2013 16:39:54GMT-4</header>
+ <header name='Server'>Clayster</header>
+ <header name='Content-Type'>text/turtle</header>
+ <header name='Content-Length'>...</header>
+ <header name='Connection'>Close</header>
+ </headers>
+ <data>
+ <text>
+ ...
+ </text>
+ </data>
+ </resp>
+ </iq>
+ """
+
+ name = 'response'
+ namespace = 'urn:xmpp:http'
+ interfaces = set(['code', 'message', 'version'])
+ plugin_attrib = 'http-resp'
+
+ def get_code(self):
+ code = self._get_attr('statusCode', None)
+ return int(code) if code is not None else code
+
+ def set_code(self, code):
+ self._set_attr('statusCode', str(code))
+
+ def get_message(self):
+ return self._get_attr('statusMessage', '')
+
+ def set_message(self, message):
+ self._set_attr('statusMessage', message)
+
+ def set_version(self, version='1.1'):
+ self._set_attr('version', version)
diff --git a/sleekxmpp/roster/single.py b/sleekxmpp/roster/single.py
index f080ae8a..e9ce4f21 100644
--- a/sleekxmpp/roster/single.py
+++ b/sleekxmpp/roster/single.py
@@ -237,8 +237,7 @@ class RosterNode(object):
if not self.xmpp.is_component:
return self.update(jid, subscription='remove')
- def update(self, jid, name=None, subscription=None, groups=[],
- block=True, timeout=None, callback=None):
+ def update(self, jid, name=None, subscription=None, groups=None, block=True, timeout=None, callback=None):
"""
Update a JID's subscription information.
@@ -258,6 +257,9 @@ class RosterNode(object):
Will be executed when the roster is received.
Implies block=False.
"""
+ if not groups:
+ groups = []
+
self[jid]['name'] = name
self[jid]['groups'] = groups
self[jid].save()
diff --git a/sleekxmpp/stanza/atom.py b/sleekxmpp/stanza/atom.py
index 244ef315..4e9591a5 100644
--- a/sleekxmpp/stanza/atom.py
+++ b/sleekxmpp/stanza/atom.py
@@ -6,8 +6,7 @@
See the file LICENSE for copying permission.
"""
-from sleekxmpp.xmlstream import ElementBase
-
+from sleekxmpp.xmlstream import register_stanza_plugin, ElementBase
class AtomEntry(ElementBase):
@@ -22,5 +21,23 @@ class AtomEntry(ElementBase):
namespace = 'http://www.w3.org/2005/Atom'
name = 'entry'
plugin_attrib = 'entry'
- interfaces = set(('title', 'summary'))
- sub_interfaces = set(('title', 'summary'))
+ interfaces = set(('title', 'summary', 'id', 'published', 'updated'))
+ sub_interfaces = set(('title', 'summary', 'id', 'published',
+ 'updated'))
+
+class AtomAuthor(ElementBase):
+
+ """
+ An Atom author.
+
+ Stanza Interface:
+ name -- The printable author name
+ uri -- The bare jid of the author
+ """
+
+ name = 'author'
+ plugin_attrib = 'author'
+ interfaces = set(('name', 'uri'))
+ sub_interfaces = set(('name', 'uri'))
+
+register_stanza_plugin(AtomEntry, AtomAuthor)
diff --git a/sleekxmpp/stanza/rootstanza.py b/sleekxmpp/stanza/rootstanza.py
index a7c2b218..52b807e5 100644
--- a/sleekxmpp/stanza/rootstanza.py
+++ b/sleekxmpp/stanza/rootstanza.py
@@ -60,7 +60,9 @@ class RootStanza(StanzaBase):
self.send()
elif isinstance(e, XMPPError):
# We raised this deliberately
+ keep_id = self['id']
self.reply(clear=e.clear)
+ self['id'] = keep_id
self['error']['condition'] = e.condition
self['error']['text'] = e.text
self['error']['type'] = e.etype
@@ -72,7 +74,9 @@ class RootStanza(StanzaBase):
self.send()
else:
# We probably didn't raise this on purpose, so send an error stanza
+ keep_id = self['id']
self.reply()
+ self['id'] = keep_id
self['error']['condition'] = 'undefined-condition'
self['error']['text'] = "SleekXMPP got into trouble."
self['error']['type'] = 'cancel'
diff --git a/sleekxmpp/test/sleektest.py b/sleekxmpp/test/sleektest.py
index d28f77e2..e26f99ce 100644
--- a/sleekxmpp/test/sleektest.py
+++ b/sleekxmpp/test/sleektest.py
@@ -288,11 +288,8 @@ class SleekTest(unittest.TestCase):
if self.xmpp:
self.xmpp.socket.disconnect_error()
- def stream_start(self, mode='client', skip=True, header=None,
- socket='mock', jid='tester@localhost',
- password='test', server='localhost',
- port=5222, sasl_mech=None,
- plugins=None, plugin_config={}):
+ def stream_start(self, mode='client', skip=True, header=None, socket='mock', jid='tester@localhost',
+ password='test', server='localhost', port=5222, sasl_mech=None, plugins=None, plugin_config=None):
"""
Initialize an XMPP client or component using a dummy XML stream.
@@ -315,6 +312,9 @@ class SleekTest(unittest.TestCase):
plugins -- List of plugins to register. By default, all plugins
are loaded.
"""
+ if not plugin_config:
+ plugin_config = {}
+
if mode == 'client':
self.xmpp = ClientXMPP(jid, password,
sasl_mech=sasl_mech,
@@ -425,8 +425,7 @@ class SleekTest(unittest.TestCase):
parts.append('xmlns="%s"' % default_ns)
return header % ' '.join(parts)
- def recv(self, data, defaults=[], method='exact',
- use_values=True, timeout=1):
+ def recv(self, data, defaults=None, method='exact', use_values=True, timeout=1):
"""
Pass data to the dummy XMPP client as if it came from an XMPP server.
@@ -447,6 +446,9 @@ class SleekTest(unittest.TestCase):
timeout -- Time to wait in seconds for data to be received by
a live connection.
"""
+ if not defaults:
+ defaults = []
+
if self.xmpp.socket.is_live:
# we are working with a live connection, so we should
# verify what has been received instead of simulating
diff --git a/sleekxmpp/thirdparty/__init__.py b/sleekxmpp/thirdparty/__init__.py
index 2a1db717..337598ac 100644
--- a/sleekxmpp/thirdparty/__init__.py
+++ b/sleekxmpp/thirdparty/__init__.py
@@ -10,3 +10,4 @@ except:
from sleekxmpp.thirdparty import socks
from sleekxmpp.thirdparty.mini_dateutil import tzutc, tzoffset, parse_iso
+from sleekxmpp.thirdparty.orderedset import OrderedSet
diff --git a/sleekxmpp/thirdparty/orderedset.py b/sleekxmpp/thirdparty/orderedset.py
new file mode 100644
index 00000000..f6642db3
--- /dev/null
+++ b/sleekxmpp/thirdparty/orderedset.py
@@ -0,0 +1,89 @@
+# Copyright (c) 2009 Raymond Hettinger
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+import collections
+
+class OrderedSet(collections.MutableSet):
+
+ def __init__(self, iterable=None):
+ self.end = end = []
+ end += [None, end, end] # sentinel node for doubly linked list
+ self.map = {} # key --> [key, prev, next]
+ if iterable is not None:
+ self |= iterable
+
+ def __len__(self):
+ return len(self.map)
+
+ def __contains__(self, key):
+ return key in self.map
+
+ def add(self, key):
+ if key not in self.map:
+ end = self.end
+ curr = end[1]
+ curr[2] = end[1] = self.map[key] = [key, curr, end]
+
+ def discard(self, key):
+ if key in self.map:
+ key, prev, next = self.map.pop(key)
+ prev[2] = next
+ next[1] = prev
+
+ def __iter__(self):
+ end = self.end
+ curr = end[2]
+ while curr is not end:
+ yield curr[0]
+ curr = curr[2]
+
+ def __reversed__(self):
+ end = self.end
+ curr = end[1]
+ while curr is not end:
+ yield curr[0]
+ curr = curr[1]
+
+ def pop(self, last=True):
+ if not self:
+ raise KeyError('set is empty')
+ key = self.end[1][0] if last else self.end[2][0]
+ self.discard(key)
+ return key
+
+ def __repr__(self):
+ if not self:
+ return '%s()' % (self.__class__.__name__,)
+ return '%s(%r)' % (self.__class__.__name__, list(self))
+
+ def __eq__(self, other):
+ if isinstance(other, OrderedSet):
+ return len(self) == len(other) and list(self) == list(other)
+ return set(self) == set(other)
+
+
+if __name__ == '__main__':
+ s = OrderedSet('abracadaba')
+ t = OrderedSet('simsalabim')
+ print(s | t)
+ print(s & t)
+ print(s - t) \ No newline at end of file
diff --git a/sleekxmpp/thirdparty/socks.py b/sleekxmpp/thirdparty/socks.py
index 9239a7b9..34090d51 100644
--- a/sleekxmpp/thirdparty/socks.py
+++ b/sleekxmpp/thirdparty/socks.py
@@ -28,6 +28,9 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
This module provides a standard socket-like interface for Python
for tunneling connections through SOCKS proxies.
+"""
+
+"""
Minor modifications made by Christopher Gilbert (http://motomastyle.com/)
for use in PyLoris (http://pyloris.sourceforge.net/)
@@ -35,10 +38,13 @@ for use in PyLoris (http://pyloris.sourceforge.net/)
Minor modifications made by Mario Vilas (http://breakingcode.wordpress.com/)
mainly to merge bug fixes found in Sourceforge
+Minor modifications made by Eugene Dementiev (http://www.dementiev.eu/)
+
"""
import socket
import struct
+import sys
PROXY_TYPE_SOCKS4 = 1
PROXY_TYPE_SOCKS5 = 2
@@ -208,12 +214,12 @@ class socksocket(socket.socket):
if self.__proxy[3]:
# Resolve remotely
ipaddr = None
- req = req + chr(0x03).encode() + chr(len(destaddr)).encode() + destaddr
+ req = req + chr(0x03).encode() + chr(len(destaddr)).encode() + destaddr.encode()
else:
# Resolve locally
ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
req = req + chr(0x01).encode() + ipaddr
- req = req + struct.pack(">H", destport)
+ req += struct.pack(">H", destport)
self.sendall(req)
# Get the response
resp = self.__recvall(4)
@@ -282,7 +288,7 @@ class socksocket(socket.socket):
# The username parameter is considered userid for SOCKS4
if self.__proxy[4] != None:
req = req + self.__proxy[4]
- req = req + chr(0x00).encode()
+ req += chr(0x00).encode()
# DNS name if remote resolving is required
# NOTE: This is actually an extension to the SOCKS4 protocol
# called SOCKS4A and may not be supported in all cases.
@@ -323,7 +329,10 @@ class socksocket(socket.socket):
# We read the response until we get the string "\r\n\r\n"
resp = self.recv(1)
while resp.find("\r\n\r\n".encode()) == -1:
- resp = resp + self.recv(1)
+ recv = self.recv(1)
+ if not recv:
+ raise GeneralProxyError((1, _generalerrors[1]))
+ resp = resp + recv
# We just need the first line to check if the connection
# was successful
statusline = resp.splitlines()[0].split(" ".encode(), 2)
diff --git a/sleekxmpp/thirdparty/statemachine.py b/sleekxmpp/thirdparty/statemachine.py
index 113320fa..6c504dce 100644
--- a/sleekxmpp/thirdparty/statemachine.py
+++ b/sleekxmpp/thirdparty/statemachine.py
@@ -34,7 +34,7 @@ class StateMachine(object):
self.lock.release()
- def transition(self, from_state, to_state, wait=0.0, func=None, args=[], kwargs={}):
+ def transition(self, from_state, to_state, wait=0.0, func=None, args=None, kwargs=None):
'''
Transition from the given `from_state` to the given `to_state`.
This method will return `True` if the state machine is now in `to_state`. It
@@ -65,15 +65,23 @@ class StateMachine(object):
values for `args` and `kwargs` are provided, they are expanded and passed like so:
`func( *args, **kwargs )`.
'''
+ if not args:
+ args = []
+ if not kwargs:
+ kwargs = {}
return self.transition_any((from_state,), to_state, wait=wait,
func=func, args=args, kwargs=kwargs)
- def transition_any(self, from_states, to_state, wait=0.0, func=None, args=[], kwargs={}):
+ def transition_any(self, from_states, to_state, wait=0.0, func=None, args=None, kwargs=None):
'''
Transition from any of the given `from_states` to the given `to_state`.
'''
+ if not args:
+ args = []
+ if not kwargs:
+ kwargs = {}
if not isinstance(from_states, (tuple, list, set)):
raise ValueError("from_states should be a list, tuple, or set")
diff --git a/sleekxmpp/util/__init__.py b/sleekxmpp/util/__init__.py
index 05286d33..47a935af 100644
--- a/sleekxmpp/util/__init__.py
+++ b/sleekxmpp/util/__init__.py
@@ -32,12 +32,17 @@ def _gevent_threads_enabled():
if _gevent_threads_enabled():
import gevent.queue as queue
- Queue = queue.JoinableQueue
+ _queue = queue.JoinableQueue
else:
try:
import queue
except ImportError:
import Queue as queue
- Queue = queue.Queue
+ _queue = queue.Queue
+class Queue(_queue):
+ def put(self, item, block=True, timeout=None):
+ if _queue.full(self):
+ _queue.get(self)
+ return _queue.put(self, item, block, timeout)
QueueEmpty = queue.Empty
diff --git a/sleekxmpp/util/sasl/mechanisms.py b/sleekxmpp/util/sasl/mechanisms.py
index d341ed3e..7a7ebf7b 100644
--- a/sleekxmpp/util/sasl/mechanisms.py
+++ b/sleekxmpp/util/sasl/mechanisms.py
@@ -223,17 +223,16 @@ class SCRAM(Mech):
return self.hash(text).digest()
def saslname(self, value):
- escaped = b''
- for char in bytes(value):
- if char == b',':
- escaped += b'=2C'
- elif char == b'=':
- escaped += b'=3D'
+ value = value.decode("utf-8")
+ escaped = []
+ for char in value:
+ if char == ',':
+ escaped += '=2C'
+ elif char == '=':
+ escaped += '=3D'
else:
- if isinstance(char, int):
- char = chr(char)
- escaped += bytes(char)
- return escaped
+ escaped += char
+ return "".join(escaped).encode("utf-8")
def parse(self, challenge):
items = {}
diff --git a/sleekxmpp/version.py b/sleekxmpp/version.py
index ecf62550..acea9334 100644
--- a/sleekxmpp/version.py
+++ b/sleekxmpp/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.3.1'
-__version_info__ = (1, 3, 1, '', 0)
+__version__ = '1.4.0'
+__version_info__ = (1, 4, 0, '', 0)
diff --git a/sleekxmpp/xmlstream/cert.py b/sleekxmpp/xmlstream/cert.py
index 71146f36..d357b326 100644
--- a/sleekxmpp/xmlstream/cert.py
+++ b/sleekxmpp/xmlstream/cert.py
@@ -181,4 +181,4 @@ def verify(expected, raw_cert):
return True
raise CertificateError(
- 'Could not match certficate against hostname: %s' % expected)
+ 'Could not match certificate against hostname: %s' % expected)
diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py
index 97107098..c2e0f718 100644
--- a/sleekxmpp/xmlstream/stanzabase.py
+++ b/sleekxmpp/xmlstream/stanzabase.py
@@ -19,6 +19,7 @@ import logging
import weakref
from xml.etree import cElementTree as ET
+from sleekxmpp.util import safedict
from sleekxmpp.xmlstream import JID
from sleekxmpp.xmlstream.tostring import tostring
from sleekxmpp.thirdparty import OrderedDict
@@ -562,10 +563,13 @@ class ElementBase(object):
.. versionadded:: 1.0-Beta1
"""
- values = {}
+ values = OrderedDict()
values['lang'] = self['lang']
for interface in self.interfaces:
- values[interface] = self[interface]
+ if isinstance(self[interface], JID):
+ values[interface] = self[interface].jid
+ else:
+ values[interface] = self[interface]
if interface in self.lang_interfaces:
values['%s|*' % interface] = self['%s|*' % interface]
for plugin, stanza in self.plugins.items():
@@ -676,6 +680,8 @@ class ElementBase(object):
if lang and attrib in self.lang_interfaces:
kwargs['lang'] = lang
+ kwargs = safedict(kwargs)
+
if attrib == 'substanzas':
return self.iterables
elif attrib in self.interfaces or attrib == 'lang':
@@ -752,6 +758,8 @@ class ElementBase(object):
if lang and attrib in self.lang_interfaces:
kwargs['lang'] = lang
+ kwargs = safedict(kwargs)
+
if attrib in self.interfaces or attrib == 'lang':
if value is not None:
set_method = "set_%s" % attrib.lower()
@@ -838,6 +846,8 @@ class ElementBase(object):
if lang and attrib in self.lang_interfaces:
kwargs['lang'] = lang
+ kwargs = safedict(kwargs)
+
if attrib in self.interfaces or attrib == 'lang':
del_method = "del_%s" % attrib.lower()
del_method2 = "del%s" % attrib.title()
diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py
index 66985f3d..62d46100 100644
--- a/sleekxmpp/xmlstream/xmlstream.py
+++ b/sleekxmpp/xmlstream/xmlstream.py
@@ -114,7 +114,8 @@ class XMLStream(object):
:param int port: The port to use for the connection. Defaults to 0.
"""
- def __init__(self, socket=None, host='', port=0):
+ def __init__(self, socket=None, host='', port=0, certfile=None,
+ keyfile=None, ca_certs=None, **kwargs):
#: Most XMPP servers support TLSv1, but OpenFire in particular
#: does not work well with it. For OpenFire, set
#: :attr:`ssl_version` to use ``SSLv23``::
@@ -136,16 +137,16 @@ class XMLStream(object):
#:
#: On Mac OS X, certificates in the system keyring will
#: be consulted, even if they are not in the provided file.
- self.ca_certs = None
+ self.ca_certs = ca_certs
#: Path to a file containing a client certificate to use for
#: authenticating via SASL EXTERNAL. If set, there must also
#: be a corresponding `:attr:keyfile` value.
- self.certfile = None
+ self.certfile = certfile
#: Path to a file containing the private key for the selected
#: client certificate to use for authenticating via SASL EXTERNAL.
- self.keyfile = None
+ self.keyfile = keyfile
self._der_cert = None
@@ -291,7 +292,7 @@ class XMLStream(object):
self.event_queue = Queue()
#: A queue of string data to be sent over the stream.
- self.send_queue = Queue()
+ self.send_queue = Queue(maxsize=256)
self.send_queue_lock = threading.Lock()
self.send_lock = threading.RLock()
@@ -460,9 +461,11 @@ class XMLStream(object):
def _connect(self, reattempt=True):
self.scheduler.remove('Session timeout check')
- if self.reconnect_delay is None or not reattempt:
+ if self.reconnect_delay is None:
delay = 1.0
- else:
+ self.reconnect_delay = delay
+
+ if reattempt:
delay = min(self.reconnect_delay * 2, self.reconnect_max_delay)
delay = random.normalvariate(delay, delay * 0.1)
log.debug('Waiting %s seconds before connecting.', delay)
@@ -523,7 +526,8 @@ class XMLStream(object):
'keyfile': self.keyfile,
'ca_certs': self.ca_certs,
'cert_reqs': cert_policy,
- 'do_handshake_on_connect': False
+ 'do_handshake_on_connect': False,
+ "ssl_version": self.ssl_version
})
if sys.version_info >= (2, 7):
@@ -847,13 +851,14 @@ class XMLStream(object):
'keyfile': self.keyfile,
'ca_certs': self.ca_certs,
'cert_reqs': cert_policy,
- 'do_handshake_on_connect': False
+ 'do_handshake_on_connect': False,
+ "ssl_version": self.ssl_version
})
if sys.version_info >= (2, 7):
ssl_args['ciphers'] = self.ciphers
- ssl_socket = ssl.wrap_socket(self.socket, **ssl_args);
+ ssl_socket = ssl.wrap_socket(self.socket, **ssl_args)
if hasattr(self.socket, 'socket'):
# We are using a testing socket, so preserve the top
@@ -938,12 +943,13 @@ class XMLStream(object):
self.whitespace_keepalive_interval = 300
"""
- self.schedule('Whitespace Keepalive',
- self.whitespace_keepalive_interval,
- self.send_raw,
- args=(' ',),
- kwargs={'now': True},
- repeat=True)
+ if self.whitespace_keepalive:
+ self.schedule('Whitespace Keepalive',
+ self.whitespace_keepalive_interval,
+ self.send_raw,
+ args=(' ',),
+ kwargs={'now': True},
+ repeat=True)
def _remove_schedules(self, event):
"""Remove whitespace keepalive and certificate expiration schedules."""
@@ -1148,7 +1154,7 @@ class XMLStream(object):
"""
return len(self.__event_handlers.get(name, []))
- def event(self, name, data={}, direct=False):
+ def event(self, name, data=None, direct=False):
"""Manually trigger a custom event.
:param name: The name of the event to trigger.
@@ -1159,6 +1165,9 @@ class XMLStream(object):
event queue. All event handlers will run in the
same thread.
"""
+ if not data:
+ data = {}
+
log.debug("Event triggered: " + name)
handlers = self.__event_handlers.get(name, [])
@@ -1318,9 +1327,6 @@ class XMLStream(object):
try:
sent += self.socket.send(data[sent:])
count += 1
- except Socket.error as serr:
- if serr.errno != errno.EINTR:
- raise
except ssl.SSLError as serr:
if tries >= self.ssl_retry_max:
log.debug('SSL error: max retries reached')
@@ -1335,6 +1341,9 @@ class XMLStream(object):
if not self.stop.is_set():
time.sleep(self.ssl_retry_delay)
tries += 1
+ except Socket.error as serr:
+ if serr.errno != errno.EINTR:
+ raise
if count > 1:
log.debug('SENT: %d chunks', count)
except (Socket.error, ssl.SSLError) as serr:
@@ -1744,9 +1753,6 @@ class XMLStream(object):
try:
sent += self.socket.send(enc_data[sent:])
count += 1
- except Socket.error as serr:
- if serr.errno != errno.EINTR:
- raise
except ssl.SSLError as serr:
if tries >= self.ssl_retry_max:
log.debug('SSL error: max retries reached')
@@ -1759,6 +1765,9 @@ class XMLStream(object):
if not self.stop.is_set():
time.sleep(self.ssl_retry_delay)
tries += 1
+ except Socket.error as serr:
+ if serr.errno != errno.EINTR:
+ raise
if count > 1:
log.debug('SENT: %d chunks', count)
self.send_queue.task_done()
diff --git a/tests/test_stanza_element.py b/tests/test_stanza_element.py
index 2b9676cf..e678b56e 100644
--- a/tests/test_stanza_element.py
+++ b/tests/test_stanza_element.py
@@ -385,7 +385,7 @@ class TestElementBase(SleekTest):
interfaces = set(('bar', 'baz'))
def setBar(self, value):
- self._set_sub_text("path/to/only/bar", value);
+ self._set_sub_text("path/to/only/bar", value)
def getBar(self):
return self._get_sub_text("path/to/only/bar")
@@ -394,7 +394,7 @@ class TestElementBase(SleekTest):
self._del_sub("path/to/only/bar")
def setBaz(self, value):
- self._set_sub_text("path/to/just/baz", value);
+ self._set_sub_text("path/to/just/baz", value)
def getBaz(self):
return self._get_sub_text("path/to/just/baz")
diff --git a/tests/test_stanza_xep_0004.py b/tests/test_stanza_xep_0004.py
index 9056c663..b87afb24 100644
--- a/tests/test_stanza_xep_0004.py
+++ b/tests/test_stanza_xep_0004.py
@@ -11,8 +11,8 @@ class TestDataForms(SleekTest):
def setUp(self):
register_stanza_plugin(Message, xep_0004.Form)
- register_stanza_plugin(xep_0004.Form, xep_0004.FormField)
- register_stanza_plugin(xep_0004.FormField, xep_0004.FieldOption)
+ register_stanza_plugin(xep_0004.Form, xep_0004.FormField, iterable=True)
+ register_stanza_plugin(xep_0004.FormField, xep_0004.FieldOption, iterable=True)
def testMultipleInstructions(self):
"""Testing using multiple instructions elements in a data form."""
@@ -68,7 +68,7 @@ class TestDataForms(SleekTest):
'value': 'cool'},
{'label': 'Urgh!',
'value': 'urgh'}]}
- form['fields'] = fields
+ form.set_fields(fields)
self.check(msg, """
@@ -141,13 +141,13 @@ class TestDataForms(SleekTest):
'value': 'cool'},
{'label': 'Urgh!',
'value': 'urgh'}]}
- form['fields'] = fields
+ form.set_fields(fields)
form['type'] = 'submit'
- form['values'] = {'f1': 'username',
+ form.set_values({'f1': 'username',
'f2': 'hunter2',
'f3': 'A long\nmultiline\nmessage',
- 'f4': 'cool'}
+ 'f4': 'cool'})
self.check(form, """
<x xmlns="jabber:x:data" type="submit">
@@ -189,7 +189,7 @@ class TestDataForms(SleekTest):
'value': 'cool'},
{'label': 'Urgh!',
'value': 'urgh'}]}
- form['fields'] = fields
+ form.set_fields(fields)
form['type'] = 'cancel'
@@ -197,5 +197,52 @@ class TestDataForms(SleekTest):
<x xmlns="jabber:x:data" type="cancel" />
""")
+ def testReported(self):
+ msg = self.Message()
+ form = msg['form']
+ form['type'] = 'result'
+
+ form.add_reported(var='f1', ftype='text-single', label='Username')
+
+ form.add_item({'f1': 'username@example.org'})
+
+ self.check(msg, """
+ <message>
+ <x xmlns="jabber:x:data" type="result">
+ <reported>
+ <field var="f1" type="text-single" label="Username" />
+ </reported>
+ <item>
+ <field var="f1">
+ <value>username@example.org</value>
+ </field>
+ </item>
+ </x>
+ </message>
+ """)
+
+ def testSetReported(self):
+ msg = self.Message()
+ form = msg['form']
+ form['type'] = 'result'
+
+ reported = {'f1': {
+ 'var': 'f1',
+ 'type': 'text-single',
+ 'label': 'Username'
+ }}
+
+ form.set_reported(reported)
+
+ self.check(msg, """
+ <message>
+ <x xmlns="jabber:x:data" type="result">
+ <reported>
+ <field var="f1" type="text-single" label="Username" />
+ </reported>
+ </x>
+ </message>
+ """)
+
suite = unittest.TestLoader().loadTestsFromTestCase(TestDataForms)
diff --git a/tests/test_stanza_xep_0122.py b/tests/test_stanza_xep_0122.py
new file mode 100644
index 00000000..fca49bbb
--- /dev/null
+++ b/tests/test_stanza_xep_0122.py
@@ -0,0 +1,189 @@
+import unittest
+
+from sleekxmpp import Message
+from sleekxmpp.test import SleekTest
+import sleekxmpp.plugins.xep_0004 as xep_0004
+import sleekxmpp.plugins.xep_0122 as xep_0122
+from sleekxmpp.xmlstream import register_stanza_plugin
+
+
+class TestDataForms(SleekTest):
+
+ def setUp(self):
+ register_stanza_plugin(Message, xep_0004.Form)
+ register_stanza_plugin(xep_0004.Form, xep_0004.FormField, iterable=True)
+ register_stanza_plugin(xep_0004.FormField, xep_0004.FieldOption, iterable=True)
+ register_stanza_plugin(xep_0004.FormField, xep_0122.FormValidation)
+
+ def test_basic_validation(self):
+ """Testing basic validation setting and getting."""
+ msg = self.Message()
+ form = msg['form']
+ field = form.addField(var='f1',
+ ftype='text-single',
+ label='Text',
+ desc='A text field',
+ required=True,
+ value='Some text!')
+
+ validation = field['validate']
+ validation['datatype'] = 'xs:string'
+ validation.set_basic(True)
+
+ self.check(msg, """
+ <message>
+ <x xmlns="jabber:x:data" type="form">
+ <field var="f1" type="text-single" label="Text">
+ <desc>A text field</desc>
+ <required />
+ <value>Some text!</value>
+ <validate xmlns="http://jabber.org/protocol/xdata-validate" datatype="xs:string">
+ <basic/>
+ </validate>
+ </field>
+ </x>
+ </message>
+ """)
+
+ self.assertTrue(validation.get_basic())
+ self.assertFalse(validation.get_open())
+ self.assertFalse(validation.get_range())
+ self.assertFalse(validation.get_regex())
+
+ def test_open_validation(self):
+ """Testing open validation setting and getting."""
+ msg = self.Message()
+ form = msg['form']
+ field = form.addField(var='f1',
+ ftype='text-single',
+ label='Text',
+ desc='A text field',
+ required=True,
+ value='Some text!')
+
+ validation = field['validate']
+ validation.set_open(True)
+
+ self.check(msg, """
+ <message>
+ <x xmlns="jabber:x:data" type="form">
+ <field var="f1" type="text-single" label="Text">
+ <desc>A text field</desc>
+ <required />
+ <value>Some text!</value>
+ <validate xmlns="http://jabber.org/protocol/xdata-validate">
+ <open />
+ </validate>
+ </field>
+ </x>
+ </message>
+ """)
+
+ self.assertFalse(validation.get_basic())
+ self.assertTrue(validation.get_open())
+ self.assertFalse(validation.get_range())
+ self.assertFalse(validation.get_regex())
+
+ def test_regex_validation(self):
+ """Testing regex validation setting and getting."""
+ msg = self.Message()
+ form = msg['form']
+ field = form.addField(var='f1',
+ ftype='text-single',
+ label='Text',
+ desc='A text field',
+ required=True,
+ value='Some text!')
+
+ regex_value = '[0-9]+'
+
+ validation = field['validate']
+ validation.set_regex(regex_value)
+
+ self.check(msg, """
+ <message>
+ <x xmlns="jabber:x:data" type="form">
+ <field var="f1" type="text-single" label="Text">
+ <desc>A text field</desc>
+ <required />
+ <value>Some text!</value>
+ <validate xmlns="http://jabber.org/protocol/xdata-validate">
+ <regex>[0-9]+</regex>
+ </validate>
+ </field>
+ </x>
+ </message>
+ """)
+
+ self.assertFalse(validation.get_basic())
+ self.assertFalse(validation.get_open())
+ self.assertFalse(validation.get_range())
+ self.assertTrue(validation.get_regex())
+
+ self.assertEqual(regex_value, validation.get_regex())
+
+ def test_range_validation(self):
+ """Testing range validation setting and getting."""
+ msg = self.Message()
+ form = msg['form']
+ field = form.addField(var='f1',
+ ftype='text-single',
+ label='Text',
+ desc='A text field',
+ required=True,
+ value='Some text!')
+
+ validation = field['validate']
+ validation.set_range(True, minimum=0, maximum=10)
+
+ self.check(msg, """
+ <message>
+ <x xmlns="jabber:x:data" type="form">
+ <field var="f1" type="text-single" label="Text">
+ <desc>A text field</desc>
+ <required />
+ <value>Some text!</value>
+ <validate xmlns="http://jabber.org/protocol/xdata-validate">
+ <range min="0" max="10" />
+ </validate>
+ </field>
+ </x>
+ </message>
+ """)
+
+ self.assertDictEqual(dict(minimum=str(0), maximum=str(10)), validation.get_range())
+
+ def test_reported_field_validation(self):
+ """
+ Testing adding validation to the field when it's stored in the reported.
+ :return:
+ """
+ msg = self.Message()
+ form = msg['form']
+ field = form.addReported(var='f1', ftype='text-single', label='Text')
+ validation = field['validate']
+ validation.set_basic(True)
+
+ form.addItem({'f1': 'Some text!'})
+
+ self.check(msg, """
+ <message>
+ <x xmlns="jabber:x:data" type="form">
+ <reported>
+ <field var="f1" type="text-single" label="Text">
+ <validate xmlns="http://jabber.org/protocol/xdata-validate">
+ <basic />
+ </validate>
+ </field>
+ </reported>
+ <item>
+ <field var="f1">
+ <value>Some text!</value>
+ </field>
+ </item>
+ </x>
+ </message>
+ """)
+
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestDataForms)
diff --git a/tests/test_stanza_xep_0323.py b/tests/test_stanza_xep_0323.py
index 67e0daf0..7b1dfe42 100644
--- a/tests/test_stanza_xep_0323.py
+++ b/tests/test_stanza_xep_0323.py
@@ -6,7 +6,7 @@ import sleekxmpp.plugins.xep_0323 as xep_0323
namespace='sn'
class TestSensorDataStanzas(SleekTest):
-
+
def setUp(self):
pass
@@ -59,8 +59,8 @@ class TestSensorDataStanzas(SleekTest):
iq['req']['momentary'] = 'true'
- iq['req'].add_node("Device02", "Source02", "CacheType");
- iq['req'].add_node("Device44");
+ iq['req'].add_node("Device02", "Source02", "CacheType")
+ iq['req'].add_node("Device44")
self.check(iq,"""
<iq type='get'
@@ -75,7 +75,7 @@ class TestSensorDataStanzas(SleekTest):
"""
)
- iq['req'].del_node("Device02");
+ iq['req'].del_node("Device02")
self.check(iq,"""
<iq type='get'
@@ -89,7 +89,7 @@ class TestSensorDataStanzas(SleekTest):
"""
)
- iq['req'].del_nodes();
+ iq['req'].del_nodes()
self.check(iq,"""
<iq type='get'
@@ -115,8 +115,8 @@ class TestSensorDataStanzas(SleekTest):
iq['req']['momentary'] = 'true'
- iq['req'].add_field("Top temperature");
- iq['req'].add_field("Bottom temperature");
+ iq['req'].add_field("Top temperature")
+ iq['req'].add_field("Bottom temperature")
self.check(iq,"""
<iq type='get'
@@ -171,7 +171,7 @@ class TestSensorDataStanzas(SleekTest):
iq['accepted']['seqnr'] = '2'
self.check(iq,"""
- <iq type='result'
+ <iq type='result'
from='device@clayster.com'
to='master@clayster.com/amr'
id='2'>
@@ -193,7 +193,7 @@ class TestSensorDataStanzas(SleekTest):
iq['rejected']['error'] = 'Access denied.'
self.check(iq,"""
- <iq type='error'
+ <iq type='error'
from='device@clayster.com'
to='master@clayster.com/amr'
id='4'>
@@ -237,12 +237,12 @@ class TestSensorDataStanzas(SleekTest):
msg['to'] = 'master@clayster.com/amr'
msg['fields']['seqnr'] = '1'
- node = msg['fields'].add_node("Device02");
- ts = node.add_timestamp("2013-03-07T16:24:30");
+ node = msg['fields'].add_node("Device02")
+ ts = node.add_timestamp("2013-03-07T16:24:30")
- data = ts.add_data(typename="numeric", name="Temperature", value="-12.42", unit='K');
- data['momentary'] = 'true';
- data['automaticReadout'] = 'true';
+ data = ts.add_data(typename="numeric", name="Temperature", value="-12.42", unit='K')
+ data['momentary'] = 'true'
+ data['automaticReadout'] = 'true'
self.check(msg,"""
<message from='device@clayster.com'
@@ -250,7 +250,7 @@ class TestSensorDataStanzas(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'>
<node nodeId='Device02'>
<timestamp value='2013-03-07T16:24:30'>
- <numeric name='Temperature' momentary='true' automaticReadout='true' value='-12.42' unit='K'/>
+ <numeric name='Temperature' momentary='true' automaticReadout='true' value='-12.42' unit='K'/>
</timestamp>
</node>
</fields>
@@ -258,10 +258,9 @@ class TestSensorDataStanzas(SleekTest):
"""
)
- node = msg['fields'].add_node("EmptyDevice");
- node = msg['fields'].add_node("Device04");
- ts = node.add_timestamp("EmptyTimestamp");
-
+ node = msg['fields'].add_node("EmptyDevice")
+ node = msg['fields'].add_node("Device04")
+ ts = node.add_timestamp("EmptyTimestamp")
self.check(msg,"""
<message from='device@clayster.com'
@@ -269,7 +268,7 @@ class TestSensorDataStanzas(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'>
<node nodeId='Device02'>
<timestamp value='2013-03-07T16:24:30'>
- <numeric name='Temperature' momentary='true' automaticReadout='true' value='-12.42' unit='K'/>
+ <numeric name='Temperature' momentary='true' automaticReadout='true' value='-12.42' unit='K'/>
</timestamp>
</node>
<node nodeId='EmptyDevice'/>
@@ -281,32 +280,32 @@ class TestSensorDataStanzas(SleekTest):
"""
)
- node = msg['fields'].add_node("Device77");
- ts = node.add_timestamp("2013-05-03T12:00:01");
- data = ts.add_data(typename="numeric", name="Temperature", value="-12.42", unit='K');
- data['historicalDay'] = 'true';
- data = ts.add_data(typename="numeric", name="Speed", value="312.42", unit='km/h');
- data['historicalWeek'] = 'false';
- data = ts.add_data(typename="string", name="Temperature name", value="Bottom oil");
- data['historicalMonth'] = 'true';
- data = ts.add_data(typename="string", name="Speed name", value="Top speed");
- data['historicalQuarter'] = 'false';
- data = ts.add_data(typename="dateTime", name="T1", value="1979-01-01T00:00:00");
- data['historicalYear'] = 'true';
- data = ts.add_data(typename="dateTime", name="T2", value="2000-01-01T01:02:03");
- data['historicalOther'] = 'false';
- data = ts.add_data(typename="timeSpan", name="TS1", value="P5Y");
- data['missing'] = 'true';
- data = ts.add_data(typename="timeSpan", name="TS2", value="PT2M1S");
- data['manualEstimate'] = 'false';
- data = ts.add_data(typename="enum", name="top color", value="red", dataType="string");
- data['invoiced'] = 'true';
- data = ts.add_data(typename="enum", name="bottom color", value="black", dataType="string");
- data['powerFailure'] = 'false';
- data = ts.add_data(typename="boolean", name="Temperature real", value="false");
- data['historicalDay'] = 'true';
- data = ts.add_data(typename="boolean", name="Speed real", value="true");
- data['historicalWeek'] = 'false';
+ node = msg['fields'].add_node("Device77")
+ ts = node.add_timestamp("2013-05-03T12:00:01")
+ data = ts.add_data(typename="numeric", name="Temperature", value="-12.42", unit='K')
+ data['historicalDay'] = 'true'
+ data = ts.add_data(typename="numeric", name="Speed", value="312.42", unit='km/h')
+ data['historicalWeek'] = 'false'
+ data = ts.add_data(typename="string", name="Temperature name", value="Bottom oil")
+ data['historicalMonth'] = 'true'
+ data = ts.add_data(typename="string", name="Speed name", value="Top speed")
+ data['historicalQuarter'] = 'false'
+ data = ts.add_data(typename="dateTime", name="T1", value="1979-01-01T00:00:00")
+ data['historicalYear'] = 'true'
+ data = ts.add_data(typename="dateTime", name="T2", value="2000-01-01T01:02:03")
+ data['historicalOther'] = 'false'
+ data = ts.add_data(typename="timeSpan", name="TS1", value="P5Y")
+ data['missing'] = 'true'
+ data = ts.add_data(typename="timeSpan", name="TS2", value="PT2M1S")
+ data['manualEstimate'] = 'false'
+ data = ts.add_data(typename="enum", name="top color", value="red", dataType="string")
+ data['invoiced'] = 'true'
+ data = ts.add_data(typename="enum", name="bottom color", value="black", dataType="string")
+ data['powerFailure'] = 'false'
+ data = ts.add_data(typename="boolean", name="Temperature real", value="false")
+ data['historicalDay'] = 'true'
+ data = ts.add_data(typename="boolean", name="Speed real", value="true")
+ data['historicalWeek'] = 'false'
self.check(msg,"""
<message from='device@clayster.com'
@@ -314,7 +313,7 @@ class TestSensorDataStanzas(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'>
<node nodeId='Device02'>
<timestamp value='2013-03-07T16:24:30'>
- <numeric name='Temperature' momentary='true' automaticReadout='true' value='-12.42' unit='K'/>
+ <numeric name='Temperature' momentary='true' automaticReadout='true' value='-12.42' unit='K'/>
</timestamp>
</node>
<node nodeId='EmptyDevice'/>
@@ -323,18 +322,18 @@ class TestSensorDataStanzas(SleekTest):
</node>
<node nodeId='Device77'>
<timestamp value='2013-05-03T12:00:01'>
- <numeric name='Temperature' historicalDay='true' value='-12.42' unit='K'/>
- <numeric name='Speed' historicalWeek='false' value='312.42' unit='km/h'/>
- <string name='Temperature name' historicalMonth='true' value='Bottom oil'/>
- <string name='Speed name' historicalQuarter='false' value='Top speed'/>
- <dateTime name='T1' historicalYear='true' value='1979-01-01T00:00:00'/>
- <dateTime name='T2' historicalOther='false' value='2000-01-01T01:02:03'/>
- <timeSpan name='TS1' missing='true' value='P5Y'/>
- <timeSpan name='TS2' manualEstimate='false' value='PT2M1S'/>
- <enum name='top color' invoiced='true' value='red' dataType='string'/>
- <enum name='bottom color' powerFailure='false' value='black' dataType='string'/>
- <boolean name='Temperature real' historicalDay='true' value='false'/>
- <boolean name='Speed real' historicalWeek='false' value='true'/>
+ <numeric name='Temperature' historicalDay='true' value='-12.42' unit='K'/>
+ <numeric name='Speed' historicalWeek='false' value='312.42' unit='km/h'/>
+ <string name='Temperature name' historicalMonth='true' value='Bottom oil'/>
+ <string name='Speed name' historicalQuarter='false' value='Top speed'/>
+ <dateTime name='T1' historicalYear='true' value='1979-01-01T00:00:00'/>
+ <dateTime name='T2' historicalOther='false' value='2000-01-01T01:02:03'/>
+ <timeSpan name='TS1' missing='true' value='P5Y'/>
+ <timeSpan name='TS2' manualEstimate='false' value='PT2M1S'/>
+ <enum name='top color' invoiced='true' value='red' dataType='string'/>
+ <enum name='bottom color' powerFailure='false' value='black' dataType='string'/>
+ <boolean name='Temperature real' historicalDay='true' value='false'/>
+ <boolean name='Speed real' historicalWeek='false' value='true'/>
</timestamp>
</node>
</fields>
@@ -342,21 +341,19 @@ class TestSensorDataStanzas(SleekTest):
"""
)
-
+
def testTimestamp(self):
- msg = self.Message();
+ msg = self.Message()
msg['from'] = 'device@clayster.com'
msg['to'] = 'master@clayster.com/amr'
msg['fields']['seqnr'] = '1'
- node = msg['fields'].add_node("Device02");
- node = msg['fields'].add_node("Device03");
-
- ts = node.add_timestamp("2013-03-07T16:24:30");
- ts = node.add_timestamp("2013-03-07T16:24:31");
-
+ node = msg['fields'].add_node("Device02")
+ node = msg['fields'].add_node("Device03")
+ ts = node.add_timestamp("2013-03-07T16:24:30")
+ ts = node.add_timestamp("2013-03-07T16:24:31")
self.check(msg,"""
<message from='device@clayster.com'
@@ -386,8 +383,8 @@ class TestSensorDataStanzas(SleekTest):
self.check(msg,emptyStringIdXML)
msg['fields']['stringIds'] = "1"
self.check(msg,emptyStringIdXML)
-
-
+
+
suite = unittest.TestLoader().loadTestsFromTestCase(TestSensorDataStanzas)
diff --git a/tests/test_stanza_xep_0325.py b/tests/test_stanza_xep_0325.py
index b15b764c..dc2e8efe 100644
--- a/tests/test_stanza_xep_0325.py
+++ b/tests/test_stanza_xep_0325.py
@@ -15,7 +15,7 @@ import sleekxmpp.plugins.xep_0325 as xep_0325
namespace='sn'
class TestControlStanzas(SleekTest):
-
+
def setUp(self):
pass
@@ -29,8 +29,8 @@ class TestControlStanzas(SleekTest):
iq['from'] = 'master@clayster.com/amr'
iq['to'] = 'device@clayster.com'
iq['id'] = '1'
- iq['set'].add_node("Device02", "Source02", "MyCacheType");
- iq['set'].add_node("Device15");
+ iq['set'].add_node("Device02", "Source02", "MyCacheType")
+ iq['set'].add_node("Device15")
iq['set'].add_data("Tjohej", "boolean", "true")
self.check(iq,"""
@@ -47,7 +47,7 @@ class TestControlStanzas(SleekTest):
"""
)
- iq['set'].del_node("Device02");
+ iq['set'].del_node("Device02")
self.check(iq,"""
<iq type='set'
@@ -62,7 +62,7 @@ class TestControlStanzas(SleekTest):
"""
)
- iq['set'].del_nodes();
+ iq['set'].del_nodes()
self.check(iq,"""
<iq type='set'
@@ -84,12 +84,12 @@ class TestControlStanzas(SleekTest):
msg = self.Message()
msg['from'] = 'master@clayster.com/amr'
msg['to'] = 'device@clayster.com'
- msg['set'].add_node("Device02");
- msg['set'].add_node("Device15");
+ msg['set'].add_node("Device02")
+ msg['set'].add_node("Device15")
msg['set'].add_data("Tjohej", "boolean", "true")
self.check(msg,"""
- <message
+ <message
from='master@clayster.com/amr'
to='device@clayster.com'>
<set xmlns='urn:xmpp:iot:control'>
@@ -111,7 +111,7 @@ class TestControlStanzas(SleekTest):
iq['from'] = 'master@clayster.com/amr'
iq['to'] = 'device@clayster.com'
iq['id'] = '8'
- iq['setResponse']['responseCode'] = "OK";
+ iq['setResponse']['responseCode'] = "OK"
self.check(iq,"""
<iq type='result'
@@ -128,10 +128,9 @@ class TestControlStanzas(SleekTest):
iq['from'] = 'master@clayster.com/amr'
iq['to'] = 'device@clayster.com'
iq['id'] = '9'
- iq['setResponse']['responseCode'] = "OtherError";
- iq['setResponse']['error']['var'] = "Output";
- iq['setResponse']['error']['text'] = "Test of other error.!";
-
+ iq['setResponse']['responseCode'] = "OtherError"
+ iq['setResponse']['error']['var'] = "Output"
+ iq['setResponse']['error']['text'] = "Test of other error.!"
self.check(iq,"""
<iq type='error'
@@ -150,11 +149,10 @@ class TestControlStanzas(SleekTest):
iq['from'] = 'master@clayster.com/amr'
iq['to'] = 'device@clayster.com'
iq['id'] = '9'
- iq['setResponse']['responseCode'] = "NotFound";
- iq['setResponse'].add_node("Device17", "Source09");
- iq['setResponse'].add_node("Device18", "Source09");
- iq['setResponse'].add_data("Tjohopp");
-
+ iq['setResponse']['responseCode'] = "NotFound"
+ iq['setResponse'].add_node("Device17", "Source09")
+ iq['setResponse'].add_node("Device18", "Source09")
+ iq['setResponse'].add_data("Tjohopp")
self.check(iq,"""
<iq type='error'
@@ -179,38 +177,38 @@ class TestControlStanzas(SleekTest):
iq['from'] = 'master@clayster.com/amr'
iq['to'] = 'device@clayster.com'
iq['id'] = '1'
- iq['set'].add_node("Device02", "Source02", "MyCacheType");
- iq['set'].add_node("Device15");
+ iq['set'].add_node("Device02", "Source02", "MyCacheType")
+ iq['set'].add_node("Device15")
- iq['set'].add_data("Tjohej", "boolean", "true");
- iq['set'].add_data("Tjohej2", "boolean", "false");
+ iq['set'].add_data("Tjohej", "boolean", "true")
+ iq['set'].add_data("Tjohej2", "boolean", "false")
- iq['set'].add_data("TjohejC", "color", "FF00FF");
- iq['set'].add_data("TjohejC2", "color", "00FF00");
+ iq['set'].add_data("TjohejC", "color", "FF00FF")
+ iq['set'].add_data("TjohejC2", "color", "00FF00")
- iq['set'].add_data("TjohejS", "string", "String1");
- iq['set'].add_data("TjohejS2", "string", "String2");
+ iq['set'].add_data("TjohejS", "string", "String1")
+ iq['set'].add_data("TjohejS2", "string", "String2")
- iq['set'].add_data("TjohejDate", "date", "2012-01-01");
- iq['set'].add_data("TjohejDate2", "date", "1900-12-03");
+ iq['set'].add_data("TjohejDate", "date", "2012-01-01")
+ iq['set'].add_data("TjohejDate2", "date", "1900-12-03")
- iq['set'].add_data("TjohejDateT4", "dateTime", "1900-12-03 12:30");
- iq['set'].add_data("TjohejDateT2", "dateTime", "1900-12-03 11:22");
+ iq['set'].add_data("TjohejDateT4", "dateTime", "1900-12-03 12:30")
+ iq['set'].add_data("TjohejDateT2", "dateTime", "1900-12-03 11:22")
- iq['set'].add_data("TjohejDouble2", "double", "200.22");
- iq['set'].add_data("TjohejDouble3", "double", "-12232131.3333");
+ iq['set'].add_data("TjohejDouble2", "double", "200.22")
+ iq['set'].add_data("TjohejDouble3", "double", "-12232131.3333")
- iq['set'].add_data("TjohejDur", "duration", "P5Y");
- iq['set'].add_data("TjohejDur2", "duration", "PT2M1S");
+ iq['set'].add_data("TjohejDur", "duration", "P5Y")
+ iq['set'].add_data("TjohejDur2", "duration", "PT2M1S")
- iq['set'].add_data("TjohejInt", "int", "1");
- iq['set'].add_data("TjohejInt2", "int", "-42");
+ iq['set'].add_data("TjohejInt", "int", "1")
+ iq['set'].add_data("TjohejInt2", "int", "-42")
- iq['set'].add_data("TjohejLong", "long", "123456789098");
- iq['set'].add_data("TjohejLong2", "long", "-90983243827489374");
+ iq['set'].add_data("TjohejLong", "long", "123456789098")
+ iq['set'].add_data("TjohejLong2", "long", "-90983243827489374")
- iq['set'].add_data("TjohejTime", "time", "23:59");
- iq['set'].add_data("TjohejTime2", "time", "12:00");
+ iq['set'].add_data("TjohejTime", "time", "23:59")
+ iq['set'].add_data("TjohejTime2", "time", "12:00")
self.check(iq,"""
<iq type='set'
@@ -244,5 +242,5 @@ class TestControlStanzas(SleekTest):
</iq>
"""
)
-
+
suite = unittest.TestLoader().loadTestsFromTestCase(TestControlStanzas)
diff --git a/tests/test_stream_xep_0050.py b/tests/test_stream_xep_0050.py
index 261a0057..0a3eb718 100644
--- a/tests/test_stream_xep_0050.py
+++ b/tests/test_stream_xep_0050.py
@@ -119,7 +119,7 @@ class TestAdHocCommands(SleekTest):
def handle_command(iq, session):
def handle_form(form, session):
- results.append(form['values']['foo'])
+ results.append(form.get_values()['foo'])
form = self.xmpp['xep_0004'].makeForm('form')
form.addField(var='foo', ftype='text-single', label='Foo')
@@ -191,10 +191,10 @@ class TestAdHocCommands(SleekTest):
def handle_command(iq, session):
def handle_step2(form, session):
- results.append(form['values']['bar'])
+ results.append(form.get_values()['bar'])
def handle_step1(form, session):
- results.append(form['values']['foo'])
+ results.append(form.get_values()['foo'])
form = self.xmpp['xep_0004'].makeForm('form')
form.addField(var='bar', ftype='text-single', label='Bar')
@@ -426,7 +426,7 @@ class TestAdHocCommands(SleekTest):
def handle_form(forms, session):
for form in forms:
- results.append(form['values']['FORM_TYPE'])
+ results.append(form.get_values()['FORM_TYPE'])
form1 = self.xmpp['xep_0004'].makeForm('form')
form1.addField(var='FORM_TYPE', ftype='hidden', value='form_1')
diff --git a/tests/test_stream_xep_0323.py b/tests/test_stream_xep_0323.py
index fd2ad225..94f1d638 100644
--- a/tests/test_stream_xep_0323.py
+++ b/tests/test_stream_xep_0323.py
@@ -19,7 +19,7 @@ class TestStreamSensorData(SleekTest):
pass
def _time_now(self):
- return datetime.datetime.now().replace(microsecond=0).isoformat();
+ return datetime.datetime.now().replace(microsecond=0).isoformat()
def tearDown(self):
self.stream_close()
@@ -29,12 +29,12 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- myDevice = Device("Device22");
- myDevice._add_field(name="Temperature", typename="numeric", unit="°C");
+ myDevice = Device("Device22")
+ myDevice._add_field(name="Temperature", typename="numeric", unit="°C")
myDevice._set_momentary_timestamp("2013-03-07T16:24:30")
- myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"});
+ myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"})
- self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5);
+ self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5)
self.recv("""
<iq type='get'
@@ -46,7 +46,7 @@ class TestStreamSensorData(SleekTest):
""")
self.send("""
- <iq type='result'
+ <iq type='result'
from='device@clayster.com'
to='master@clayster.com/amr'
id='1'>
@@ -60,11 +60,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'>
<node nodeId='Device22'>
<timestamp value='2013-03-07T16:24:30'>
- <numeric name='Temperature' momentary='true' automaticReadout='true' value='23.4' unit='°C'/>
+ <numeric name='Temperature' momentary='true' automaticReadout='true' value='23.4' unit='°C'/>
</timestamp>
</node>
</fields>
- </message>
+ </message>
""")
def testRequestRejectAuth(self):
@@ -73,7 +73,7 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- self.xmpp['xep_0323']._set_authenticated("darth@deathstar.com");
+ self.xmpp['xep_0323']._set_authenticated("darth@deathstar.com")
self.recv("""
<iq type='get'
@@ -85,7 +85,7 @@ class TestStreamSensorData(SleekTest):
""")
self.send("""
- <iq type='error'
+ <iq type='error'
from='device@clayster.com'
to='master@clayster.com/amr'
id='4'>
@@ -101,8 +101,8 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- myDevice = Device("Device44");
- self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5);
+ myDevice = Device("Device44")
+ self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5)
print("."),
@@ -118,7 +118,7 @@ class TestStreamSensorData(SleekTest):
""")
self.send("""
- <iq type='error'
+ <iq type='error'
from='device@clayster.com'
to='master@clayster.com/amr'
id='77'>
@@ -142,7 +142,7 @@ class TestStreamSensorData(SleekTest):
""")
self.send("""
- <iq type='result'
+ <iq type='result'
from='device@clayster.com'
to='master@clayster.com/amr'
id='8'>
@@ -157,11 +157,11 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- myDevice = Device("Device44");
- myDevice._add_field(name='Voltage', typename="numeric", unit="V");
- myDevice._add_field_timestamp_data(name="Voltage", value="230.4", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"});
+ myDevice = Device("Device44")
+ myDevice._add_field(name='Voltage', typename="numeric", unit="V")
+ myDevice._add_field_timestamp_data(name="Voltage", value="230.4", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"})
- self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5);
+ self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5)
print("."),
@@ -177,7 +177,7 @@ class TestStreamSensorData(SleekTest):
""")
self.send("""
- <iq type='error'
+ <iq type='error'
from='device@clayster.com'
to='master@clayster.com/amr'
id='7'>
@@ -201,7 +201,7 @@ class TestStreamSensorData(SleekTest):
""")
self.send("""
- <iq type='result'
+ <iq type='result'
from='device@clayster.com'
to='master@clayster.com/amr'
id='8'>
@@ -215,11 +215,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'>
<node nodeId='Device44'>
<timestamp value='2000-01-01T00:01:02'>
- <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/>
+ <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/>
</timestamp>
</node>
</fields>
- </message>
+ </message>
""")
self.send("""
@@ -227,7 +227,7 @@ class TestStreamSensorData(SleekTest):
to='master@clayster.com/amr'>
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='7' done='true'>
</fields>
- </message>
+ </message>
""")
def testRequestMultiTimestampSingleField(self):
@@ -236,15 +236,15 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- myDevice = Device("Device44");
- myDevice._add_field(name='Voltage', typename="numeric", unit="V");
- myDevice._add_field_timestamp_data(name="Voltage", value="230.4", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"});
- myDevice._add_field(name='Current', typename="numeric", unit="A");
- myDevice._add_field(name='Height', typename="string");
- myDevice._add_field_timestamp_data(name="Voltage", value="230.6", timestamp="2000-01-01T01:01:02");
- myDevice._add_field_timestamp_data(name="Height", value="115 m", timestamp="2000-01-01T01:01:02", flags={"invoiced": "true"});
+ myDevice = Device("Device44")
+ myDevice._add_field(name='Voltage', typename="numeric", unit="V")
+ myDevice._add_field_timestamp_data(name="Voltage", value="230.4", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"})
+ myDevice._add_field(name='Current', typename="numeric", unit="A")
+ myDevice._add_field(name='Height', typename="string")
+ myDevice._add_field_timestamp_data(name="Voltage", value="230.6", timestamp="2000-01-01T01:01:02")
+ myDevice._add_field_timestamp_data(name="Height", value="115 m", timestamp="2000-01-01T01:01:02", flags={"invoiced": "true"})
- self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5);
+ self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5)
print("."),
@@ -260,7 +260,7 @@ class TestStreamSensorData(SleekTest):
""")
self.send("""
- <iq type='result'
+ <iq type='result'
from='device@clayster.com'
to='master@clayster.com/amr'
id='8'>
@@ -274,11 +274,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'>
<node nodeId='Device44'>
<timestamp value='2000-01-01T00:01:02'>
- <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/>
+ <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/>
</timestamp>
</node>
</fields>
- </message>
+ </message>
""")
self.send("""
@@ -287,11 +287,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'>
<node nodeId='Device44'>
<timestamp value='2000-01-01T01:01:02'>
- <numeric name='Voltage' value='230.6' unit='V'/>
+ <numeric name='Voltage' value='230.6' unit='V'/>
</timestamp>
</node>
</fields>
- </message>
+ </message>
""")
self.send("""
@@ -299,7 +299,7 @@ class TestStreamSensorData(SleekTest):
to='master@clayster.com/amr'>
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='7' done='true'>
</fields>
- </message>
+ </message>
""")
def testRequestMultiTimestampAllFields(self):
@@ -308,15 +308,15 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- myDevice = Device("Device44");
- myDevice._add_field(name='Voltage', typename="numeric", unit="V");
- myDevice._add_field_timestamp_data(name="Voltage", value="230.4", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"});
- myDevice._add_field(name='Current', typename="numeric", unit="A");
- myDevice._add_field(name='Height', typename="string");
- myDevice._add_field_timestamp_data(name="Voltage", value="230.6", timestamp="2000-01-01T01:01:02");
- myDevice._add_field_timestamp_data(name="Height", value="115 m", timestamp="2000-01-01T01:01:02", flags={"invoiced": "true"});
+ myDevice = Device("Device44")
+ myDevice._add_field(name='Voltage', typename="numeric", unit="V")
+ myDevice._add_field_timestamp_data(name="Voltage", value="230.4", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"})
+ myDevice._add_field(name='Current', typename="numeric", unit="A")
+ myDevice._add_field(name='Height', typename="string")
+ myDevice._add_field_timestamp_data(name="Voltage", value="230.6", timestamp="2000-01-01T01:01:02")
+ myDevice._add_field_timestamp_data(name="Height", value="115 m", timestamp="2000-01-01T01:01:02", flags={"invoiced": "true"})
- self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5);
+ self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5)
print("."),
@@ -330,7 +330,7 @@ class TestStreamSensorData(SleekTest):
""")
self.send("""
- <iq type='result'
+ <iq type='result'
from='device@clayster.com'
to='master@clayster.com/amr'
id='8'>
@@ -344,11 +344,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'>
<node nodeId='Device44'>
<timestamp value='2000-01-01T00:01:02'>
- <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/>
+ <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/>
</timestamp>
</node>
</fields>
- </message>
+ </message>
""")
self.send("""
@@ -357,12 +357,12 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'>
<node nodeId='Device44'>
<timestamp value='2000-01-01T01:01:02'>
- <numeric name='Voltage' value='230.6' unit='V'/>
- <string name='Height' invoiced='true' value='115 m'/>
+ <numeric name='Voltage' value='230.6' unit='V'/>
+ <string name='Height' invoiced='true' value='115 m'/>
</timestamp>
</node>
</fields>
- </message>
+ </message>
""")
self.send("""
@@ -370,7 +370,7 @@ class TestStreamSensorData(SleekTest):
to='master@clayster.com/amr'>
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='7' done='true'>
</fields>
- </message>
+ </message>
""")
def testRequestAPI(self):
@@ -379,7 +379,7 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", callback=None);
+ self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", callback=None)
self.send("""
<iq type='get'
@@ -390,7 +390,7 @@ class TestStreamSensorData(SleekTest):
</iq>
""")
- self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33', 'Device22'], callback=None);
+ self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33', 'Device22'], callback=None)
self.send("""
<iq type='get'
@@ -404,7 +404,7 @@ class TestStreamSensorData(SleekTest):
</iq>
""")
- self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", fields=['Temperature', 'Voltage'], callback=None);
+ self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", fields=['Temperature', 'Voltage'], callback=None)
self.send("""
<iq type='get'
@@ -424,13 +424,13 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- results = [];
+ results = []
def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None):
if (result == "rejected") and (error_msg == "Invalid device Device22"):
- results.append("rejected");
+ results.append("rejected")
- self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33', 'Device22'], callback=my_callback);
+ self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33', 'Device22'], callback=my_callback)
self.send("""
<iq type='get'
@@ -445,20 +445,20 @@ class TestStreamSensorData(SleekTest):
""")
self.recv("""
- <iq type='error'
+ <iq type='error'
from='you@google.com'
to='tester@localhost'
id='1'>
<rejected xmlns='urn:xmpp:iot:sensordata' seqnr='1'>
<error>Invalid device Device22</error>
</rejected>
- </iq>
+ </iq>
""")
time.sleep(.1)
- self.failUnless(results == ["rejected"],
- "Rejected callback was not properly executed");
+ self.failUnless(results == ["rejected"],
+ "Rejected callback was not properly executed")
def testRequestAcceptedAPI(self):
@@ -466,12 +466,12 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- results = [];
+ results = []
def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None):
- results.append(result);
+ results.append(result)
- self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33', 'Device22'], callback=my_callback);
+ self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33', 'Device22'], callback=my_callback)
self.send("""
<iq type='get'
@@ -486,18 +486,18 @@ class TestStreamSensorData(SleekTest):
""")
self.recv("""
- <iq type='result'
+ <iq type='result'
from='you@google.com'
to='tester@localhost'
id='1'>
<accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/>
- </iq>
+ </iq>
""")
time.sleep(.1)
- self.failUnless(results == ["accepted"],
- "Accepted callback was not properly executed");
+ self.failUnless(results == ["accepted"],
+ "Accepted callback was not properly executed")
def testRequestFieldsAPI(self):
@@ -505,25 +505,25 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- results = [];
- callback_data = {};
+ results = []
+ callback_data = {}
def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None):
- results.append(result);
+ results.append(result)
if result == "fields":
- callback_data["nodeId"] = nodeId;
- callback_data["timestamp"] = timestamp;
- callback_data["error_msg"] = error_msg;
+ callback_data["nodeId"] = nodeId
+ callback_data["timestamp"] = timestamp
+ callback_data["error_msg"] = error_msg
for f in fields:
- callback_data["field_" + f['name']] = f;
+ callback_data["field_" + f['name']] = f
t1= threading.Thread(name="request_data",
target=self.xmpp['xep_0323'].request_data,
- kwargs={"from_jid": "tester@localhost",
- "to_jid": "you@google.com",
- "nodeIds": ['Device33'],
- "callback": my_callback});
- t1.start();
+ kwargs={"from_jid": "tester@localhost",
+ "to_jid": "you@google.com",
+ "nodeIds": ['Device33'],
+ "callback": my_callback})
+ t1.start()
#self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33'], callback=my_callback);
self.send("""
@@ -538,12 +538,12 @@ class TestStreamSensorData(SleekTest):
""")
self.recv("""
- <iq type='result'
+ <iq type='result'
from='you@google.com'
to='tester@localhost'
id='1'>
<accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/>
- </iq>
+ </iq>
""")
self.recv("""
@@ -552,42 +552,42 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'>
<node nodeId='Device33'>
<timestamp value='2000-01-01T00:01:02'>
- <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/>
- <boolean name='TestBool' value='true'/>
+ <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/>
+ <boolean name='TestBool' value='true'/>
</timestamp>
</node>
</fields>
- </message>
+ </message>
""")
self.recv("""
<message from='you@google.com'
to='tester@localhost'>
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'/>
- </message>
+ </message>
""")
- t1.join();
+ t1.join()
time.sleep(.5)
- self.failUnlessEqual(results, ["accepted","fields","done"]);
+ self.failUnlessEqual(results, ["accepted","fields","done"])
# self.assertIn("nodeId", callback_data);
self.assertTrue("nodeId" in callback_data)
- self.failUnlessEqual(callback_data["nodeId"], "Device33");
+ self.failUnlessEqual(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.assertTrue("timestamp" in callback_data)
+ self.failUnlessEqual(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.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.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.assertTrue("field_TestBool" in callback_data)
+ self.failUnlessEqual(callback_data["field_TestBool"], {"name": "TestBool", "value": "true", "typename": "boolean" })
def testServiceDiscoveryClient(self):
self.stream_start(mode='client',
plugins=['xep_0030',
- 'xep_0323']);
+ 'xep_0323'])
self.recv("""
<iq type='get'
@@ -605,14 +605,14 @@ class TestStreamSensorData(SleekTest):
<query xmlns='http://jabber.org/protocol/disco#info'>
<identity category='client' type='bot'/>
<feature var='urn:xmpp:iot:sensordata'/>
- </query>
- </iq>
+ </query>
+ </iq>
""")
def testServiceDiscoveryComponent(self):
self.stream_start(mode='component',
plugins=['xep_0030',
- 'xep_0323']);
+ 'xep_0323'])
self.recv("""
<iq type='get'
@@ -631,8 +631,8 @@ class TestStreamSensorData(SleekTest):
<query xmlns='http://jabber.org/protocol/disco#info'>
<identity category='component' type='generic'/>
<feature var='urn:xmpp:iot:sensordata'/>
- </query>
- </iq>
+ </query>
+ </iq>
""")
def testRequestTimeout(self):
@@ -641,23 +641,23 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- results = [];
- callback_data = {};
+ results = []
+ callback_data = {}
def my_callback(from_jid, result, nodeId=None, timestamp=None, error_msg=None):
- results.append(result);
+ results.append(result)
if result == "failure":
- callback_data["nodeId"] = nodeId;
- callback_data["timestamp"] = timestamp;
- callback_data["error_msg"] = error_msg;
+ callback_data["nodeId"] = nodeId
+ callback_data["timestamp"] = timestamp
+ callback_data["error_msg"] = error_msg
t1= threading.Thread(name="request_data",
target=self.xmpp['xep_0323'].request_data,
- kwargs={"from_jid": "tester@localhost",
- "to_jid": "you@google.com",
- "nodeIds": ['Device33'],
- "callback": my_callback});
- t1.start();
+ kwargs={"from_jid": "tester@localhost",
+ "to_jid": "you@google.com",
+ "nodeIds": ['Device33'],
+ "callback": my_callback})
+ t1.start()
self.send("""
<iq type='get'
@@ -671,12 +671,12 @@ class TestStreamSensorData(SleekTest):
""")
self.recv("""
- <iq type='result'
+ <iq type='result'
from='you@google.com'
to='tester@localhost'
id='1'>
<accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/>
- </iq>
+ </iq>
""")
self.recv("""
@@ -688,31 +688,31 @@ class TestStreamSensorData(SleekTest):
</message>
""")
- t1.join();
+ t1.join()
time.sleep(.5)
- self.failUnlessEqual(results, ["accepted","failure"]);
+ self.failUnlessEqual(results, ["accepted","failure"])
# self.assertIn("nodeId", callback_data);
- self.assertTrue("nodeId" in callback_data);
- self.failUnlessEqual(callback_data["nodeId"], "Device33");
+ self.assertTrue("nodeId" in callback_data)
+ self.failUnlessEqual(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.assertTrue("timestamp" in callback_data)
+ self.failUnlessEqual(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.assertTrue("error_msg" in callback_data)
+ self.failUnlessEqual(callback_data["error_msg"], "Timeout.")
def testDelayedRequest(self):
self.stream_start(mode='component',
plugins=['xep_0030',
'xep_0323'])
- myDevice = Device("Device22");
- myDevice._add_field(name="Temperature", typename="numeric", unit="°C");
+ myDevice = Device("Device22")
+ myDevice._add_field(name="Temperature", typename="numeric", unit="°C")
myDevice._set_momentary_timestamp("2013-03-07T16:24:30")
- myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"});
+ myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"})
- self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5);
+ self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5)
dtnow = datetime.datetime.now()
ts_2sec = datetime.timedelta(0,2)
@@ -729,7 +729,7 @@ class TestStreamSensorData(SleekTest):
""")
self.send("""
- <iq type='result'
+ <iq type='result'
from='device@clayster.com'
to='master@clayster.com/amr'
id='1'>
@@ -743,7 +743,7 @@ class TestStreamSensorData(SleekTest):
<message from='device@clayster.com'
to='master@clayster.com/amr'>
<started xmlns='urn:xmpp:iot:sensordata' seqnr='1' />
- </message>
+ </message>
""")
self.send("""
@@ -752,11 +752,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'>
<node nodeId='Device22'>
<timestamp value='2013-03-07T16:24:30'>
- <numeric name='Temperature' momentary='true' automaticReadout='true' value='23.4' unit='°C'/>
+ <numeric name='Temperature' momentary='true' automaticReadout='true' value='23.4' unit='°C'/>
</timestamp>
</node>
</fields>
- </message>
+ </message>
""")
def testDelayedRequestFail(self):
@@ -764,12 +764,12 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- myDevice = Device("Device22");
- myDevice._add_field(name="Temperature", typename="numeric", unit="°C");
+ myDevice = Device("Device22")
+ myDevice._add_field(name="Temperature", typename="numeric", unit="°C")
myDevice._set_momentary_timestamp("2013-03-07T16:24:30")
- myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"});
+ myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"})
- self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5);
+ self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5)
dtnow = datetime.datetime.now()
ts_2sec = datetime.timedelta(0,2)
@@ -792,7 +792,7 @@ class TestStreamSensorData(SleekTest):
xml_stanza['rejected']['error'] = error_text
self._filtered_stanza_check("""
- <iq type='error'
+ <iq type='error'
from='device@clayster.com'
to='master@clayster.com/amr'
id='1'>
@@ -825,13 +825,13 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- myDevice = Device("Device44");
- myDevice._add_field(name='Voltage', typename="numeric", unit="V");
- myDevice._add_field_timestamp_data(name="Voltage", value="230.1", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"});
- myDevice._add_field_timestamp_data(name="Voltage", value="230.2", timestamp="2000-02-01T00:01:02", flags={"invoiced": "true"});
- myDevice._add_field_timestamp_data(name="Voltage", value="230.3", timestamp="2000-03-01T00:01:02", flags={"invoiced": "true"});
+ myDevice = Device("Device44")
+ myDevice._add_field(name='Voltage', typename="numeric", unit="V")
+ myDevice._add_field_timestamp_data(name="Voltage", value="230.1", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"})
+ myDevice._add_field_timestamp_data(name="Voltage", value="230.2", timestamp="2000-02-01T00:01:02", flags={"invoiced": "true"})
+ myDevice._add_field_timestamp_data(name="Voltage", value="230.3", timestamp="2000-03-01T00:01:02", flags={"invoiced": "true"})
- self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5);
+ self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5)
print("."),
@@ -847,7 +847,7 @@ class TestStreamSensorData(SleekTest):
""")
self.send("""
- <iq type='result'
+ <iq type='result'
from='device@clayster.com'
to='master@clayster.com/amr'
id='6'>
@@ -861,11 +861,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'>
<node nodeId='Device44'>
<timestamp value='2000-02-01T00:01:02'>
- <numeric name='Voltage' invoiced='true' value='230.2' unit='V'/>
+ <numeric name='Voltage' invoiced='true' value='230.2' unit='V'/>
</timestamp>
</node>
</fields>
- </message>
+ </message>
""")
self.send("""
@@ -874,11 +874,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'>
<node nodeId='Device44'>
<timestamp value='2000-03-01T00:01:02'>
- <numeric name='Voltage' invoiced='true' value='230.3' unit='V'/>
+ <numeric name='Voltage' invoiced='true' value='230.3' unit='V'/>
</timestamp>
</node>
</fields>
- </message>
+ </message>
""")
self.send("""
@@ -886,7 +886,7 @@ class TestStreamSensorData(SleekTest):
to='master@clayster.com/amr'>
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='6' done='true'>
</fields>
- </message>
+ </message>
""")
def testRequestFieldTo(self):
@@ -895,13 +895,13 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- myDevice = Device("Device44");
- myDevice._add_field(name='Voltage', typename="numeric", unit="V");
- myDevice._add_field_timestamp_data(name="Voltage", value="230.1", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"});
- myDevice._add_field_timestamp_data(name="Voltage", value="230.2", timestamp="2000-02-01T00:01:02", flags={"invoiced": "true"});
- myDevice._add_field_timestamp_data(name="Voltage", value="230.3", timestamp="2000-03-01T00:01:02", flags={"invoiced": "true"});
+ myDevice = Device("Device44")
+ myDevice._add_field(name='Voltage', typename="numeric", unit="V")
+ myDevice._add_field_timestamp_data(name="Voltage", value="230.1", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"})
+ myDevice._add_field_timestamp_data(name="Voltage", value="230.2", timestamp="2000-02-01T00:01:02", flags={"invoiced": "true"})
+ myDevice._add_field_timestamp_data(name="Voltage", value="230.3", timestamp="2000-03-01T00:01:02", flags={"invoiced": "true"})
- self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5);
+ self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5)
print("."),
@@ -917,7 +917,7 @@ class TestStreamSensorData(SleekTest):
""")
self.send("""
- <iq type='result'
+ <iq type='result'
from='device@clayster.com'
to='master@clayster.com/amr'
id='6'>
@@ -931,11 +931,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'>
<node nodeId='Device44'>
<timestamp value='2000-01-01T00:01:02'>
- <numeric name='Voltage' invoiced='true' value='230.1' unit='V'/>
+ <numeric name='Voltage' invoiced='true' value='230.1' unit='V'/>
</timestamp>
</node>
</fields>
- </message>
+ </message>
""")
self.send("""
@@ -944,11 +944,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'>
<node nodeId='Device44'>
<timestamp value='2000-02-01T00:01:02'>
- <numeric name='Voltage' invoiced='true' value='230.2' unit='V'/>
+ <numeric name='Voltage' invoiced='true' value='230.2' unit='V'/>
</timestamp>
</node>
</fields>
- </message>
+ </message>
""")
self.send("""
@@ -956,7 +956,7 @@ class TestStreamSensorData(SleekTest):
to='master@clayster.com/amr'>
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='6' done='true'>
</fields>
- </message>
+ </message>
""")
def testRequestFieldFromTo(self):
@@ -965,13 +965,13 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- myDevice = Device("Device44");
- myDevice._add_field(name='Voltage', typename="numeric", unit="V");
- myDevice._add_field_timestamp_data(name="Voltage", value="230.1", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"});
- myDevice._add_field_timestamp_data(name="Voltage", value="230.2", timestamp="2000-02-01T00:01:02", flags={"invoiced": "true"});
- myDevice._add_field_timestamp_data(name="Voltage", value="230.3", timestamp="2000-03-01T00:01:02", flags={"invoiced": "true"});
+ myDevice = Device("Device44")
+ myDevice._add_field(name='Voltage', typename="numeric", unit="V")
+ myDevice._add_field_timestamp_data(name="Voltage", value="230.1", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"})
+ myDevice._add_field_timestamp_data(name="Voltage", value="230.2", timestamp="2000-02-01T00:01:02", flags={"invoiced": "true"})
+ myDevice._add_field_timestamp_data(name="Voltage", value="230.3", timestamp="2000-03-01T00:01:02", flags={"invoiced": "true"})
- self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5);
+ self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5)
print("."),
@@ -987,7 +987,7 @@ class TestStreamSensorData(SleekTest):
""")
self.send("""
- <iq type='result'
+ <iq type='result'
from='device@clayster.com'
to='master@clayster.com/amr'
id='6'>
@@ -1001,11 +1001,11 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'>
<node nodeId='Device44'>
<timestamp value='2000-02-01T00:01:02'>
- <numeric name='Voltage' invoiced='true' value='230.2' unit='V'/>
+ <numeric name='Voltage' invoiced='true' value='230.2' unit='V'/>
</timestamp>
</node>
</fields>
- </message>
+ </message>
""")
self.send("""
@@ -1013,7 +1013,7 @@ class TestStreamSensorData(SleekTest):
to='master@clayster.com/amr'>
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='6' done='true'>
</fields>
- </message>
+ </message>
""")
def testDelayedRequestClient(self):
@@ -1021,25 +1021,25 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- results = [];
- callback_data = {};
+ results = []
+ callback_data = {}
def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None):
- results.append(result);
+ results.append(result)
if result == "fields":
- callback_data["nodeId"] = nodeId;
- callback_data["timestamp"] = timestamp;
- callback_data["error_msg"] = error_msg;
+ callback_data["nodeId"] = nodeId
+ callback_data["timestamp"] = timestamp
+ callback_data["error_msg"] = error_msg
for f in fields:
- callback_data["field_" + f['name']] = f;
+ callback_data["field_" + f['name']] = f
t1= threading.Thread(name="request_data",
target=self.xmpp['xep_0323'].request_data,
- kwargs={"from_jid": "tester@localhost",
- "to_jid": "you@google.com",
- "nodeIds": ['Device33'],
- "callback": my_callback});
- t1.start();
+ kwargs={"from_jid": "tester@localhost",
+ "to_jid": "you@google.com",
+ "nodeIds": ['Device33'],
+ "callback": my_callback})
+ t1.start()
#self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33'], callback=my_callback);
self.send("""
@@ -1054,20 +1054,20 @@ class TestStreamSensorData(SleekTest):
""")
self.recv("""
- <iq type='result'
+ <iq type='result'
from='you@google.com'
to='tester@localhost'
id='1'>
<accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1' queued='true'/>
- </iq>
+ </iq>
""")
self.recv("""
<message from='device@clayster.com'
to='master@clayster.com/amr'>
<started xmlns='urn:xmpp:iot:sensordata' seqnr='1' />
- </message>
- """)
+ </message>
+ """)
self.recv("""
<message from='you@google.com'
@@ -1075,37 +1075,37 @@ class TestStreamSensorData(SleekTest):
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'>
<node nodeId='Device33'>
<timestamp value='2000-01-01T00:01:02'>
- <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/>
- <boolean name='TestBool' value='true'/>
+ <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/>
+ <boolean name='TestBool' value='true'/>
</timestamp>
</node>
</fields>
- </message>
+ </message>
""")
self.recv("""
<message from='you@google.com'
to='tester@localhost'>
<fields xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'/>
- </message>
+ </message>
""")
- t1.join();
+ t1.join()
time.sleep(.5)
- self.failUnlessEqual(results, ["queued","started","fields","done"]);
+ self.failUnlessEqual(results, ["queued","started","fields","done"])
# self.assertIn("nodeId", callback_data);
- self.assertTrue("nodeId" in callback_data);
- self.failUnlessEqual(callback_data["nodeId"], "Device33");
+ self.assertTrue("nodeId" in callback_data)
+ self.failUnlessEqual(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.assertTrue("timestamp" in callback_data)
+ self.failUnlessEqual(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.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.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.assertTrue("field_TestBool" in callback_data)
+ self.failUnlessEqual(callback_data["field_TestBool"], {"name": "TestBool", "value": "true", "typename": "boolean" })
def testRequestFieldsCancelAPI(self):
@@ -1114,12 +1114,12 @@ class TestStreamSensorData(SleekTest):
plugins=['xep_0030',
'xep_0323'])
- results = [];
+ results = []
def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None):
- results.append(result);
+ results.append(result)
- session = self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33'], callback=my_callback);
+ session = self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33'], callback=my_callback)
self.send("""
<iq type='get'
@@ -1133,15 +1133,15 @@ class TestStreamSensorData(SleekTest):
""")
self.recv("""
- <iq type='result'
+ <iq type='result'
from='you@google.com'
to='tester@localhost'
id='1'>
<accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/>
- </iq>
+ </iq>
""")
- self.xmpp['xep_0323'].cancel_request(session=session);
+ self.xmpp['xep_0323'].cancel_request(session=session)
self.send("""
<iq type='get'
@@ -1163,19 +1163,19 @@ class TestStreamSensorData(SleekTest):
time.sleep(.5)
- self.failUnlessEqual(results, ["accepted","cancelled"]);
+ self.failUnlessEqual(results, ["accepted","cancelled"])
def testDelayedRequestCancel(self):
self.stream_start(mode='component',
plugins=['xep_0030',
'xep_0323'])
- myDevice = Device("Device22");
- myDevice._add_field(name="Temperature", typename="numeric", unit="°C");
+ myDevice = Device("Device22")
+ myDevice._add_field(name="Temperature", typename="numeric", unit="°C")
myDevice._set_momentary_timestamp("2013-03-07T16:24:30")
- myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"});
+ myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"})
- self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5);
+ self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5)
dtnow = datetime.datetime.now()
ts_2sec = datetime.timedelta(0,2)
@@ -1192,7 +1192,7 @@ class TestStreamSensorData(SleekTest):
""")
self.send("""
- <iq type='result'
+ <iq type='result'
from='device@clayster.com'
to='master@clayster.com/amr'
id='1'>
diff --git a/tests/test_stream_xep_0325.py b/tests/test_stream_xep_0325.py
index 4b3250fc..2ebdd121 100644
--- a/tests/test_stream_xep_0325.py
+++ b/tests/test_stream_xep_0325.py
@@ -28,7 +28,7 @@ class TestStreamControl(SleekTest):
pass
def _time_now(self):
- return datetime.datetime.now().replace(microsecond=0).isoformat();
+ return datetime.datetime.now().replace(microsecond=0).isoformat()
def tearDown(self):
self.stream_close()
@@ -38,10 +38,10 @@ class TestStreamControl(SleekTest):
plugins=['xep_0030',
'xep_0325'])
- myDevice = Device("Device22");
- myDevice._add_control_field(name="Temperature", typename="int", value="15");
+ myDevice = Device("Device22")
+ myDevice._add_control_field(name="Temperature", typename="int", value="15")
- self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5);
+ self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5)
self.recv("""
<iq type='set'
@@ -60,26 +60,26 @@ class TestStreamControl(SleekTest):
to='master@clayster.com/amr'
id='1'>
<setResponse xmlns='urn:xmpp:iot:control' responseCode="OK" />
- </iq>
+ </iq>
""")
- self.assertEqual(myDevice._get_field_value("Temperature"), "17");
+ self.assertEqual(myDevice._get_field_value("Temperature"), "17")
def testRequestSetMulti(self):
self.stream_start(mode='component',
plugins=['xep_0030',
'xep_0325'])
- myDevice = Device("Device22");
- myDevice._add_control_field(name="Temperature", typename="int", value="15");
- myDevice._add_control_field(name="Startup", typename="date", value="2013-01-03");
+ myDevice = Device("Device22")
+ myDevice._add_control_field(name="Temperature", typename="int", value="15")
+ myDevice._add_control_field(name="Startup", typename="date", value="2013-01-03")
- myDevice2 = Device("Device23");
- myDevice2._add_control_field(name="Temperature", typename="int", value="19");
- myDevice2._add_control_field(name="Startup", typename="date", value="2013-01-09");
+ myDevice2 = Device("Device23")
+ myDevice2._add_control_field(name="Temperature", typename="int", value="19")
+ myDevice2._add_control_field(name="Startup", typename="date", value="2013-01-09")
- self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5);
- self.xmpp['xep_0325'].register_node(nodeId="Device23", device=myDevice2, commTimeout=0.5);
+ self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5)
+ self.xmpp['xep_0325'].register_node(nodeId="Device23", device=myDevice2, commTimeout=0.5)
self.recv("""
<iq type='set'
@@ -99,11 +99,11 @@ class TestStreamControl(SleekTest):
to='master@clayster.com/amr'
id='1'>
<setResponse xmlns='urn:xmpp:iot:control' responseCode="OK" />
- </iq>
+ </iq>
""")
- self.assertEqual(myDevice._get_field_value("Temperature"), "17");
- self.assertEqual(myDevice2._get_field_value("Temperature"), "19");
+ self.assertEqual(myDevice._get_field_value("Temperature"), "17")
+ self.assertEqual(myDevice2._get_field_value("Temperature"), "19")
self.recv("""
<iq type='set'
@@ -125,23 +125,23 @@ class TestStreamControl(SleekTest):
to='master@clayster.com/amr'
id='2'>
<setResponse xmlns='urn:xmpp:iot:control' responseCode="OK" />
- </iq>
+ </iq>
""")
- self.assertEqual(myDevice._get_field_value("Temperature"), "20");
- self.assertEqual(myDevice2._get_field_value("Temperature"), "20");
- self.assertEqual(myDevice._get_field_value("Startup"), "2013-02-01");
- self.assertEqual(myDevice2._get_field_value("Startup"), "2013-02-01");
+ self.assertEqual(myDevice._get_field_value("Temperature"), "20")
+ self.assertEqual(myDevice2._get_field_value("Temperature"), "20")
+ self.assertEqual(myDevice._get_field_value("Startup"), "2013-02-01")
+ self.assertEqual(myDevice2._get_field_value("Startup"), "2013-02-01")
def testRequestSetFail(self):
self.stream_start(mode='component',
plugins=['xep_0030',
'xep_0325'])
- myDevice = Device("Device23");
- myDevice._add_control_field(name="Temperature", typename="int", value="15");
+ myDevice = Device("Device23")
+ myDevice._add_control_field(name="Temperature", typename="int", value="15")
- self.xmpp['xep_0325'].register_node(nodeId="Device23", device=myDevice, commTimeout=0.5);
+ self.xmpp['xep_0325'].register_node(nodeId="Device23", device=myDevice, commTimeout=0.5)
self.recv("""
<iq type='set'
@@ -163,24 +163,24 @@ class TestStreamControl(SleekTest):
<parameter name='Voltage' />
<error var='Output'>Invalid field Voltage</error>
</setResponse>
- </iq>
+ </iq>
""")
- self.assertEqual(myDevice._get_field_value("Temperature"), "15");
- self.assertFalse(myDevice.has_control_field("Voltage", "int"));
+ self.assertEqual(myDevice._get_field_value("Temperature"), "15")
+ self.assertFalse(myDevice.has_control_field("Voltage", "int"))
def testDirectSetOk(self):
self.stream_start(mode='component',
plugins=['xep_0030',
'xep_0325'])
- myDevice = Device("Device22");
- myDevice._add_control_field(name="Temperature", typename="int", value="15");
+ myDevice = Device("Device22")
+ myDevice._add_control_field(name="Temperature", typename="int", value="15")
- self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5);
+ self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5)
self.recv("""
- <message
+ <message
from='master@clayster.com/amr'
to='device@clayster.com'>
<set xmlns='urn:xmpp:iot:control'>
@@ -191,20 +191,20 @@ class TestStreamControl(SleekTest):
time.sleep(.5)
- self.assertEqual(myDevice._get_field_value("Temperature"), "17");
+ self.assertEqual(myDevice._get_field_value("Temperature"), "17")
def testDirectSetFail(self):
self.stream_start(mode='component',
plugins=['xep_0030',
'xep_0325'])
- myDevice = Device("Device22");
- myDevice._add_control_field(name="Temperature", typename="int", value="15");
+ myDevice = Device("Device22")
+ myDevice._add_control_field(name="Temperature", typename="int", value="15")
- self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5);
+ self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5)
self.recv("""
- <message
+ <message
from='master@clayster.com/amr'
to='device@clayster.com'>
<set xmlns='urn:xmpp:iot:control'>
@@ -215,8 +215,8 @@ class TestStreamControl(SleekTest):
time.sleep(.5)
- self.assertEqual(myDevice._get_field_value("Temperature"), "15");
- self.assertFalse(myDevice.has_control_field("Voltage", "int"));
+ self.assertEqual(myDevice._get_field_value("Temperature"), "15")
+ self.assertFalse(myDevice.has_control_field("Voltage", "int"))
def testRequestSetOkAPI(self):
@@ -225,16 +225,16 @@ class TestStreamControl(SleekTest):
plugins=['xep_0030',
'xep_0325'])
- results = [];
+ results = []
def my_callback(from_jid, result, nodeIds=None, fields=None, error_msg=None):
- results.append(result);
+ results.append(result)
fields = []
fields.append(("Temperature", "double", "20.5"))
fields.append(("TemperatureAlarmSetting", "string", "High"))
- self.xmpp['xep_0325'].set_request(from_jid="tester@localhost", to_jid="you@google.com", fields=fields, nodeIds=['Device33', 'Device22'], callback=my_callback);
+ self.xmpp['xep_0325'].set_request(from_jid="tester@localhost", to_jid="you@google.com", fields=fields, nodeIds=['Device33', 'Device22'], callback=my_callback)
self.send("""
<iq type='set'
@@ -256,12 +256,12 @@ class TestStreamControl(SleekTest):
to='tester@localhost'
id='1'>
<setResponse xmlns='urn:xmpp:iot:control' responseCode="OK" />
- </iq>
+ </iq>
""")
time.sleep(.5)
- self.assertEqual(results, ["OK"]);
+ self.assertEqual(results, ["OK"])
def testRequestSetErrorAPI(self):
@@ -269,16 +269,16 @@ class TestStreamControl(SleekTest):
plugins=['xep_0030',
'xep_0325'])
- results = [];
+ results = []
def my_callback(from_jid, result, nodeIds=None, fields=None, error_msg=None):
- results.append(result);
+ results.append(result)
fields = []
fields.append(("Temperature", "double", "20.5"))
fields.append(("TemperatureAlarmSetting", "string", "High"))
- self.xmpp['xep_0325'].set_request(from_jid="tester@localhost", to_jid="you@google.com", fields=fields, nodeIds=['Device33', 'Device22'], callback=my_callback);
+ self.xmpp['xep_0325'].set_request(from_jid="tester@localhost", to_jid="you@google.com", fields=fields, nodeIds=['Device33', 'Device22'], callback=my_callback)
self.send("""
<iq type='set'
@@ -302,17 +302,17 @@ class TestStreamControl(SleekTest):
<setResponse xmlns='urn:xmpp:iot:control' responseCode="OtherError" >
<error var='Temperature'>Sensor error</error>
</setResponse>
- </iq>
+ </iq>
""")
time.sleep(.5)
- self.assertEqual(results, ["OtherError"]);
+ self.assertEqual(results, ["OtherError"])
def testServiceDiscoveryClient(self):
self.stream_start(mode='client',
plugins=['xep_0030',
- 'xep_0325']);
+ 'xep_0325'])
self.recv("""
<iq type='get'
@@ -330,14 +330,14 @@ class TestStreamControl(SleekTest):
<query xmlns='http://jabber.org/protocol/disco#info'>
<identity category='client' type='bot'/>
<feature var='urn:xmpp:iot:control'/>
- </query>
- </iq>
+ </query>
+ </iq>
""")
def testServiceDiscoveryComponent(self):
self.stream_start(mode='component',
plugins=['xep_0030',
- 'xep_0325']);
+ 'xep_0325'])
self.recv("""
<iq type='get'
@@ -356,8 +356,8 @@ class TestStreamControl(SleekTest):
<query xmlns='http://jabber.org/protocol/disco#info'>
<identity category='component' type='generic'/>
<feature var='urn:xmpp:iot:control'/>
- </query>
- </iq>
+ </query>
+ </iq>
""")
diff --git a/tox.ini b/tox.ini
index 91617941..e0e79ff4 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py26,py27,py31,py32,py33
+envlist = py26,py27,py34
[testenv]
deps = nose
commands = nosetests --where=tests --exclude=live -i sleektest.py