From ea3d39b50e303db191cefad194a65b48d178e3ef Mon Sep 17 00:00:00 2001 From: ekini Date: Mon, 23 Jul 2012 15:39:07 +0700 Subject: added xep-0138 support (compression) --- sleekxmpp/plugins/xep_0138.py | 148 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 sleekxmpp/plugins/xep_0138.py 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) -- cgit v1.2.3 From 75a18b5ffe9057e234403e54a16e24bb2cc7ba25 Mon Sep 17 00:00:00 2001 From: Joe Hildebrand Date: Mon, 29 Oct 2012 10:03:32 -0600 Subject: Allow IQ timeouts to be asynchronous, by passing a timeout_callback parameter to send(). An example modification of disco is included. If this approach is approved, I'll go through and update the other plugins. --- sleekxmpp/plugins/xep_0030/disco.py | 10 ++++++++-- sleekxmpp/stanza/iq.py | 37 ++++++++++++++++++++++++++++++++----- tests/test_stream_handlers.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 7 deletions(-) diff --git a/sleekxmpp/plugins/xep_0030/disco.py b/sleekxmpp/plugins/xep_0030/disco.py index 278b4a34..8a397923 100644 --- a/sleekxmpp/plugins/xep_0030/disco.py +++ b/sleekxmpp/plugins/xep_0030/disco.py @@ -324,6 +324,8 @@ 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 + has been received in timeout seconds. """ if local is None: if jid is not None and not isinstance(jid, JID): @@ -364,7 +366,8 @@ class XEP_0030(BasePlugin): iq['disco_info']['node'] = node if node else '' return iq.send(timeout=kwargs.get('timeout', None), block=kwargs.get('block', True), - callback=kwargs.get('callback', None)) + callback=kwargs.get('callback', None), + timeout_callback=kwargs.get('timeout_callback', None)) def set_info(self, jid=None, node=None, info=None): """ @@ -405,6 +408,8 @@ 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 + has been received in timeout seconds. """ if local or local is None and jid is None: items = self.api['get_items'](jid, node, @@ -423,7 +428,8 @@ class XEP_0030(BasePlugin): else: return iq.send(timeout=kwargs.get('timeout', None), block=kwargs.get('block', True), - callback=kwargs.get('callback', None)) + callback=kwargs.get('callback', None), + timeout_callback=kwargs.get('timeout_callback', None)) def set_items(self, jid=None, node=None, **kwargs): """ diff --git a/sleekxmpp/stanza/iq.py b/sleekxmpp/stanza/iq.py index f45b3c67..71c0444d 100644 --- a/sleekxmpp/stanza/iq.py +++ b/sleekxmpp/stanza/iq.py @@ -154,7 +154,7 @@ class Iq(RootStanza): StanzaBase.reply(self, clear) return self - def send(self, block=True, timeout=None, callback=None, now=False): + def send(self, block=True, timeout=None, callback=None, now=False, timeout_callback=None): """ Send an stanza over the XML stream. @@ -181,15 +181,32 @@ class Iq(RootStanza): now -- Indicates if the send queue should be skipped and send the stanza immediately. Used during stream initialization. Defaults to False. + timeout_callback -- Optional reference to a stream handler function. + Will be executed when the timeout expires before a + response has been received with the originally-sent IQ + stanza. Only called if there is a callback parameter + (and therefore are in async mode). """ if timeout is None: timeout = self.stream.response_timeout if callback is not None and self['type'] in ('get', 'set'): handler_name = 'IqCallback_%s' % self['id'] - handler = Callback(handler_name, - MatcherId(self['id']), - callback, - once=True) + if timeout_callback: + self.callback = callback + self.timeout_callback = timeout_callback + self.stream.schedule('IqTimeout_%s' % self['id'], + timeout, + self._fire_timeout, + repeat=False) + handler = Callback(handler_name, + MatcherId(self['id']), + self._handle_result, + once=True) + else: + handler = Callback(handler_name, + MatcherId(self['id']), + callback, + once=True) self.stream.register_handler(handler) StanzaBase.send(self, now=now) return handler_name @@ -206,6 +223,16 @@ class Iq(RootStanza): else: return StanzaBase.send(self, now=now) + def _handle_result(self, iq): + # we got the IQ, so don't fire the timeout + self.stream.scheduler.remove('IqTimeout_%s' % self['id']) + self.callback(iq) + + def _fire_timeout(self): + # don't fire the handler for the IQ, if it finally does come in + self.stream.remove_handler('IqCallback_%s' % self['id']) + self.timeout_callback(self) + def _set_stanza_values(self, values): """ Set multiple stanza interface values using a dictionary. diff --git a/tests/test_stream_handlers.py b/tests/test_stream_handlers.py index 7fd4e648..cdd128bb 100644 --- a/tests/test_stream_handlers.py +++ b/tests/test_stream_handlers.py @@ -153,6 +153,35 @@ class TestHandlers(SleekTest): self.failUnless(events == ['foo'], "Iq callback was not executed: %s" % events) + def testIqTimeoutCallback(self): + """Test that iq.send(tcallback=handle_foo, timeout_callback=handle_timeout) works.""" + events = [] + + def handle_foo(iq): + events.append('foo') + + def handle_timeout(iq): + events.append('timeout') + + iq = self.Iq() + iq['type'] = 'get' + iq['id'] = 'test-foo' + iq['to'] = 'user@localhost' + iq['query'] = 'foo' + iq.send(callback=handle_foo, timeout_callback=handle_timeout, timeout=0.05) + + self.send(""" + + + + """) + + # Give event queue time to process + time.sleep(0.1) + + self.failUnless(events == ['timeout'], + "Iq timeout was not executed: %s" % events) + def testMultipleHandlersForStanza(self): """ Test that multiple handlers for a single stanza work -- cgit v1.2.3 From c8c20fff71c31cfcaee9cda44b4cfddfd0763cc2 Mon Sep 17 00:00:00 2001 From: Joe Hildebrand Date: Mon, 29 Oct 2012 14:15:07 -0600 Subject: update JID_CACHE logic again. --- sleekxmpp/jid.py | 69 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/sleekxmpp/jid.py b/sleekxmpp/jid.py index feab4082..8f1a81d4 100644 --- a/sleekxmpp/jid.py +++ b/sleekxmpp/jid.py @@ -69,6 +69,20 @@ JID_CACHE = OrderedDict() 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.iteritems(): + if not item[1]: # if not locked + found = key + break + 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, @@ -418,19 +432,29 @@ class JID(object): # pylint: disable=W0212 def __init__(self, jid=None, **kwargs): - jid_data = (jid, kwargs.get('local', None), - kwargs.get('domain', None), - kwargs.get('resource', None)) - locked = kwargs.get('cache_lock', False) - - if jid_data in JID_CACHE: - parsed_jid, locked = JID_CACHE[jid_data] - self._jid = parsed_jid - else: - if jid is None: - jid = '' - + in_local = kwargs.get('local', None) + in_domain = kwargs.get('domain', None) + in_resource = kwargs.get('resource', None) + parts = None + if in_local or in_domain or in_resource: + parts = (in_local, in_domain, in_resource) + + # only check cache if there is a jid string, or parts, not if there + # are both + self._jid = None + key = None + if (jid is not None) and (parts is None): + if isinstance(jid, JID): + # it's already good to go, and there are no additions + self._jid = jid._jid + return + key = jid + self._jid, locked = JID_CACHE.get(jid, (None, locked)) + elif jid is None and parts is not None: + key = parts + self._jid, locked = JID_CACHE.get(parts, (None, locked)) + if not self._jid: if not jid: parsed_jid = (None, None, None) elif not isinstance(jid, JID): @@ -440,27 +464,16 @@ class JID(object): local, domain, resource = parsed_jid - local = kwargs.get('local', local) - domain = kwargs.get('domain', domain) - resource = kwargs.get('resource', resource) - if 'local' in kwargs: - local = _escape_node(local) + local = _escape_node(in_local) if 'domain' in kwargs: - domain = _validate_domain(domain) + domain = _validate_domain(in_domain) if 'resource' in kwargs: - resource = _validate_resource(resource) + resource = _validate_resource(in_resource) self._jid = (local, domain, resource) - - JID_CACHE[jid_data] = (self._jid, locked) - if len(JID_CACHE) > JID_CACHE_MAX_SIZE: - with JID_CACHE_LOCK: - key, item = JID_CACHE.popitem(False) - if item[1]: - # Need to reinsert locked JIDs - JID_CACHE[key] = item - + if key: + _cache(key, self._jid, locked) def unescape(self): """Return an unescaped JID object. -- cgit v1.2.3 From 577fd714724a915d438a232fb1a54379a2a8b96a Mon Sep 17 00:00:00 2001 From: Keith Gray Date: Sun, 15 Jun 2014 18:40:58 -0500 Subject: Fixed a unicode error in xep_0065 on Python 3 --- sleekxmpp/plugins/xep_0065/proxy.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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() -- cgit v1.2.3 From e94a73553d808f13708490024f73d4a5ca5a8bb3 Mon Sep 17 00:00:00 2001 From: Keith Gray Date: Sun, 15 Jun 2014 19:01:19 -0500 Subject: New version of the socks library socksipy from https://code.googlle.com/p/socksipy-branch/ --- sleekxmpp/thirdparty/socks.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sleekxmpp/thirdparty/socks.py b/sleekxmpp/thirdparty/socks.py index 9239a7b9..f32eb21e 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,7 +214,7 @@ 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)) @@ -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) -- cgit v1.2.3 From 3670d82f1c3ae78831574dbb4cd3138931ef4d5a Mon Sep 17 00:00:00 2001 From: Michael Trinque Date: Sun, 10 Aug 2014 16:02:10 -0700 Subject: Added wait param to XEP_0009 RemoteSession.close This parameter is False by default to preserve existing behavior. --- sleekxmpp/plugins/xep_0009/remote.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sleekxmpp/plugins/xep_0009/remote.py b/sleekxmpp/plugins/xep_0009/remote.py index 8c08e8f3..8847ff24 100644 --- a/sleekxmpp/plugins/xep_0009/remote.py +++ b/sleekxmpp/plugins/xep_0009/remote.py @@ -569,11 +569,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): -- cgit v1.2.3 From 79f3c1ac8f1aa0b099958e824dc53c17daf9849f Mon Sep 17 00:00:00 2001 From: Robin Gloster Date: Sun, 17 Aug 2014 23:12:46 +0200 Subject: serialize JID to allow json serializing --- sleekxmpp/xmlstream/stanzabase.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index 97107098..c6ed4163 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -565,7 +565,10 @@ class ElementBase(object): values = {} 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(): -- cgit v1.2.3 From 1063feb33bbe23c9196ebe278f306a7f2309ebad Mon Sep 17 00:00:00 2001 From: Robin Gloster Date: Sun, 17 Aug 2014 23:37:19 +0200 Subject: only schedule whitespace keepalive if enabled --- sleekxmpp/xmlstream/xmlstream.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py index 66985f3d..e011cf3d 100644 --- a/sleekxmpp/xmlstream/xmlstream.py +++ b/sleekxmpp/xmlstream/xmlstream.py @@ -938,12 +938,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.""" -- cgit v1.2.3 From d61f1cd035fa661588909ea0fdc4b62cc8a90161 Mon Sep 17 00:00:00 2001 From: Robin Gloster Date: Sun, 17 Aug 2014 23:37:19 +0200 Subject: only schedule whitespace keepalive if enabled --- sleekxmpp/xmlstream/xmlstream.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py index 66985f3d..e011cf3d 100644 --- a/sleekxmpp/xmlstream/xmlstream.py +++ b/sleekxmpp/xmlstream/xmlstream.py @@ -938,12 +938,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.""" -- cgit v1.2.3 From 08c62a6bf1fe19ddf1f0c8fa441750f41f5d9436 Mon Sep 17 00:00:00 2001 From: Robin Gloster Date: Mon, 18 Aug 2014 00:18:10 +0200 Subject: fix mutable default arguments :boom: --- sleekxmpp/clientxmpp.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sleekxmpp/clientxmpp.py b/sleekxmpp/clientxmpp.py index f837c0f2..8db6ef17 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,8 +59,13 @@ class ClientXMPP(BaseXMPP): :param escape_quotes: **Deprecated.** """ - def __init__(self, jid, password, plugin_config={}, plugin_whitelist=[], - escape_quotes=True, sasl_mech=None, lang='en'): + def __init__(self, jid, password, plugin_config=None, plugin_whitelist=None, escape_quotes=True, sasl_mech=None, + lang='en'): + if not plugin_whitelist: + plugin_whitelist = [] + if not plugin_config: + plugin_config = {} + BaseXMPP.__init__(self, jid, 'jabber:client') self.escape_quotes = escape_quotes -- cgit v1.2.3 From 7265682a4d57d88956cb54f98f7a470465bbf417 Mon Sep 17 00:00:00 2001 From: Robin Gloster Date: Mon, 18 Aug 2014 00:52:24 +0200 Subject: cleanup semicolons, whitespace and mutable default arguments --- README.rst | 12 +- examples/IoT_TestDevice.py | 8 +- examples/roster_browser.py | 2 +- sleekxmpp/basexmpp.py | 3 +- sleekxmpp/componentxmpp.py | 9 +- .../features/feature_mechanisms/mechanisms.py | 10 +- sleekxmpp/plugins/google/auth/stanza.py | 2 +- sleekxmpp/plugins/google/nosave/stanza.py | 2 +- sleekxmpp/plugins/google/settings/settings.py | 2 - sleekxmpp/plugins/xep_0004/stanza/form.py | 1 - sleekxmpp/plugins/xep_0009/remote.py | 3 +- sleekxmpp/plugins/xep_0009/rpc.py | 4 +- sleekxmpp/plugins/xep_0030/disco.py | 9 +- sleekxmpp/plugins/xep_0323/device.py | 115 +++--- sleekxmpp/plugins/xep_0323/sensordata.py | 430 ++++++++++----------- sleekxmpp/plugins/xep_0323/stanza/sensordata.py | 178 ++++----- sleekxmpp/plugins/xep_0323/timerreset.py | 2 +- sleekxmpp/plugins/xep_0325/control.py | 377 +++++++++--------- sleekxmpp/plugins/xep_0325/device.py | 46 +-- sleekxmpp/plugins/xep_0325/stanza/control.py | 123 +++--- sleekxmpp/roster/single.py | 6 +- sleekxmpp/test/sleektest.py | 16 +- sleekxmpp/thirdparty/socks.py | 4 +- sleekxmpp/thirdparty/statemachine.py | 4 +- sleekxmpp/xmlstream/xmlstream.py | 19 +- tests/test_stanza_element.py | 4 +- tests/test_stanza_xep_0323.py | 135 ++++--- tests/test_stanza_xep_0325.py | 80 ++-- tests/test_stream_xep_0323.py | 424 ++++++++++---------- tests/test_stream_xep_0325.py | 110 +++--- 30 files changed, 1073 insertions(+), 1067 deletions(-) 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 `_ +`XMPP: The Definitive Guide `_ 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 `_, + `fritzy@netflint.net `_, `@fritzy `_ Nathan is also the author of XMPPHP and `Seesmic-AS3-XMPP - `_, and a former member of + `_, and a former member of the XMPP Council. **Co-Author:** Lance Stout - `lancestout@gmail.com `_, + `lancestout@gmail.com `_, `@lancestout `_ **Contributors:** diff --git a/examples/IoT_TestDevice.py b/examples/IoT_TestDevice.py index 8105aaff..cd80cee2 100755 --- a/examples/IoT_TestDevice.py +++ b/examples/IoT_TestDevice.py @@ -179,13 +179,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/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/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py index bf0ae4df..8cd61b63 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 @@ -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/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/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/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/form.py b/sleekxmpp/plugins/xep_0004/stanza/form.py index bbd8540f..baa01528 100644 --- a/sleekxmpp/plugins/xep_0004/stanza/form.py +++ b/sleekxmpp/plugins/xep_0004/stanza/form.py @@ -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]) diff --git a/sleekxmpp/plugins/xep_0009/remote.py b/sleekxmpp/plugins/xep_0009/remote.py index 8847ff24..e85c3fa9 100644 --- a/sleekxmpp/plugins/xep_0009/remote.py +++ b/sleekxmpp/plugins/xep_0009/remote.py @@ -697,7 +697,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_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..30c28504 100644 --- a/sleekxmpp/plugins/xep_0323/sensordata.py +++ b/sleekxmpp/plugins/xep_0323/sensordata.py @@ -29,12 +29,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 @@ -55,10 +55,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 +69,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 +89,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,7 +102,7 @@ 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 @@ -155,11 +155,11 @@ 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 self.test_authenticated_from = "" @@ -182,7 +182,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 +198,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 +212,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 +238,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 +283,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,30 +292,30 @@ 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: @@ -324,19 +324,19 @@ class XEP_0323(BasePlugin): 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 +344,41 @@ 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; + 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.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 +390,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. - + 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,7 +442,7 @@ 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. @@ -452,99 +452,99 @@ class XEP_0323(BasePlugin): 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 + msg['fields']['done'] = '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] 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. 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 availble. + The callback function must support the following arguments: from_jid -- The jid of the responding device(s) @@ -565,7 +565,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 +575,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 +593,131 @@ 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. + """ + 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"); + 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. 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..f36d95e5 100644 --- a/sleekxmpp/plugins/xep_0323/timerreset.py +++ b/sleekxmpp/plugins/xep_0323/timerreset.py @@ -23,7 +23,7 @@ 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, **kwargs): 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 @@ -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/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/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/socks.py b/sleekxmpp/thirdparty/socks.py index 9239a7b9..0c1f6eba 100644 --- a/sleekxmpp/thirdparty/socks.py +++ b/sleekxmpp/thirdparty/socks.py @@ -213,7 +213,7 @@ class socksocket(socket.socket): # 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 +282,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. diff --git a/sleekxmpp/thirdparty/statemachine.py b/sleekxmpp/thirdparty/statemachine.py index 113320fa..9f6906bf 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, **kwargs): ''' 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 @@ -70,7 +70,7 @@ class StateMachine(object): 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, **kwargs): ''' Transition from any of the given `from_states` to the given `to_state`. ''' diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py index e011cf3d..01787b5e 100644 --- a/sleekxmpp/xmlstream/xmlstream.py +++ b/sleekxmpp/xmlstream/xmlstream.py @@ -853,7 +853,7 @@ class XMLStream(object): 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 @@ -1149,7 +1149,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. @@ -1160,6 +1160,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, []) @@ -1319,9 +1322,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') @@ -1336,6 +1336,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: @@ -1745,9 +1748,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') @@ -1760,6 +1760,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_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,""" @@ -193,7 +193,7 @@ class TestSensorDataStanzas(SleekTest): iq['rejected']['error'] = 'Access denied.' self.check(iq,""" - @@ -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,""" - + @@ -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,""" - + @@ -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,""" - + @@ -323,18 +322,18 @@ class TestSensorDataStanzas(SleekTest): - - - - - - - - - - - - + + + + + + + + + + + + @@ -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,""" @@ -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,""" """ ) - + suite = unittest.TestLoader().loadTestsFromTestCase(TestControlStanzas) 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(""" @@ -60,11 +60,11 @@ class TestStreamSensorData(SleekTest): - + - + """) 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(""" @@ -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(""" - @@ -142,7 +142,7 @@ class TestStreamSensorData(SleekTest): """) self.send(""" - @@ -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(""" - @@ -201,7 +201,7 @@ class TestStreamSensorData(SleekTest): """) self.send(""" - @@ -215,11 +215,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -227,7 +227,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - @@ -274,11 +274,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -287,11 +287,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -299,7 +299,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - @@ -344,11 +344,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -357,12 +357,12 @@ class TestStreamSensorData(SleekTest): - - + + - + """) self.send(""" @@ -370,7 +370,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" """) - 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(""" """) - 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(""" Invalid device Device22 - + """) 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(""" - + """) 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(""" - - + """) self.recv(""" @@ -552,42 +552,42 @@ class TestStreamSensorData(SleekTest): - - + + - + """) self.recv(""" - + """) - 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(""" - - + + """) def testServiceDiscoveryComponent(self): self.stream_start(mode='component', plugins=['xep_0030', - 'xep_0323']); + 'xep_0323']) self.recv(""" - - + + """) 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(""" - + """) self.recv(""" @@ -688,31 +688,31 @@ class TestStreamSensorData(SleekTest): """) - 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(""" - @@ -743,7 +743,7 @@ class TestStreamSensorData(SleekTest): - + """) self.send(""" @@ -752,11 +752,11 @@ class TestStreamSensorData(SleekTest): - + - + """) 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(""" - @@ -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(""" - @@ -861,11 +861,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -874,11 +874,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -886,7 +886,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - @@ -931,11 +931,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -944,11 +944,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -956,7 +956,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - @@ -1001,11 +1001,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -1013,7 +1013,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - - + """) self.recv(""" - - """) + + """) self.recv(""" - - + + - + """) self.recv(""" - + """) - 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(""" - + """) - self.xmpp['xep_0323'].cancel_request(session=session); + self.xmpp['xep_0323'].cancel_request(session=session) self.send(""" 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(""" - + """) - 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(""" - + """) - 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(""" - + """) - 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(""" Invalid field Voltage - + """) - 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(""" - @@ -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(""" - @@ -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(""" - + """) 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(""" Sensor error - + """) 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(""" - - + + """) def testServiceDiscoveryComponent(self): self.stream_start(mode='component', plugins=['xep_0030', - 'xep_0325']); + 'xep_0325']) self.recv(""" - - + + """) -- cgit v1.2.3 From 4144d60017e200d97bcfe0286daee06d4641a9e0 Mon Sep 17 00:00:00 2001 From: Robin Gloster Date: Mon, 18 Aug 2014 00:52:24 +0200 Subject: cleanup semicolons, whitespace and mutable default arguments --- README.rst | 12 +- examples/IoT_TestDevice.py | 8 +- examples/roster_browser.py | 2 +- sleekxmpp/basexmpp.py | 3 +- sleekxmpp/componentxmpp.py | 9 +- .../features/feature_mechanisms/mechanisms.py | 10 +- sleekxmpp/plugins/google/auth/stanza.py | 2 +- sleekxmpp/plugins/google/nosave/stanza.py | 2 +- sleekxmpp/plugins/google/settings/settings.py | 2 - sleekxmpp/plugins/xep_0004/stanza/form.py | 1 - sleekxmpp/plugins/xep_0009/remote.py | 3 +- sleekxmpp/plugins/xep_0009/rpc.py | 4 +- sleekxmpp/plugins/xep_0030/disco.py | 9 +- sleekxmpp/plugins/xep_0323/device.py | 115 +++--- sleekxmpp/plugins/xep_0323/sensordata.py | 430 ++++++++++----------- sleekxmpp/plugins/xep_0323/stanza/sensordata.py | 178 ++++----- sleekxmpp/plugins/xep_0323/timerreset.py | 2 +- sleekxmpp/plugins/xep_0325/control.py | 377 +++++++++--------- sleekxmpp/plugins/xep_0325/device.py | 46 +-- sleekxmpp/plugins/xep_0325/stanza/control.py | 123 +++--- sleekxmpp/roster/single.py | 6 +- sleekxmpp/test/sleektest.py | 16 +- sleekxmpp/thirdparty/socks.py | 4 +- sleekxmpp/thirdparty/statemachine.py | 4 +- sleekxmpp/xmlstream/xmlstream.py | 19 +- tests/test_stanza_element.py | 4 +- tests/test_stanza_xep_0323.py | 135 ++++--- tests/test_stanza_xep_0325.py | 80 ++-- tests/test_stream_xep_0323.py | 424 ++++++++++---------- tests/test_stream_xep_0325.py | 110 +++--- 30 files changed, 1073 insertions(+), 1067 deletions(-) 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 `_ +`XMPP: The Definitive Guide `_ 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 `_, + `fritzy@netflint.net `_, `@fritzy `_ Nathan is also the author of XMPPHP and `Seesmic-AS3-XMPP - `_, and a former member of + `_, and a former member of the XMPP Council. **Co-Author:** Lance Stout - `lancestout@gmail.com `_, + `lancestout@gmail.com `_, `@lancestout `_ **Contributors:** diff --git a/examples/IoT_TestDevice.py b/examples/IoT_TestDevice.py index 8105aaff..cd80cee2 100755 --- a/examples/IoT_TestDevice.py +++ b/examples/IoT_TestDevice.py @@ -179,13 +179,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/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/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py index bf0ae4df..8cd61b63 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 @@ -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/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/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/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/form.py b/sleekxmpp/plugins/xep_0004/stanza/form.py index bbd8540f..baa01528 100644 --- a/sleekxmpp/plugins/xep_0004/stanza/form.py +++ b/sleekxmpp/plugins/xep_0004/stanza/form.py @@ -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]) diff --git a/sleekxmpp/plugins/xep_0009/remote.py b/sleekxmpp/plugins/xep_0009/remote.py index 8847ff24..e85c3fa9 100644 --- a/sleekxmpp/plugins/xep_0009/remote.py +++ b/sleekxmpp/plugins/xep_0009/remote.py @@ -697,7 +697,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_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..30c28504 100644 --- a/sleekxmpp/plugins/xep_0323/sensordata.py +++ b/sleekxmpp/plugins/xep_0323/sensordata.py @@ -29,12 +29,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 @@ -55,10 +55,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 +69,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 +89,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,7 +102,7 @@ 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 @@ -155,11 +155,11 @@ 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 self.test_authenticated_from = "" @@ -182,7 +182,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 +198,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 +212,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 +238,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 +283,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,30 +292,30 @@ 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: @@ -324,19 +324,19 @@ class XEP_0323(BasePlugin): 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 +344,41 @@ 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; + 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.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 +390,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. - + 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,7 +442,7 @@ 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. @@ -452,99 +452,99 @@ class XEP_0323(BasePlugin): 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 + msg['fields']['done'] = '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] 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. 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 availble. + The callback function must support the following arguments: from_jid -- The jid of the responding device(s) @@ -565,7 +565,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 +575,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 +593,131 @@ 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. + """ + 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"); + 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. 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..f36d95e5 100644 --- a/sleekxmpp/plugins/xep_0323/timerreset.py +++ b/sleekxmpp/plugins/xep_0323/timerreset.py @@ -23,7 +23,7 @@ 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, **kwargs): 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 @@ -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/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/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/socks.py b/sleekxmpp/thirdparty/socks.py index 9239a7b9..0c1f6eba 100644 --- a/sleekxmpp/thirdparty/socks.py +++ b/sleekxmpp/thirdparty/socks.py @@ -213,7 +213,7 @@ class socksocket(socket.socket): # 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 +282,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. diff --git a/sleekxmpp/thirdparty/statemachine.py b/sleekxmpp/thirdparty/statemachine.py index 113320fa..9f6906bf 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, **kwargs): ''' 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 @@ -70,7 +70,7 @@ class StateMachine(object): 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, **kwargs): ''' Transition from any of the given `from_states` to the given `to_state`. ''' diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py index 66985f3d..a871238a 100644 --- a/sleekxmpp/xmlstream/xmlstream.py +++ b/sleekxmpp/xmlstream/xmlstream.py @@ -853,7 +853,7 @@ class XMLStream(object): 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 @@ -1148,7 +1148,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 +1159,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 +1321,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 +1335,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 +1747,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 +1759,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_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,""" @@ -193,7 +193,7 @@ class TestSensorDataStanzas(SleekTest): iq['rejected']['error'] = 'Access denied.' self.check(iq,""" - @@ -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,""" - + @@ -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,""" - + @@ -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,""" - + @@ -323,18 +322,18 @@ class TestSensorDataStanzas(SleekTest): - - - - - - - - - - - - + + + + + + + + + + + + @@ -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,""" @@ -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,""" """ ) - + suite = unittest.TestLoader().loadTestsFromTestCase(TestControlStanzas) 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(""" @@ -60,11 +60,11 @@ class TestStreamSensorData(SleekTest): - + - + """) 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(""" @@ -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(""" - @@ -142,7 +142,7 @@ class TestStreamSensorData(SleekTest): """) self.send(""" - @@ -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(""" - @@ -201,7 +201,7 @@ class TestStreamSensorData(SleekTest): """) self.send(""" - @@ -215,11 +215,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -227,7 +227,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - @@ -274,11 +274,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -287,11 +287,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -299,7 +299,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - @@ -344,11 +344,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -357,12 +357,12 @@ class TestStreamSensorData(SleekTest): - - + + - + """) self.send(""" @@ -370,7 +370,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" """) - 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(""" """) - 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(""" Invalid device Device22 - + """) 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(""" - + """) 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(""" - - + """) self.recv(""" @@ -552,42 +552,42 @@ class TestStreamSensorData(SleekTest): - - + + - + """) self.recv(""" - + """) - 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(""" - - + + """) def testServiceDiscoveryComponent(self): self.stream_start(mode='component', plugins=['xep_0030', - 'xep_0323']); + 'xep_0323']) self.recv(""" - - + + """) 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(""" - + """) self.recv(""" @@ -688,31 +688,31 @@ class TestStreamSensorData(SleekTest): """) - 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(""" - @@ -743,7 +743,7 @@ class TestStreamSensorData(SleekTest): - + """) self.send(""" @@ -752,11 +752,11 @@ class TestStreamSensorData(SleekTest): - + - + """) 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(""" - @@ -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(""" - @@ -861,11 +861,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -874,11 +874,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -886,7 +886,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - @@ -931,11 +931,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -944,11 +944,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -956,7 +956,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - @@ -1001,11 +1001,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -1013,7 +1013,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - - + """) self.recv(""" - - """) + + """) self.recv(""" - - + + - + """) self.recv(""" - + """) - 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(""" - + """) - self.xmpp['xep_0323'].cancel_request(session=session); + self.xmpp['xep_0323'].cancel_request(session=session) self.send(""" 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(""" - + """) - 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(""" - + """) - 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(""" - + """) - 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(""" Invalid field Voltage - + """) - 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(""" - @@ -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(""" - @@ -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(""" - + """) 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(""" Sensor error - + """) 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(""" - - + + """) def testServiceDiscoveryComponent(self): self.stream_start(mode='component', plugins=['xep_0030', - 'xep_0325']); + 'xep_0325']) self.recv(""" - - + + """) -- cgit v1.2.3 From 1bf34f7fe694c2997710e0d23b0784e824df5e5e Mon Sep 17 00:00:00 2001 From: Robin Gloster Date: Mon, 18 Aug 2014 00:18:10 +0200 Subject: fix mutable default arguments :boom: --- sleekxmpp/clientxmpp.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sleekxmpp/clientxmpp.py b/sleekxmpp/clientxmpp.py index f837c0f2..8db6ef17 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,8 +59,13 @@ class ClientXMPP(BaseXMPP): :param escape_quotes: **Deprecated.** """ - def __init__(self, jid, password, plugin_config={}, plugin_whitelist=[], - escape_quotes=True, sasl_mech=None, lang='en'): + def __init__(self, jid, password, plugin_config=None, plugin_whitelist=None, escape_quotes=True, sasl_mech=None, + lang='en'): + if not plugin_whitelist: + plugin_whitelist = [] + if not plugin_config: + plugin_config = {} + BaseXMPP.__init__(self, jid, 'jabber:client') self.escape_quotes = escape_quotes -- cgit v1.2.3 From 3dd379cdf12d885e26f8ec26c54879a95d5f0b84 Mon Sep 17 00:00:00 2001 From: Robin Gloster Date: Mon, 18 Aug 2014 15:15:14 +0200 Subject: Revert "cleanup semicolons, whitespace and mutable default arguments" This reverts commit 7265682a4d57d88956cb54f98f7a470465bbf417. --- README.rst | 12 +- examples/IoT_TestDevice.py | 8 +- examples/roster_browser.py | 2 +- sleekxmpp/basexmpp.py | 3 +- sleekxmpp/componentxmpp.py | 9 +- .../features/feature_mechanisms/mechanisms.py | 10 +- sleekxmpp/plugins/google/auth/stanza.py | 2 +- sleekxmpp/plugins/google/nosave/stanza.py | 2 +- sleekxmpp/plugins/google/settings/settings.py | 2 + sleekxmpp/plugins/xep_0004/stanza/form.py | 1 + sleekxmpp/plugins/xep_0009/remote.py | 3 +- sleekxmpp/plugins/xep_0009/rpc.py | 4 +- sleekxmpp/plugins/xep_0030/disco.py | 9 +- sleekxmpp/plugins/xep_0323/device.py | 115 +++--- sleekxmpp/plugins/xep_0323/sensordata.py | 430 ++++++++++----------- sleekxmpp/plugins/xep_0323/stanza/sensordata.py | 178 ++++----- sleekxmpp/plugins/xep_0323/timerreset.py | 2 +- sleekxmpp/plugins/xep_0325/control.py | 377 +++++++++--------- sleekxmpp/plugins/xep_0325/device.py | 46 +-- sleekxmpp/plugins/xep_0325/stanza/control.py | 123 +++--- sleekxmpp/roster/single.py | 6 +- sleekxmpp/test/sleektest.py | 16 +- sleekxmpp/thirdparty/socks.py | 4 +- sleekxmpp/thirdparty/statemachine.py | 4 +- sleekxmpp/xmlstream/xmlstream.py | 19 +- tests/test_stanza_element.py | 4 +- tests/test_stanza_xep_0323.py | 135 +++---- tests/test_stanza_xep_0325.py | 80 ++-- tests/test_stream_xep_0323.py | 424 ++++++++++---------- tests/test_stream_xep_0325.py | 110 +++--- 30 files changed, 1067 insertions(+), 1073 deletions(-) diff --git a/README.rst b/README.rst index 3c142c9b..7c14ab5a 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 `_ +`XMPP: The Definitive Guide `_ 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 `_, + `fritzy@netflint.net `_, `@fritzy `_ Nathan is also the author of XMPPHP and `Seesmic-AS3-XMPP - `_, and a former member of + `_, and a former member of the XMPP Council. **Co-Author:** Lance Stout - `lancestout@gmail.com `_, + `lancestout@gmail.com `_, `@lancestout `_ **Contributors:** diff --git a/examples/IoT_TestDevice.py b/examples/IoT_TestDevice.py index cd80cee2..8105aaff 100755 --- a/examples/IoT_TestDevice.py +++ b/examples/IoT_TestDevice.py @@ -179,13 +179,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/roster_browser.py b/examples/roster_browser.py index a16de24c..485ac941 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: %s' % err.iq['error']['condition']) + print('Error: %' % err.iq['error']['condition']) except IqTimeout: print('Error: Request timed out') self.send_presence() diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py index 8cd61b63..bf0ae4df 100644 --- a/sleekxmpp/basexmpp.py +++ b/sleekxmpp/basexmpp.py @@ -25,6 +25,7 @@ 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 @@ -244,7 +245,7 @@ class BaseXMPP(XMLStream): self.plugin[name].post_inited = True return XMLStream.process(self, *args, **kwargs) - def register_plugin(self, plugin, pconfig=None, module=None): + def register_plugin(self, plugin, pconfig={}, 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/componentxmpp.py b/sleekxmpp/componentxmpp.py index 4b229a6f..bac455e2 100644 --- a/sleekxmpp/componentxmpp.py +++ b/sleekxmpp/componentxmpp.py @@ -49,13 +49,8 @@ class ComponentXMPP(BaseXMPP): Defaults to ``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 = {} - + def __init__(self, jid, secret, host=None, port=None, + plugin_config={}, plugin_whitelist=[], use_jc_ns=False): 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 1d8f8798..17ad5ed0 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/plugins/google/auth/stanza.py b/sleekxmpp/plugins/google/auth/stanza.py index 2d13f85a..49c5cba7 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.discovery_attr) == 'true' + return self.parent()._get_attr(self.disovery_attr) == 'true' def set_client_uses_full_bind_result(self, value): print('>>>', value) diff --git a/sleekxmpp/plugins/google/nosave/stanza.py b/sleekxmpp/plugins/google/nosave/stanza.py index 791d4b0c..d8701322 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, value): + def set_source(self): self._set_attr('source', str(value)) diff --git a/sleekxmpp/plugins/google/settings/settings.py b/sleekxmpp/plugins/google/settings/settings.py index 591956fc..7122ff56 100644 --- a/sleekxmpp/plugins/google/settings/settings.py +++ b/sleekxmpp/plugins/google/settings/settings.py @@ -6,6 +6,8 @@ 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/form.py b/sleekxmpp/plugins/xep_0004/stanza/form.py index baa01528..bbd8540f 100644 --- a/sleekxmpp/plugins/xep_0004/stanza/form.py +++ b/sleekxmpp/plugins/xep_0004/stanza/form.py @@ -151,6 +151,7 @@ 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]) diff --git a/sleekxmpp/plugins/xep_0009/remote.py b/sleekxmpp/plugins/xep_0009/remote.py index e85c3fa9..8847ff24 100644 --- a/sleekxmpp/plugins/xep_0009/remote.py +++ b/sleekxmpp/plugins/xep_0009/remote.py @@ -697,8 +697,7 @@ 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 6179355e..3378c650 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 721f73f6..8a397923 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=None): + def _run_node_handler(self, htype, jid, node=None, ifrom=None, data={}): """ Execute the most specific node handler for the given JID/node combination. @@ -615,9 +615,6 @@ 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_0323/device.py b/sleekxmpp/plugins/xep_0323/device.py index 80e6fd95..0bc20327 100644 --- a/sleekxmpp/plugins/xep_0323/device.py +++ b/sleekxmpp/plugins/xep_0323/device.py @@ -13,18 +13,15 @@ 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=None): - if not fields: - fields = {} - + def __init__(self, nodeId, fields={}): self.nodeId = nodeId self.fields = fields # see fields described below # {'type':'numeric', @@ -41,19 +38,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): """ @@ -68,7 +65,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 @@ -76,11 +73,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: @@ -92,10 +89,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) @@ -104,10 +101,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 @@ -117,27 +114,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') @@ -145,36 +142,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") @@ -198,7 +195,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): """ @@ -210,7 +207,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): """ @@ -224,12 +221,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): """ @@ -242,17 +239,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 30c28504..2e2f2470 100644 --- a/sleekxmpp/plugins/xep_0323/sensordata.py +++ b/sleekxmpp/plugins/xep_0323/sensordata.py @@ -29,12 +29,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 @@ -55,10 +55,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 +69,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 +89,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,7 +102,7 @@ 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 @@ -155,11 +155,11 @@ 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 self.test_authenticated_from = "" @@ -182,7 +182,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 +198,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 +212,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 +238,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 +283,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,30 +292,30 @@ 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: @@ -324,19 +324,19 @@ class XEP_0323(BasePlugin): 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 +344,41 @@ 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 + 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.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 +390,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. - + 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,7 +442,7 @@ 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. @@ -452,99 +452,99 @@ class XEP_0323(BasePlugin): 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; + msg['fields']['done'] = '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]; 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. 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 availble. + The callback function must support the following arguments: from_jid -- The jid of the responding device(s) @@ -565,7 +565,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 +575,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 +593,131 @@ 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 += 1 - self.seqnr_lock.release() - return str(self.last_seqnr) + self.seqnr_lock.acquire(); + self.last_seqnr = 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. + """ + 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") + 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. 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) - - callback(from_jid=msg['from'], result="fields", nodeId=node['nodeId'], timestamp=ts['value'], fields=fields) + field_block["flags"] = flags; + fields.append(field_block); + 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. """ - 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") - + 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 e8718161..a11c3e94 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 f36d95e5..578f1efe 100644 --- a/sleekxmpp/plugins/xep_0323/timerreset.py +++ b/sleekxmpp/plugins/xep_0323/timerreset.py @@ -23,7 +23,7 @@ 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=[], kwargs={}): 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 11e7a045..e34eb2c2 100644 --- a/sleekxmpp/plugins/xep_0325/control.py +++ b/sleekxmpp/plugins/xep_0325/control.py @@ -12,6 +12,7 @@ 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 @@ -25,16 +26,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 @@ -51,9 +52,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: @@ -64,7 +65,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 ----------- @@ -84,15 +85,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. @@ -101,7 +102,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 @@ -134,11 +135,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 = "" @@ -155,13 +156,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()); # ================================================================= @@ -169,10 +170,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 @@ -184,30 +185,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 += 1 - self.seqnr_lock.release() - return str(self.last_seqnr) + self.seqnr_lock.acquire(); + self.last_seqnr = 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 @@ -215,79 +216,80 @@ 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 @@ -297,72 +299,73 @@ 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 @@ -370,51 +373,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. @@ -425,62 +428,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) @@ -491,46 +494,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). - nodeId -- [optional] Only applicable when result == "error" - List of node Ids of failing device(s). - - fields -- [optional] Only applicable when result == "error" + 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) @@ -538,32 +541,34 @@ 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'] + 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); - 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 f1ed0733..a60d5f9a 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 1fd5c35d..67107ecb 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,91 +415,90 @@ 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/roster/single.py b/sleekxmpp/roster/single.py index e9ce4f21..f080ae8a 100644 --- a/sleekxmpp/roster/single.py +++ b/sleekxmpp/roster/single.py @@ -237,7 +237,8 @@ class RosterNode(object): if not self.xmpp.is_component: return self.update(jid, subscription='remove') - def update(self, jid, name=None, subscription=None, groups=None, block=True, timeout=None, callback=None): + def update(self, jid, name=None, subscription=None, groups=[], + block=True, timeout=None, callback=None): """ Update a JID's subscription information. @@ -257,9 +258,6 @@ 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/test/sleektest.py b/sleekxmpp/test/sleektest.py index e26f99ce..d28f77e2 100644 --- a/sleekxmpp/test/sleektest.py +++ b/sleekxmpp/test/sleektest.py @@ -288,8 +288,11 @@ 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=None): + 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={}): """ Initialize an XMPP client or component using a dummy XML stream. @@ -312,9 +315,6 @@ 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,7 +425,8 @@ class SleekTest(unittest.TestCase): parts.append('xmlns="%s"' % default_ns) return header % ' '.join(parts) - def recv(self, data, defaults=None, method='exact', use_values=True, timeout=1): + def recv(self, data, defaults=[], method='exact', + use_values=True, timeout=1): """ Pass data to the dummy XMPP client as if it came from an XMPP server. @@ -446,9 +447,6 @@ 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/socks.py b/sleekxmpp/thirdparty/socks.py index 0c1f6eba..9239a7b9 100644 --- a/sleekxmpp/thirdparty/socks.py +++ b/sleekxmpp/thirdparty/socks.py @@ -213,7 +213,7 @@ class socksocket(socket.socket): # Resolve locally ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) req = req + chr(0x01).encode() + ipaddr - req += struct.pack(">H", destport) + req = req + struct.pack(">H", destport) self.sendall(req) # Get the response resp = self.__recvall(4) @@ -282,7 +282,7 @@ class socksocket(socket.socket): # The username parameter is considered userid for SOCKS4 if self.__proxy[4] != None: req = req + self.__proxy[4] - req += chr(0x00).encode() + req = 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. diff --git a/sleekxmpp/thirdparty/statemachine.py b/sleekxmpp/thirdparty/statemachine.py index 9f6906bf..113320fa 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=[], kwargs={}): ''' 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 @@ -70,7 +70,7 @@ class StateMachine(object): 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=[], kwargs={}): ''' Transition from any of the given `from_states` to the given `to_state`. ''' diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py index 01787b5e..e011cf3d 100644 --- a/sleekxmpp/xmlstream/xmlstream.py +++ b/sleekxmpp/xmlstream/xmlstream.py @@ -853,7 +853,7 @@ class XMLStream(object): 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 @@ -1149,7 +1149,7 @@ class XMLStream(object): """ return len(self.__event_handlers.get(name, [])) - def event(self, name, data=None, direct=False): + def event(self, name, data={}, direct=False): """Manually trigger a custom event. :param name: The name of the event to trigger. @@ -1160,9 +1160,6 @@ 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, []) @@ -1322,6 +1319,9 @@ 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') @@ -1336,9 +1336,6 @@ 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: @@ -1748,6 +1745,9 @@ 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') @@ -1760,9 +1760,6 @@ 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 e678b56e..2b9676cf 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_0323.py b/tests/test_stanza_xep_0323.py index 7b1dfe42..67e0daf0 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,""" @@ -193,7 +193,7 @@ class TestSensorDataStanzas(SleekTest): iq['rejected']['error'] = 'Access denied.' self.check(iq,""" - @@ -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,""" - + @@ -258,9 +258,10 @@ 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,""" - + @@ -280,32 +281,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,""" - + @@ -322,18 +323,18 @@ class TestSensorDataStanzas(SleekTest): - - - - - - - - - - - - + + + + + + + + + + + + @@ -341,19 +342,21 @@ 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") + 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"); + - ts = node.add_timestamp("2013-03-07T16:24:30") - ts = node.add_timestamp("2013-03-07T16:24:31") self.check(msg,""" @@ -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,""" """ ) - + suite = unittest.TestLoader().loadTestsFromTestCase(TestControlStanzas) diff --git a/tests/test_stream_xep_0323.py b/tests/test_stream_xep_0323.py index 94f1d638..fd2ad225 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(""" @@ -60,11 +60,11 @@ class TestStreamSensorData(SleekTest): - + - + """) 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(""" @@ -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(""" - @@ -142,7 +142,7 @@ class TestStreamSensorData(SleekTest): """) self.send(""" - @@ -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(""" - @@ -201,7 +201,7 @@ class TestStreamSensorData(SleekTest): """) self.send(""" - @@ -215,11 +215,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -227,7 +227,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - @@ -274,11 +274,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -287,11 +287,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -299,7 +299,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - @@ -344,11 +344,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -357,12 +357,12 @@ class TestStreamSensorData(SleekTest): - - + + - + """) self.send(""" @@ -370,7 +370,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" """) - 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(""" """) - 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(""" Invalid device Device22 - + """) 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(""" - + """) 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(""" - - + """) self.recv(""" @@ -552,42 +552,42 @@ class TestStreamSensorData(SleekTest): - - + + - + """) self.recv(""" - + """) - 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(""" - - + + """) def testServiceDiscoveryComponent(self): self.stream_start(mode='component', plugins=['xep_0030', - 'xep_0323']) + 'xep_0323']); self.recv(""" - - + + """) 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(""" - + """) self.recv(""" @@ -688,31 +688,31 @@ class TestStreamSensorData(SleekTest): """) - 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(""" - @@ -743,7 +743,7 @@ class TestStreamSensorData(SleekTest): - + """) self.send(""" @@ -752,11 +752,11 @@ class TestStreamSensorData(SleekTest): - + - + """) 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(""" - @@ -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(""" - @@ -861,11 +861,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -874,11 +874,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -886,7 +886,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - @@ -931,11 +931,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -944,11 +944,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -956,7 +956,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - @@ -1001,11 +1001,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -1013,7 +1013,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - - + """) self.recv(""" - - """) + + """) self.recv(""" - - + + - + """) self.recv(""" - + """) - 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(""" - + """) - self.xmpp['xep_0323'].cancel_request(session=session) + self.xmpp['xep_0323'].cancel_request(session=session); self.send(""" diff --git a/tests/test_stream_xep_0325.py b/tests/test_stream_xep_0325.py index 2ebdd121..4b3250fc 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(""" - + """) - 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(""" - + """) - 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(""" - + """) - 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(""" Invalid field Voltage - + """) - 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(""" - @@ -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(""" - @@ -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(""" - + """) 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(""" Sensor error - + """) 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(""" - - + + """) def testServiceDiscoveryComponent(self): self.stream_start(mode='component', plugins=['xep_0030', - 'xep_0325']) + 'xep_0325']); self.recv(""" - - + + """) -- cgit v1.2.3 From aabec8b993748866b9cf3f09ebb7ae7ce2ddc426 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 21 Aug 2014 10:05:42 -0700 Subject: Fix some more Unicode in **kwargs issues in Py2.6 --- sleekxmpp/plugins/xep_0004/stanza/form.py | 9 ++++++++- sleekxmpp/xmlstream/stanzabase.py | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/sleekxmpp/plugins/xep_0004/stanza/form.py b/sleekxmpp/plugins/xep_0004/stanza/form.py index bbd8540f..f51e7f09 100644 --- a/sleekxmpp/plugins/xep_0004/stanza/form.py +++ b/sleekxmpp/plugins/xep_0004/stanza/form.py @@ -195,7 +195,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'] diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index c6ed4163..11c8dd67 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 @@ -679,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': @@ -755,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() @@ -841,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() -- cgit v1.2.3 From afc939708ff71e168f9204f1eab8823b7dc9f875 Mon Sep 17 00:00:00 2001 From: Robin Gloster Date: Mon, 18 Aug 2014 00:52:24 +0200 Subject: cleanup semicolons, whitespace and mutable default arguments --- README.rst | 12 +- examples/IoT_TestDevice.py | 8 +- examples/roster_browser.py | 2 +- sleekxmpp/basexmpp.py | 3 +- sleekxmpp/componentxmpp.py | 9 +- .../features/feature_mechanisms/mechanisms.py | 10 +- sleekxmpp/plugins/google/auth/stanza.py | 2 +- sleekxmpp/plugins/google/nosave/stanza.py | 2 +- sleekxmpp/plugins/google/settings/settings.py | 2 - sleekxmpp/plugins/xep_0004/stanza/form.py | 1 - sleekxmpp/plugins/xep_0009/remote.py | 3 +- sleekxmpp/plugins/xep_0009/rpc.py | 4 +- sleekxmpp/plugins/xep_0030/disco.py | 9 +- sleekxmpp/plugins/xep_0323/device.py | 115 +++--- sleekxmpp/plugins/xep_0323/sensordata.py | 430 ++++++++++----------- sleekxmpp/plugins/xep_0323/stanza/sensordata.py | 178 ++++----- sleekxmpp/plugins/xep_0323/timerreset.py | 2 +- sleekxmpp/plugins/xep_0325/control.py | 377 +++++++++--------- sleekxmpp/plugins/xep_0325/device.py | 46 +-- sleekxmpp/plugins/xep_0325/stanza/control.py | 123 +++--- sleekxmpp/roster/single.py | 6 +- sleekxmpp/test/sleektest.py | 16 +- sleekxmpp/thirdparty/socks.py | 4 +- sleekxmpp/thirdparty/statemachine.py | 4 +- sleekxmpp/xmlstream/xmlstream.py | 19 +- tests/test_stanza_element.py | 4 +- tests/test_stanza_xep_0323.py | 135 ++++--- tests/test_stanza_xep_0325.py | 80 ++-- tests/test_stream_xep_0323.py | 424 ++++++++++---------- tests/test_stream_xep_0325.py | 110 +++--- 30 files changed, 1073 insertions(+), 1067 deletions(-) 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 `_ +`XMPP: The Definitive Guide `_ 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 `_, + `fritzy@netflint.net `_, `@fritzy `_ Nathan is also the author of XMPPHP and `Seesmic-AS3-XMPP - `_, and a former member of + `_, and a former member of the XMPP Council. **Co-Author:** Lance Stout - `lancestout@gmail.com `_, + `lancestout@gmail.com `_, `@lancestout `_ **Contributors:** diff --git a/examples/IoT_TestDevice.py b/examples/IoT_TestDevice.py index 8105aaff..cd80cee2 100755 --- a/examples/IoT_TestDevice.py +++ b/examples/IoT_TestDevice.py @@ -179,13 +179,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/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/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py index bf0ae4df..8cd61b63 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 @@ -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/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/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/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/form.py b/sleekxmpp/plugins/xep_0004/stanza/form.py index f51e7f09..1d733760 100644 --- a/sleekxmpp/plugins/xep_0004/stanza/form.py +++ b/sleekxmpp/plugins/xep_0004/stanza/form.py @@ -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]) diff --git a/sleekxmpp/plugins/xep_0009/remote.py b/sleekxmpp/plugins/xep_0009/remote.py index 8847ff24..e85c3fa9 100644 --- a/sleekxmpp/plugins/xep_0009/remote.py +++ b/sleekxmpp/plugins/xep_0009/remote.py @@ -697,7 +697,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_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..30c28504 100644 --- a/sleekxmpp/plugins/xep_0323/sensordata.py +++ b/sleekxmpp/plugins/xep_0323/sensordata.py @@ -29,12 +29,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 @@ -55,10 +55,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 +69,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 +89,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,7 +102,7 @@ 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 @@ -155,11 +155,11 @@ 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 self.test_authenticated_from = "" @@ -182,7 +182,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 +198,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 +212,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 +238,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 +283,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,30 +292,30 @@ 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: @@ -324,19 +324,19 @@ class XEP_0323(BasePlugin): 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 +344,41 @@ 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; + 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.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 +390,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. - + 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,7 +442,7 @@ 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. @@ -452,99 +452,99 @@ class XEP_0323(BasePlugin): 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 + msg['fields']['done'] = '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] 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. 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 availble. + The callback function must support the following arguments: from_jid -- The jid of the responding device(s) @@ -565,7 +565,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 +575,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 +593,131 @@ 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. + """ + 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"); + 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. 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..f36d95e5 100644 --- a/sleekxmpp/plugins/xep_0323/timerreset.py +++ b/sleekxmpp/plugins/xep_0323/timerreset.py @@ -23,7 +23,7 @@ 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, **kwargs): 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 @@ -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/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/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/socks.py b/sleekxmpp/thirdparty/socks.py index 9239a7b9..0c1f6eba 100644 --- a/sleekxmpp/thirdparty/socks.py +++ b/sleekxmpp/thirdparty/socks.py @@ -213,7 +213,7 @@ class socksocket(socket.socket): # 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 +282,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. diff --git a/sleekxmpp/thirdparty/statemachine.py b/sleekxmpp/thirdparty/statemachine.py index 113320fa..9f6906bf 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, **kwargs): ''' 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 @@ -70,7 +70,7 @@ class StateMachine(object): 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, **kwargs): ''' Transition from any of the given `from_states` to the given `to_state`. ''' diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py index e011cf3d..01787b5e 100644 --- a/sleekxmpp/xmlstream/xmlstream.py +++ b/sleekxmpp/xmlstream/xmlstream.py @@ -853,7 +853,7 @@ class XMLStream(object): 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 @@ -1149,7 +1149,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. @@ -1160,6 +1160,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, []) @@ -1319,9 +1322,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') @@ -1336,6 +1336,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: @@ -1745,9 +1748,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') @@ -1760,6 +1760,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_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,""" @@ -193,7 +193,7 @@ class TestSensorDataStanzas(SleekTest): iq['rejected']['error'] = 'Access denied.' self.check(iq,""" - @@ -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,""" - + @@ -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,""" - + @@ -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,""" - + @@ -323,18 +322,18 @@ class TestSensorDataStanzas(SleekTest): - - - - - - - - - - - - + + + + + + + + + + + + @@ -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,""" @@ -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,""" """ ) - + suite = unittest.TestLoader().loadTestsFromTestCase(TestControlStanzas) 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(""" @@ -60,11 +60,11 @@ class TestStreamSensorData(SleekTest): - + - + """) 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(""" @@ -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(""" - @@ -142,7 +142,7 @@ class TestStreamSensorData(SleekTest): """) self.send(""" - @@ -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(""" - @@ -201,7 +201,7 @@ class TestStreamSensorData(SleekTest): """) self.send(""" - @@ -215,11 +215,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -227,7 +227,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - @@ -274,11 +274,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -287,11 +287,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -299,7 +299,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - @@ -344,11 +344,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -357,12 +357,12 @@ class TestStreamSensorData(SleekTest): - - + + - + """) self.send(""" @@ -370,7 +370,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" """) - 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(""" """) - 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(""" Invalid device Device22 - + """) 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(""" - + """) 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(""" - - + """) self.recv(""" @@ -552,42 +552,42 @@ class TestStreamSensorData(SleekTest): - - + + - + """) self.recv(""" - + """) - 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(""" - - + + """) def testServiceDiscoveryComponent(self): self.stream_start(mode='component', plugins=['xep_0030', - 'xep_0323']); + 'xep_0323']) self.recv(""" - - + + """) 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(""" - + """) self.recv(""" @@ -688,31 +688,31 @@ class TestStreamSensorData(SleekTest): """) - 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(""" - @@ -743,7 +743,7 @@ class TestStreamSensorData(SleekTest): - + """) self.send(""" @@ -752,11 +752,11 @@ class TestStreamSensorData(SleekTest): - + - + """) 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(""" - @@ -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(""" - @@ -861,11 +861,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -874,11 +874,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -886,7 +886,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - @@ -931,11 +931,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -944,11 +944,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -956,7 +956,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - @@ -1001,11 +1001,11 @@ class TestStreamSensorData(SleekTest): - + - + """) self.send(""" @@ -1013,7 +1013,7 @@ class TestStreamSensorData(SleekTest): to='master@clayster.com/amr'> - + """) 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(""" - - + """) self.recv(""" - - """) + + """) self.recv(""" - - + + - + """) self.recv(""" - + """) - 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(""" - + """) - self.xmpp['xep_0323'].cancel_request(session=session); + self.xmpp['xep_0323'].cancel_request(session=session) self.send(""" 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(""" - + """) - 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(""" - + """) - 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(""" - + """) - 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(""" Invalid field Voltage - + """) - 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(""" - @@ -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(""" - @@ -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(""" - + """) 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(""" Sensor error - + """) 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(""" - - + + """) def testServiceDiscoveryComponent(self): self.stream_start(mode='component', plugins=['xep_0030', - 'xep_0325']); + 'xep_0325']) self.recv(""" - - + + """) -- cgit v1.2.3 From 073e85381a86069e931369bb5353cab2a2e3682d Mon Sep 17 00:00:00 2001 From: Robin Gloster Date: Sat, 23 Aug 2014 14:25:35 +0200 Subject: fix args, kwargs which were broken with #310. this is essentially the same but working --- sleekxmpp/plugins/xep_0323/timerreset.py | 7 ++++++- sleekxmpp/thirdparty/statemachine.py | 12 ++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/sleekxmpp/plugins/xep_0323/timerreset.py b/sleekxmpp/plugins/xep_0323/timerreset.py index f36d95e5..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/thirdparty/statemachine.py b/sleekxmpp/thirdparty/statemachine.py index 9f6906bf..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") -- cgit v1.2.3 From f5ae98aaf18e441ce2d7b924a992d6b827823b23 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 25 Aug 2014 01:08:13 +0200 Subject: Fix saslprep on the username MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two issues fixed here: - ints are not comparable with bytes, so char was never == to b',', which renders the whole function pointless - The bytes were converted back to “characters” by using chr(), which doesn’t make sense if the username contains characters that fit on more than one bytes. This would trigger an “invalid username” error from the server when using a non-ascii JID. --- sleekxmpp/util/sasl/mechanisms.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) 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 = {} -- cgit v1.2.3 From 35f33f1614de11409d4b452ad1e5a11e133e06c8 Mon Sep 17 00:00:00 2001 From: Matthieu Rakotojaona Date: Sat, 30 Aug 2014 17:23:27 +0200 Subject: Extend AtomEntry capabilities --- sleekxmpp/stanza/atom.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) 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) -- cgit v1.2.3 From f22d8e67b402f4756a497d8797e1526c2b7682a1 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Tue, 9 Sep 2014 08:49:37 -0700 Subject: Preserve ID for error responses Fixes #319 --- sleekxmpp/stanza/rootstanza.py | 4 ++++ 1 file changed, 4 insertions(+) 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' -- cgit v1.2.3 From e328ff483334794bfd1b6a42a2e1e8e3e2814eb5 Mon Sep 17 00:00:00 2001 From: s-m-b Date: Sun, 9 Nov 2014 04:36:38 +0300 Subject: Typo fix of parameter name 'data' it is now 'iq' Code was broken during refactoring --- sleekxmpp/plugins/google/gmail/notifications.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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: -- cgit v1.2.3 From d33cc00fe9e71329090e5ddffacf190c7c838416 Mon Sep 17 00:00:00 2001 From: Oleg Antonyan Date: Sun, 23 Nov 2014 16:46:01 +0200 Subject: On initial connect use delay if connection failed --- sleekxmpp/xmlstream/xmlstream.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py index e011cf3d..bba86231 100644 --- a/sleekxmpp/xmlstream/xmlstream.py +++ b/sleekxmpp/xmlstream/xmlstream.py @@ -460,9 +460,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) -- cgit v1.2.3 From d261318e1a80b53abd677eb30f7bc5f91bae9dd6 Mon Sep 17 00:00:00 2001 From: Oleg Antonyan Date: Thu, 27 Nov 2014 07:11:06 +0200 Subject: In queues added option to remove first element on addind new if queue is full --- sleekxmpp/util/__init__.py | 9 +++++++-- sleekxmpp/xmlstream/xmlstream.py | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) 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/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py index e011cf3d..dc9928f8 100644 --- a/sleekxmpp/xmlstream/xmlstream.py +++ b/sleekxmpp/xmlstream/xmlstream.py @@ -291,7 +291,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() -- cgit v1.2.3 From 063e73c0d23d9729e9fb8b4bf4e6d8bd5b959c17 Mon Sep 17 00:00:00 2001 From: mathieui Date: Thu, 11 Dec 2014 14:41:42 +0100 Subject: Fix the element name for retrieving certs in XEP-0257 And s/258/257/ in the XEP description --- sleekxmpp/plugins/xep_0257/stanza.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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() -- cgit v1.2.3 From 61127f521d7d810a14bb6f9b017d7834584c4206 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Thu, 22 Jan 2015 11:09:47 +0530 Subject: Added PyCharm's .idea folder to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) 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/ -- cgit v1.2.3 From 2f4d811db490a69b2ba338aa45171ac15c9b5c4d Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Thu, 22 Jan 2015 11:13:03 +0530 Subject: Fixed a typo in docs/guide_xep_0030.rst --- docs/guide_xep_0030.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 -- cgit v1.2.3 From 1ce42d3a2fe62f82030268923d307cb35896d4d4 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Thu, 22 Jan 2015 11:30:38 +0530 Subject: Boilerplate example. --- examples/http_over_xmpp_transport.py | 69 ++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 examples/http_over_xmpp_transport.py diff --git a/examples/http_over_xmpp_transport.py b/examples/http_over_xmpp_transport.py new file mode 100644 index 00000000..7a60c6fc --- /dev/null +++ b/examples/http_over_xmpp_transport.py @@ -0,0 +1,69 @@ +from sleekxmpp import ClientXMPP +import logging + + +# def tracefunc(frame, event, arg, indent=[0]): +# prefix = "/Users/sangeeth/code/SleekXMPP/sleekxmpp/" +# if not frame.f_code.co_filename.startswith(prefix): +# return tracefunc +# if event == "call": +# indent[0] += 2 +# cn = getattr( +# getattr(frame.f_locals.get("self"), "__class__", None), +# "__name__", None +# ) +# print "{}{} {} {}".format( +# "." * indent[0], frame.f_code.co_filename[len(prefix):], +# cn, frame.f_code.co_name +# ) +# elif event == "return": +# indent[0] -= 2 +# return tracefunc +# +# +# import sys +# sys.settrace(tracefunc) + + +class HTTPOverXMPPClient(ClientXMPP): + def __init__(self, jid, password): + ClientXMPP.__init__(self, jid, password) + + # self.register_plugin('xep_0030') # Service Discovery + # self.register_plugin('xep_0004') # Data Forms + # self.register_plugin('xep_0060') # PubSub + # self.register_plugin('xep_0199') # XMPP Ping + # + # self.add_event_handler("session_start", self.session_start) + # self.add_event_handler("connected", self.connected) + # + # def session_start(self, event): + # print "Client::session_start()" + # self.send_presence() + # print self.get_roster() + # + # def connected(self, event): + # print "Client::connected()" + + +def get_cred(filename="/tmp/.cred"): + with open(filename, "r") as f: + return f.readline().split() + + +if __name__ == "__main__": + + logging.basicConfig( + level=logging.DEBUG, format='%(levelname)-8s %(message)s' + ) + + jid, password = get_cred() + xmpp = HTTPOverXMPPClient(jid, password) + if xmpp.connect(("talk.l.google.com", 5222)): + print "Connected!" + xmpp.process(block=True) + else: + print "Not connected!" + print "Goodbye...." + + -- cgit v1.2.3 From 8afba7de853be4eb3c5cb2467fa17db64cf84abc Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Thu, 22 Jan 2015 16:38:16 +0530 Subject: renamed example for convenience. --- examples/http_over_xmpp.py | 70 ++++++++++++++++++++++++++++++++++++ examples/http_over_xmpp_transport.py | 69 ----------------------------------- 2 files changed, 70 insertions(+), 69 deletions(-) create mode 100644 examples/http_over_xmpp.py delete mode 100644 examples/http_over_xmpp_transport.py diff --git a/examples/http_over_xmpp.py b/examples/http_over_xmpp.py new file mode 100644 index 00000000..5de8713e --- /dev/null +++ b/examples/http_over_xmpp.py @@ -0,0 +1,70 @@ +from sleekxmpp import ClientXMPP +import logging + + +# def tracefunc(frame, event, arg, indent=[0]): +# prefix = "/Users/sangeeth/code/SleekXMPP/sleekxmpp/" +# if not frame.f_code.co_filename.startswith(prefix): +# return tracefunc +# if event == "call": +# indent[0] += 2 +# cn = getattr( +# getattr(frame.f_locals.get("self"), "__class__", None), +# "__name__", None +# ) +# print "{}{} {} {}".format( +# "." * indent[0], frame.f_code.co_filename[len(prefix):], +# cn, frame.f_code.co_name +# ) +# elif event == "return": +# indent[0] -= 2 +# return tracefunc +# +# +# import sys +# sys.settrace(tracefunc) + + +class HTTPOverXMPPClient(ClientXMPP): + def __init__(self, jid, password): + ClientXMPP.__init__(self, jid, password) + + self.register_plugin('xep_0332') # HTTP over XMPP + # self.register_plugin('xep_0030') # Service Discovery + # self.register_plugin('xep_0004') # Data Forms + # self.register_plugin('xep_0060') # PubSub + # self.register_plugin('xep_0199') # XMPP Ping + # + # self.add_event_handler("session_start", self.session_start) + # self.add_event_handler("connected", self.connected) + # + # def session_start(self, event): + # print "Client::session_start()" + # self.send_presence() + # print self.get_roster() + # + # def connected(self, event): + # print "Client::connected()" + + +def get_cred(filename="/tmp/.cred"): + with open(filename, "r") as f: + return f.readline().split() + + +if __name__ == "__main__": + + logging.basicConfig( + level=logging.DEBUG, format='%(levelname)-8s %(message)s' + ) + + jid, password = get_cred() + xmpp = HTTPOverXMPPClient(jid, password) + if xmpp.connect(("talk.l.google.com", 5222)): + print "Connected!" + xmpp.process(block=True) + else: + print "Not connected!" + print "Goodbye...." + + diff --git a/examples/http_over_xmpp_transport.py b/examples/http_over_xmpp_transport.py deleted file mode 100644 index 7a60c6fc..00000000 --- a/examples/http_over_xmpp_transport.py +++ /dev/null @@ -1,69 +0,0 @@ -from sleekxmpp import ClientXMPP -import logging - - -# def tracefunc(frame, event, arg, indent=[0]): -# prefix = "/Users/sangeeth/code/SleekXMPP/sleekxmpp/" -# if not frame.f_code.co_filename.startswith(prefix): -# return tracefunc -# if event == "call": -# indent[0] += 2 -# cn = getattr( -# getattr(frame.f_locals.get("self"), "__class__", None), -# "__name__", None -# ) -# print "{}{} {} {}".format( -# "." * indent[0], frame.f_code.co_filename[len(prefix):], -# cn, frame.f_code.co_name -# ) -# elif event == "return": -# indent[0] -= 2 -# return tracefunc -# -# -# import sys -# sys.settrace(tracefunc) - - -class HTTPOverXMPPClient(ClientXMPP): - def __init__(self, jid, password): - ClientXMPP.__init__(self, jid, password) - - # self.register_plugin('xep_0030') # Service Discovery - # self.register_plugin('xep_0004') # Data Forms - # self.register_plugin('xep_0060') # PubSub - # self.register_plugin('xep_0199') # XMPP Ping - # - # self.add_event_handler("session_start", self.session_start) - # self.add_event_handler("connected", self.connected) - # - # def session_start(self, event): - # print "Client::session_start()" - # self.send_presence() - # print self.get_roster() - # - # def connected(self, event): - # print "Client::connected()" - - -def get_cred(filename="/tmp/.cred"): - with open(filename, "r") as f: - return f.readline().split() - - -if __name__ == "__main__": - - logging.basicConfig( - level=logging.DEBUG, format='%(levelname)-8s %(message)s' - ) - - jid, password = get_cred() - xmpp = HTTPOverXMPPClient(jid, password) - if xmpp.connect(("talk.l.google.com", 5222)): - print "Connected!" - xmpp.process(block=True) - else: - print "Not connected!" - print "Goodbye...." - - -- cgit v1.2.3 From 4a8951c4eeb34506d5d057a00e221500a1835c85 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Thu, 22 Jan 2015 16:39:27 +0530 Subject: added xep_0332 to plugins --- sleekxmpp/plugins/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sleekxmpp/plugins/__init__.py b/sleekxmpp/plugins/__init__.py index 951f31eb..2c90d357 100644 --- a/sleekxmpp/plugins/__init__.py +++ b/sleekxmpp/plugins/__init__.py @@ -83,4 +83,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 ] -- cgit v1.2.3 From ecd124dd068ea381555fca3c42c402c46da0cba1 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Thu, 22 Jan 2015 16:40:03 +0530 Subject: Boilerplate for xep_0332 --- sleekxmpp/plugins/xep_0332/__init__.py | 15 ++++++++++++++ sleekxmpp/plugins/xep_0332/http.py | 37 ++++++++++++++++++++++++++++++++++ sleekxmpp/plugins/xep_0332/stanza.py | 19 +++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 sleekxmpp/plugins/xep_0332/__init__.py create mode 100644 sleekxmpp/plugins/xep_0332/http.py create mode 100644 sleekxmpp/plugins/xep_0332/stanza.py diff --git a/sleekxmpp/plugins/xep_0332/__init__.py b/sleekxmpp/plugins/xep_0332/__init__.py new file mode 100644 index 00000000..3f80ce82 --- /dev/null +++ b/sleekxmpp/plugins/xep_0332/__init__.py @@ -0,0 +1,15 @@ +""" + 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.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..7b9628e3 --- /dev/null +++ b/sleekxmpp/plugins/xep_0332/http.py @@ -0,0 +1,37 @@ +""" + 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.plugins.base import BasePlugin + + +log = logging.getLogger(__name__) + + +class XEP_0332(BasePlugin): + """ + XEP-0332: HTTP over XMPP transport + """ + + name = 'xep_0332' + description = 'XEP-0332: HTTP over XMPP transport' + dependencies = set(['xep_0030', 'xep_0047', 'xep_0131']) #: xep 1, 137 and 166 are missing + default_config = {} + + def plugin_init(self): + log.debug("XEP_0332:: plugin_init()") + + def plugin_end(self): + log.debug("XEP_0332:: plugin_end()") + + def session_bind(self, jid): + log.debug("XEP_0332:: session_bind()") + diff --git a/sleekxmpp/plugins/xep_0332/stanza.py b/sleekxmpp/plugins/xep_0332/stanza.py new file mode 100644 index 00000000..9d651d5d --- /dev/null +++ b/sleekxmpp/plugins/xep_0332/stanza.py @@ -0,0 +1,19 @@ +""" + 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 Req(ElementBase): + pass + + +class Resp(ElementBase): + pass -- cgit v1.2.3 From be76dda21d93fdca8629d4b6d926d1ddd5c396af Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Fri, 23 Jan 2015 10:29:21 +0530 Subject: Added xep_0332 to setup --- setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.py b/setup.py index cc4fdd5b..566683b7 100755 --- a/setup.py +++ b/setup.py @@ -123,6 +123,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', -- cgit v1.2.3 From 0fe057b5c3f462275cf8dbf321c2ebec61de1bfe Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Tue, 27 Jan 2015 15:13:57 +0530 Subject: Boilerplate for Stanzas - request and response --- sleekxmpp/plugins/xep_0332/__init__.py | 1 + sleekxmpp/plugins/xep_0332/stanza.py | 19 ------------------- sleekxmpp/plugins/xep_0332/stanza/__init__.py | 12 ++++++++++++ sleekxmpp/plugins/xep_0332/stanza/request.py | 21 +++++++++++++++++++++ sleekxmpp/plugins/xep_0332/stanza/response.py | 21 +++++++++++++++++++++ 5 files changed, 55 insertions(+), 19 deletions(-) delete mode 100644 sleekxmpp/plugins/xep_0332/stanza.py create mode 100644 sleekxmpp/plugins/xep_0332/stanza/__init__.py create mode 100644 sleekxmpp/plugins/xep_0332/stanza/request.py create mode 100644 sleekxmpp/plugins/xep_0332/stanza/response.py diff --git a/sleekxmpp/plugins/xep_0332/__init__.py b/sleekxmpp/plugins/xep_0332/__init__.py index 3f80ce82..bdb951fc 100644 --- a/sleekxmpp/plugins/xep_0332/__init__.py +++ b/sleekxmpp/plugins/xep_0332/__init__.py @@ -11,5 +11,6 @@ from sleekxmpp.plugins.base import register_plugin from sleekxmpp.plugins.xep_0332.http import XEP_0332 +from sleekxmpp.plugins.xep_0332 import stanza register_plugin(XEP_0332) diff --git a/sleekxmpp/plugins/xep_0332/stanza.py b/sleekxmpp/plugins/xep_0332/stanza.py deleted file mode 100644 index 9d651d5d..00000000 --- a/sleekxmpp/plugins/xep_0332/stanza.py +++ /dev/null @@ -1,19 +0,0 @@ -""" - 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 Req(ElementBase): - pass - - -class Resp(ElementBase): - pass diff --git a/sleekxmpp/plugins/xep_0332/stanza/__init__.py b/sleekxmpp/plugins/xep_0332/stanza/__init__.py new file mode 100644 index 00000000..5d686710 --- /dev/null +++ b/sleekxmpp/plugins/xep_0332/stanza/__init__.py @@ -0,0 +1,12 @@ +""" + 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 * +from sleekxmpp.plugins.xep_0332.stanza.response import * diff --git a/sleekxmpp/plugins/xep_0332/stanza/request.py b/sleekxmpp/plugins/xep_0332/stanza/request.py new file mode 100644 index 00000000..3d75b534 --- /dev/null +++ b/sleekxmpp/plugins/xep_0332/stanza/request.py @@ -0,0 +1,21 @@ +""" + 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 Iq +from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin +from sleekxmpp.plugins.xep_0131.stanza import Headers + + +class Request(ElementBase): + pass + + +register_stanza_plugin(Iq, Request) +register_stanza_plugin(Request, Headers) diff --git a/sleekxmpp/plugins/xep_0332/stanza/response.py b/sleekxmpp/plugins/xep_0332/stanza/response.py new file mode 100644 index 00000000..7f8180bb --- /dev/null +++ b/sleekxmpp/plugins/xep_0332/stanza/response.py @@ -0,0 +1,21 @@ +""" + 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 Iq +from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin +from sleekxmpp.plugins.xep_0131.stanza import Headers + + +class Response(ElementBase): + pass + + +register_stanza_plugin(Iq, Response) +register_stanza_plugin(Response, Headers) -- cgit v1.2.3 From e1f25604ecbf5d6c196080cd8394191c7ea459c9 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Wed, 28 Jan 2015 14:52:15 +0530 Subject: Added callbacks, registered stanzas, added features, etc. --- sleekxmpp/plugins/xep_0332/__init__.py | 2 +- sleekxmpp/plugins/xep_0332/http.py | 40 +++++++++++++++++++++++++++ sleekxmpp/plugins/xep_0332/stanza/__init__.py | 3 +- sleekxmpp/plugins/xep_0332/stanza/request.py | 40 +++++++++++++++++++++++---- sleekxmpp/plugins/xep_0332/stanza/response.py | 37 +++++++++++++++++++++---- 5 files changed, 107 insertions(+), 15 deletions(-) diff --git a/sleekxmpp/plugins/xep_0332/__init__.py b/sleekxmpp/plugins/xep_0332/__init__.py index bdb951fc..cdbfa5d5 100644 --- a/sleekxmpp/plugins/xep_0332/__init__.py +++ b/sleekxmpp/plugins/xep_0332/__init__.py @@ -11,6 +11,6 @@ from sleekxmpp.plugins.base import register_plugin from sleekxmpp.plugins.xep_0332.http import XEP_0332 -from sleekxmpp.plugins.xep_0332 import stanza +# from sleekxmpp.plugins.xep_0332 import stanza register_plugin(XEP_0332) diff --git a/sleekxmpp/plugins/xep_0332/http.py b/sleekxmpp/plugins/xep_0332/http.py index 7b9628e3..f91cc1ff 100644 --- a/sleekxmpp/plugins/xep_0332/http.py +++ b/sleekxmpp/plugins/xep_0332/http.py @@ -10,8 +10,20 @@ 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 NAMESPACE +from sleekxmpp.plugins.xep_0332.stanza.request import Request +from sleekxmpp.plugins.xep_0332.stanza.response import Response + +from sleekxmpp.plugins.xep_0131.stanza import Headers + log = logging.getLogger(__name__) @@ -29,9 +41,37 @@ class XEP_0332(BasePlugin): def plugin_init(self): log.debug("XEP_0332:: plugin_init()") + self.xmpp.register_handler(Callback( + 'HTTP Request', StanzaPath('iq/req'), self._handle_request + )) + self.xmpp.register_handler(Callback( + 'HTTP Response', StanzaPath('iq/resp'), self._handle_response + )) + + register_stanza_plugin(Iq, Request) + register_stanza_plugin(Iq, Response) + register_stanza_plugin(Request, Headers) + register_stanza_plugin(Response, Headers) + def plugin_end(self): log.debug("XEP_0332:: plugin_end()") + self.xmpp.remove_handler('HTTP Request') + self.xmpp.remove_handler('HTTP Response') + self.xmpp['xep_0030'].del_feature(NAMESPACE) def session_bind(self, jid): log.debug("XEP_0332:: session_bind()") + self.xmpp['xep_0030'].add_feature(NAMESPACE) + + def _handle_request(self): + log.debug("XEP_0332:: _handle_request()") + + def _handle_response(self): + log.debug("XEP_0332:: _handle_response()") + + def send_request(self, method=None, resource=None, headers=None, data=None, **kwargs): + log.debug("XEP_0332:: send_request()") + + def send_response(self, status_code=None, headers=None, data=None, **kwargs): + log.debug("XEP_0332:: send_response()") diff --git a/sleekxmpp/plugins/xep_0332/stanza/__init__.py b/sleekxmpp/plugins/xep_0332/stanza/__init__.py index 5d686710..5d9283c7 100644 --- a/sleekxmpp/plugins/xep_0332/stanza/__init__.py +++ b/sleekxmpp/plugins/xep_0332/stanza/__init__.py @@ -8,5 +8,4 @@ See the file LICENSE for copying permission. """ -from sleekxmpp.plugins.xep_0332.stanza.request import * -from sleekxmpp.plugins.xep_0332.stanza.response import * +NAMESPACE = 'urn:xmpp:http' diff --git a/sleekxmpp/plugins/xep_0332/stanza/request.py b/sleekxmpp/plugins/xep_0332/stanza/request.py index 3d75b534..ea2650c0 100644 --- a/sleekxmpp/plugins/xep_0332/stanza/request.py +++ b/sleekxmpp/plugins/xep_0332/stanza/request.py @@ -8,14 +8,42 @@ See the file LICENSE for copying permission. """ -from sleekxmpp import Iq -from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin -from sleekxmpp.plugins.xep_0131.stanza import Headers +from sleekxmpp.xmlstream import ElementBase +from sleekxmpp.plugins.xep_0332.stanza import NAMESPACE class Request(ElementBase): - pass + """ + 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: + + + +
b.com
+
+
+
+ + + + +
b.com
+
text/html
+
...
+
+ + <html><header/><body><p>Beautiful home page.</p></body></html> + +
+
+ """ + + name = 'request' + namespace = NAMESPACE + interfaces = set(('method', 'resource', 'version')) + plugin_attrib = 'req' -register_stanza_plugin(Iq, Request) -register_stanza_plugin(Request, Headers) diff --git a/sleekxmpp/plugins/xep_0332/stanza/response.py b/sleekxmpp/plugins/xep_0332/stanza/response.py index 7f8180bb..4dc14344 100644 --- a/sleekxmpp/plugins/xep_0332/stanza/response.py +++ b/sleekxmpp/plugins/xep_0332/stanza/response.py @@ -8,14 +8,39 @@ See the file LICENSE for copying permission. """ -from sleekxmpp import Iq -from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin -from sleekxmpp.plugins.xep_0131.stanza import Headers +from sleekxmpp.xmlstream import ElementBase +from sleekxmpp.plugins.xep_0332.stanza import NAMESPACE class Response(ElementBase): - pass + """ + 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. -register_stanza_plugin(Iq, Response) -register_stanza_plugin(Response, Headers) + Examples: + + + +
Fri, 03 May 2013 16:39:54GMT-4
+
Clayster
+
text/turtle
+
...
+
Close
+
+ + + ... + + +
+
+ """ + + name = 'response' + namespace = NAMESPACE + interfaces = set(('statusCode', 'statusMessage', 'version')) + plugin_attrib = 'resp' -- cgit v1.2.3 From a96f608469e74d39d3e7a2a86399dbb724ffadec Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Thu, 29 Jan 2015 08:33:40 +0530 Subject: Composing request and response. --- sleekxmpp/plugins/xep_0332/__init__.py | 3 +- sleekxmpp/plugins/xep_0332/http.py | 94 +++++++++++++++++++++------ sleekxmpp/plugins/xep_0332/stanza/__init__.py | 4 +- sleekxmpp/plugins/xep_0332/stanza/data.py | 29 +++++++++ sleekxmpp/plugins/xep_0332/stanza/request.py | 26 +++++++- sleekxmpp/plugins/xep_0332/stanza/response.py | 17 ++++- 6 files changed, 146 insertions(+), 27 deletions(-) create mode 100644 sleekxmpp/plugins/xep_0332/stanza/data.py diff --git a/sleekxmpp/plugins/xep_0332/__init__.py b/sleekxmpp/plugins/xep_0332/__init__.py index cdbfa5d5..27755faa 100644 --- a/sleekxmpp/plugins/xep_0332/__init__.py +++ b/sleekxmpp/plugins/xep_0332/__init__.py @@ -10,7 +10,8 @@ from sleekxmpp.plugins.base import register_plugin +from sleekxmpp.plugins.xep_0332 import stanza from sleekxmpp.plugins.xep_0332.http import XEP_0332 -# from sleekxmpp.plugins.xep_0332 import stanza + register_plugin(XEP_0332) diff --git a/sleekxmpp/plugins/xep_0332/http.py b/sleekxmpp/plugins/xep_0332/http.py index f91cc1ff..166e6ec3 100644 --- a/sleekxmpp/plugins/xep_0332/http.py +++ b/sleekxmpp/plugins/xep_0332/http.py @@ -17,11 +17,7 @@ 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 NAMESPACE -from sleekxmpp.plugins.xep_0332.stanza.request import Request -from sleekxmpp.plugins.xep_0332.stanza.response import Response - +from sleekxmpp.plugins.xep_0332.stanza import Request, Response, Data from sleekxmpp.plugins.xep_0131.stanza import Headers @@ -35,8 +31,23 @@ class XEP_0332(BasePlugin): name = 'xep_0332' description = 'XEP-0332: HTTP over XMPP transport' - dependencies = set(['xep_0030', 'xep_0047', 'xep_0131']) #: xep 1, 137 and 166 are missing - default_config = {} + + #: 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): log.debug("XEP_0332:: plugin_init()") @@ -48,30 +59,75 @@ class XEP_0332(BasePlugin): 'HTTP Response', StanzaPath('iq/resp'), self._handle_response )) - register_stanza_plugin(Iq, Request) - register_stanza_plugin(Iq, Response) - register_stanza_plugin(Request, Headers) - register_stanza_plugin(Response, Headers) + register_stanza_plugin(Iq, Request, iterable=True) + register_stanza_plugin(Iq, Response, iterable=True) + register_stanza_plugin(Request, Headers, iterable=True) + register_stanza_plugin(Request, Data, iterable=True) + register_stanza_plugin(Response, Headers, iterable=True) + register_stanza_plugin(Response, Data, iterable=True) + + # TODO: Should we register any api's here? self.api.register() def plugin_end(self): log.debug("XEP_0332:: plugin_end()") self.xmpp.remove_handler('HTTP Request') self.xmpp.remove_handler('HTTP Response') - self.xmpp['xep_0030'].del_feature(NAMESPACE) + 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): log.debug("XEP_0332:: session_bind()") - self.xmpp['xep_0030'].add_feature(NAMESPACE) - - def _handle_request(self): + 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): log.debug("XEP_0332:: _handle_request()") + print iq - def _handle_response(self): + def _handle_response(self, iq): log.debug("XEP_0332:: _handle_response()") + print iq - def send_request(self, method=None, resource=None, headers=None, data=None, **kwargs): + def send_request(self, to=None, method=None, resource=None, headers=None, + data=None, **kwargs): log.debug("XEP_0332:: send_request()") - - def send_response(self, status_code=None, headers=None, data=None, **kwargs): + iq = self.xmpp.Iq() + iq['from'] = self.xmpp.boundjid + iq['to'] = to + iq['type'] = 'set' + iq['req']['headers'] = headers + iq['req']['method'] = method + iq['req']['resource'] = resource + iq['req']['version'] = '1.1' # TODO: set this implicitly + iq['req']['data'] = data + print iq + # 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, headers=None, data=None, + **kwargs): log.debug("XEP_0332:: send_response()") + iq = self.xmpp.Iq() + iq['from'] = self.xmpp.boundjid + iq['to'] = to + iq['type'] = 'result' + iq['resp']['headers'] = headers + iq['resp']['code'] = code + iq['resp']['version'] = '1.1' # TODO: set this implicitly + iq['resp']['data'] = data + print iq + # 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 index 5d9283c7..eeab3f31 100644 --- a/sleekxmpp/plugins/xep_0332/stanza/__init__.py +++ b/sleekxmpp/plugins/xep_0332/stanza/__init__.py @@ -8,4 +8,6 @@ See the file LICENSE for copying permission. """ -NAMESPACE = 'urn:xmpp:http' +from sleekxmpp.plugins.xep_0332.stanza.request import Request +from sleekxmpp.plugins.xep_0332.stanza.response import Response +from sleekxmpp.plugins.xep_0332.stanza.data import Data diff --git a/sleekxmpp/plugins/xep_0332/stanza/data.py b/sleekxmpp/plugins/xep_0332/stanza/data.py new file mode 100644 index 00000000..e77d2585 --- /dev/null +++ b/sleekxmpp/plugins/xep_0332/stanza/data.py @@ -0,0 +1,29 @@ +""" + 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 Data(ElementBase): + """ + The data element. + """ + name = 'data' + namespace = '' + interfaces = set(['data']) + plugin_attrib = 'data' + + def get_data(self): + print "Data:: get_data()" + + def set_data(self, data, encoding='text'): + print "Data:: set_data()" + 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 index ea2650c0..51e565e5 100644 --- a/sleekxmpp/plugins/xep_0332/stanza/request.py +++ b/sleekxmpp/plugins/xep_0332/stanza/request.py @@ -9,7 +9,6 @@ """ from sleekxmpp.xmlstream import ElementBase -from sleekxmpp.plugins.xep_0332.stanza import NAMESPACE class Request(ElementBase): @@ -43,7 +42,28 @@ class Request(ElementBase): """ name = 'request' - namespace = NAMESPACE - interfaces = set(('method', 'resource', 'version')) + namespace = 'urn:xmpp:http' + interfaces = set(['method', 'resource', 'version']) plugin_attrib = 'req' + def get_method(self): + print "Request:: get_method()" + + def set_method(self, method): + print "Request:: set_method()" + self._set_attr('method', method) + + def get_resource(self): + print "Request:: get_resource()" + + def set_resource(self, resource): + print "Request:: set_resource()" + self._set_attr('resource', resource) + + def get_version(self): + print "Request:: get_version()" + + def set_version(self, version='1.1'): + print "Request:: set_version()" + self._set_attr('version', version) + diff --git a/sleekxmpp/plugins/xep_0332/stanza/response.py b/sleekxmpp/plugins/xep_0332/stanza/response.py index 4dc14344..d863efa3 100644 --- a/sleekxmpp/plugins/xep_0332/stanza/response.py +++ b/sleekxmpp/plugins/xep_0332/stanza/response.py @@ -9,7 +9,6 @@ """ from sleekxmpp.xmlstream import ElementBase -from sleekxmpp.plugins.xep_0332.stanza import NAMESPACE class Response(ElementBase): @@ -41,6 +40,18 @@ class Response(ElementBase): """ name = 'response' - namespace = NAMESPACE - interfaces = set(('statusCode', 'statusMessage', 'version')) + namespace = 'urn:xmpp:http' + interfaces = set(['code', 'version']) plugin_attrib = 'resp' + + def get_code(self): + print "Response:: get_code()" + + def set_code(self, code): + print "Response:: set_code()" + self._set_attr('statusCode', str(code)) + self._set_attr('statusMessage', str(code)) + + def set_version(self, version='1.1'): + print "Response:: set_version()" + self._set_attr('version', version) -- cgit v1.2.3 From c16b86220047e2a2b77c585d6b0d72f5087c1371 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Tue, 3 Feb 2015 12:33:25 +0530 Subject: Raise http_request and http_response events. --- sleekxmpp/plugins/xep_0332/http.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/sleekxmpp/plugins/xep_0332/http.py b/sleekxmpp/plugins/xep_0332/http.py index 166e6ec3..06ba1477 100644 --- a/sleekxmpp/plugins/xep_0332/http.py +++ b/sleekxmpp/plugins/xep_0332/http.py @@ -91,10 +91,12 @@ class XEP_0332(BasePlugin): def _handle_request(self, iq): log.debug("XEP_0332:: _handle_request()") print iq + self.xmpp.event('http_request', iq) def _handle_response(self, iq): log.debug("XEP_0332:: _handle_response()") print iq + self.xmpp.event('http_response', iq) def send_request(self, to=None, method=None, resource=None, headers=None, data=None, **kwargs): @@ -107,12 +109,13 @@ class XEP_0332(BasePlugin): iq['req']['method'] = method iq['req']['resource'] = resource iq['req']['version'] = '1.1' # TODO: set this implicitly - iq['req']['data'] = data + if data: + iq['req']['data'] = data print iq - # 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)) + 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, headers=None, data=None, **kwargs): @@ -124,7 +127,8 @@ class XEP_0332(BasePlugin): iq['resp']['headers'] = headers iq['resp']['code'] = code iq['resp']['version'] = '1.1' # TODO: set this implicitly - iq['resp']['data'] = data + if data: + iq['resp']['data'] = data print iq # return iq.send(timeout=kwargs.get('timeout', None), # block=kwargs.get('block', True), -- cgit v1.2.3 From 8bc70264efd17390fbe795e359a7cbb0442fd0d1 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Thu, 5 Feb 2015 17:35:04 +0530 Subject: misc updates.. --- sleekxmpp/plugins/xep_0332/http.py | 62 +++++++++++++++------------ sleekxmpp/plugins/xep_0332/stanza/data.py | 9 ++-- sleekxmpp/plugins/xep_0332/stanza/request.py | 14 +++--- sleekxmpp/plugins/xep_0332/stanza/response.py | 14 +++--- 4 files changed, 53 insertions(+), 46 deletions(-) diff --git a/sleekxmpp/plugins/xep_0332/http.py b/sleekxmpp/plugins/xep_0332/http.py index 06ba1477..36b6995f 100644 --- a/sleekxmpp/plugins/xep_0332/http.py +++ b/sleekxmpp/plugins/xep_0332/http.py @@ -50,26 +50,21 @@ class XEP_0332(BasePlugin): } def plugin_init(self): - log.debug("XEP_0332:: plugin_init()") - self.xmpp.register_handler(Callback( 'HTTP Request', StanzaPath('iq/req'), self._handle_request )) self.xmpp.register_handler(Callback( 'HTTP Response', StanzaPath('iq/resp'), self._handle_response )) - register_stanza_plugin(Iq, Request, iterable=True) register_stanza_plugin(Iq, Response, iterable=True) register_stanza_plugin(Request, Headers, iterable=True) register_stanza_plugin(Request, Data, iterable=True) register_stanza_plugin(Response, Headers, iterable=True) register_stanza_plugin(Response, Data, iterable=True) - # TODO: Should we register any api's here? self.api.register() def plugin_end(self): - log.debug("XEP_0332:: plugin_end()") self.xmpp.remove_handler('HTTP Request') self.xmpp.remove_handler('HTTP Response') self.xmpp['xep_0030'].del_feature('urn:xmpp:http') @@ -79,7 +74,6 @@ class XEP_0332(BasePlugin): ) def session_bind(self, jid): - log.debug("XEP_0332:: session_bind()") self.xmpp['xep_0030'].add_feature('urn:xmpp:http') for header in self.supported_headers: self.xmpp['xep_0030'].add_feature( @@ -89,18 +83,13 @@ class XEP_0332(BasePlugin): # self.xmpp['xep_0131'].supported_headers.add(header) def _handle_request(self, iq): - log.debug("XEP_0332:: _handle_request()") - print iq self.xmpp.event('http_request', iq) def _handle_response(self, iq): - log.debug("XEP_0332:: _handle_response()") - print iq self.xmpp.event('http_response', iq) def send_request(self, to=None, method=None, resource=None, headers=None, data=None, **kwargs): - log.debug("XEP_0332:: send_request()") iq = self.xmpp.Iq() iq['from'] = self.xmpp.boundjid iq['to'] = to @@ -109,29 +98,46 @@ class XEP_0332(BasePlugin): iq['req']['method'] = method iq['req']['resource'] = resource iq['req']['version'] = '1.1' # TODO: set this implicitly - if data: + if data is not None: iq['req']['data'] = data - print iq - 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, headers=None, data=None, - **kwargs): - log.debug("XEP_0332:: send_response()") + 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['resp']['headers'] = headers iq['resp']['code'] = code + iq['resp']['message'] = message iq['resp']['version'] = '1.1' # TODO: set this implicitly - if data: + if data is not None: iq['resp']['data'] = data - print iq - # 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)) - + 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['type'] = 'error' + iq['from'] = self.xmpp.boundjid + iq['to'] = to + iq['error']['code'] = ecode + iq['error']['type'] = etype + iq['error']['condition'] = econd + 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/data.py b/sleekxmpp/plugins/xep_0332/stanza/data.py index e77d2585..9a08426b 100644 --- a/sleekxmpp/plugins/xep_0332/stanza/data.py +++ b/sleekxmpp/plugins/xep_0332/stanza/data.py @@ -16,14 +16,15 @@ class Data(ElementBase): The data element. """ name = 'data' - namespace = '' + namespace = 'urn:xmpp:http' interfaces = set(['data']) plugin_attrib = 'data' + is_extension = True - def get_data(self): - print "Data:: get_data()" + 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'): - print "Data:: set_data()" 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 index 51e565e5..07618727 100644 --- a/sleekxmpp/plugins/xep_0332/stanza/request.py +++ b/sleekxmpp/plugins/xep_0332/stanza/request.py @@ -20,7 +20,7 @@ class Request(ElementBase): Examples: - +
b.com
@@ -35,7 +35,7 @@ class Request(ElementBase):
...
- <html><header/><body><p>Beautiful home page.</p></body></html> + ...
@@ -47,23 +47,19 @@ class Request(ElementBase): plugin_attrib = 'req' def get_method(self): - print "Request:: get_method()" + return self._get_attr('method', None) def set_method(self, method): - print "Request:: set_method()" self._set_attr('method', method) def get_resource(self): - print "Request:: get_resource()" + return self._get_attr('resource', None) def set_resource(self, resource): - print "Request:: set_resource()" self._set_attr('resource', resource) def get_version(self): - print "Request:: get_version()" + return self._get_attr('version', None) def set_version(self, version='1.1'): - print "Request:: set_version()" self._set_attr('version', version) - diff --git a/sleekxmpp/plugins/xep_0332/stanza/response.py b/sleekxmpp/plugins/xep_0332/stanza/response.py index d863efa3..0fc46de8 100644 --- a/sleekxmpp/plugins/xep_0332/stanza/response.py +++ b/sleekxmpp/plugins/xep_0332/stanza/response.py @@ -41,17 +41,21 @@ class Response(ElementBase): name = 'response' namespace = 'urn:xmpp:http' - interfaces = set(['code', 'version']) + interfaces = set(['code', 'message', 'version']) plugin_attrib = 'resp' def get_code(self): - print "Response:: get_code()" + code = self._get_attr('statusCode', None) + return int(code) if code is not None else code def set_code(self, code): - print "Response:: set_code()" self._set_attr('statusCode', str(code)) - self._set_attr('statusMessage', 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'): - print "Response:: set_version()" self._set_attr('version', version) -- cgit v1.2.3 From 24264d3a07f8a1576637e6a5703682e8735c2be1 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Thu, 5 Feb 2015 18:10:10 +0530 Subject: Updated Example.. --- examples/http_over_xmpp.py | 138 ++++++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 57 deletions(-) diff --git a/examples/http_over_xmpp.py b/examples/http_over_xmpp.py index 5de8713e..5630f778 100644 --- a/examples/http_over_xmpp.py +++ b/examples/http_over_xmpp.py @@ -1,70 +1,94 @@ -from sleekxmpp import ClientXMPP -import logging +#!/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 -# def tracefunc(frame, event, arg, indent=[0]): -# prefix = "/Users/sangeeth/code/SleekXMPP/sleekxmpp/" -# if not frame.f_code.co_filename.startswith(prefix): -# return tracefunc -# if event == "call": -# indent[0] += 2 -# cn = getattr( -# getattr(frame.f_locals.get("self"), "__class__", None), -# "__name__", None -# ) -# print "{}{} {} {}".format( -# "." * indent[0], frame.f_code.co_filename[len(prefix):], -# cn, frame.f_code.co_name -# ) -# elif event == "return": -# indent[0] -= 2 -# return tracefunc -# -# -# import sys -# sys.settrace(tracefunc) +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() + - self.register_plugin('xep_0332') # HTTP over XMPP - # self.register_plugin('xep_0030') # Service Discovery - # self.register_plugin('xep_0004') # Data Forms - # self.register_plugin('xep_0060') # PubSub - # self.register_plugin('xep_0199') # XMPP Ping - # - # self.add_event_handler("session_start", self.session_start) - # self.add_event_handler("connected", self.connected) - # - # def session_start(self, event): - # print "Client::session_start()" - # self.send_presence() - # print self.get_roster() - # - # def connected(self, event): - # print "Client::connected()" - - -def get_cred(filename="/tmp/.cred"): - with open(filename, "r") as f: - return f.readline().split() - - -if __name__ == "__main__": - - logging.basicConfig( - level=logging.DEBUG, format='%(levelname)-8s %(message)s' +if __name__ == '__main__': + + 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') - jid, password = get_cred() - xmpp = HTTPOverXMPPClient(jid, password) - if xmpp.connect(("talk.l.google.com", 5222)): - print "Connected!" + 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...." - + print 'Not connected!' + print 'Goodbye....' -- cgit v1.2.3 From 904480712157d762d35de16ce55202d641a56b3c Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Thu, 5 Feb 2015 18:11:41 +0530 Subject: Added help for running example.. --- examples/http_over_xmpp.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/http_over_xmpp.py b/examples/http_over_xmpp.py index 5630f778..a2fbf664 100644 --- a/examples/http_over_xmpp.py +++ b/examples/http_over_xmpp.py @@ -51,6 +51,13 @@ class HTTPOverXMPPClient(ClientXMPP): 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 -P -i -p [-v] + # + parser = OptionParser() # Output verbosity options. -- cgit v1.2.3 From 69022c6db79131b8f1c95f135a80d2b219a7d13c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Souchon?= Date: Mon, 9 Mar 2015 12:33:18 +0100 Subject: Makes XEP-0009 compatible with Python 3 while maintaining compatibility with Python 2.6 and up. --- sleekxmpp/plugins/xep_0009/remote.py | 54 ++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/sleekxmpp/plugins/xep_0009/remote.py b/sleekxmpp/plugins/xep_0009/remote.py index 8847ff24..f5ed8008 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 '.'. ''' - __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) -- cgit v1.2.3 From 460de7d30138c39482efff1900ec23b23047949d Mon Sep 17 00:00:00 2001 From: mulog1990 Date: Tue, 10 Mar 2015 18:13:53 +0800 Subject: ssl-version not passed to wrap_socket, fixed --- sleekxmpp/xmlstream/xmlstream.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py index e011cf3d..62249f78 100644 --- a/sleekxmpp/xmlstream/xmlstream.py +++ b/sleekxmpp/xmlstream/xmlstream.py @@ -523,7 +523,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,7 +848,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): -- cgit v1.2.3 From 81b7b2c1908e0f6a5435ce67745b5f4dafb59816 Mon Sep 17 00:00:00 2001 From: Richard Kellner Date: Wed, 25 Mar 2015 14:04:46 +0100 Subject: Fixed bug #353 Python3 XEP-0084 error --- examples/set_avatar.py | 2 +- sleekxmpp/plugins/xep_0084/stanza.py | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) 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/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 = '' -- cgit v1.2.3 From 1fe7f5f4e6317c6287f380f56f3ce1ad2faa0638 Mon Sep 17 00:00:00 2001 From: Mike Taylor Date: Sat, 11 Apr 2015 17:45:23 -0400 Subject: Create .travis.yml --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .travis.yml 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 -- cgit v1.2.3 From d19a6e05b27754116b16b01c9b6bfbfe39098fdd Mon Sep 17 00:00:00 2001 From: Mike Taylor Date: Sat, 11 Apr 2015 20:37:05 -0400 Subject: remove python v3.1 - v3.3 from tox.ini --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 -- cgit v1.2.3 From 16bb5e2537d0bffe4c561fa97be8adb12110d41f Mon Sep 17 00:00:00 2001 From: Mike Taylor Date: Sat, 11 Apr 2015 20:38:11 -0400 Subject: bump to version v1.4 --- sleekxmpp/version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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) -- cgit v1.2.3 From 61a7cecb319c5628973906250ed973f21883cfd4 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Wed, 29 Apr 2015 14:44:25 +0530 Subject: Prefixed request, response and data with http. Avoided (plugin_attrib) name collision with other plugins. --- sleekxmpp/plugins/xep_0332/http.py | 56 ++++++++++++++++----------- sleekxmpp/plugins/xep_0332/stanza/__init__.py | 6 +-- sleekxmpp/plugins/xep_0332/stanza/data.py | 4 +- sleekxmpp/plugins/xep_0332/stanza/request.py | 18 ++++++--- sleekxmpp/plugins/xep_0332/stanza/response.py | 13 +++++-- 5 files changed, 59 insertions(+), 38 deletions(-) diff --git a/sleekxmpp/plugins/xep_0332/http.py b/sleekxmpp/plugins/xep_0332/http.py index 36b6995f..acaef505 100644 --- a/sleekxmpp/plugins/xep_0332/http.py +++ b/sleekxmpp/plugins/xep_0332/http.py @@ -17,7 +17,9 @@ 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 Request, Response, Data +from sleekxmpp.plugins.xep_0332.stanza import ( + HTTPRequest, HTTPResponse, HTTPData +) from sleekxmpp.plugins.xep_0131.stanza import Headers @@ -50,18 +52,26 @@ class XEP_0332(BasePlugin): } def plugin_init(self): - self.xmpp.register_handler(Callback( - 'HTTP Request', StanzaPath('iq/req'), self._handle_request - )) - self.xmpp.register_handler(Callback( - 'HTTP Response', StanzaPath('iq/resp'), self._handle_response - )) - register_stanza_plugin(Iq, Request, iterable=True) - register_stanza_plugin(Iq, Response, iterable=True) - register_stanza_plugin(Request, Headers, iterable=True) - register_stanza_plugin(Request, Data, iterable=True) - register_stanza_plugin(Response, Headers, iterable=True) - register_stanza_plugin(Response, Data, iterable=True) + 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): @@ -94,12 +104,12 @@ class XEP_0332(BasePlugin): iq['from'] = self.xmpp.boundjid iq['to'] = to iq['type'] = 'set' - iq['req']['headers'] = headers - iq['req']['method'] = method - iq['req']['resource'] = resource - iq['req']['version'] = '1.1' # TODO: set this implicitly + 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 data is not None: - iq['req']['data'] = data + iq['http-req']['http-data'] = data return iq.send( timeout=kwargs.get('timeout', None), block=kwargs.get('block', True), @@ -113,12 +123,12 @@ class XEP_0332(BasePlugin): iq['from'] = self.xmpp.boundjid iq['to'] = to iq['type'] = 'result' - iq['resp']['headers'] = headers - iq['resp']['code'] = code - iq['resp']['message'] = message - iq['resp']['version'] = '1.1' # TODO: set this implicitly + 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 data is not None: - iq['resp']['data'] = data + iq['http-resp']['http-data'] = data return iq.send( timeout=kwargs.get('timeout', None), block=kwargs.get('block', True), diff --git a/sleekxmpp/plugins/xep_0332/stanza/__init__.py b/sleekxmpp/plugins/xep_0332/stanza/__init__.py index eeab3f31..201824b7 100644 --- a/sleekxmpp/plugins/xep_0332/stanza/__init__.py +++ b/sleekxmpp/plugins/xep_0332/stanza/__init__.py @@ -8,6 +8,6 @@ See the file LICENSE for copying permission. """ -from sleekxmpp.plugins.xep_0332.stanza.request import Request -from sleekxmpp.plugins.xep_0332.stanza.response import Response -from sleekxmpp.plugins.xep_0332.stanza.data import Data +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 index 9a08426b..765536eb 100644 --- a/sleekxmpp/plugins/xep_0332/stanza/data.py +++ b/sleekxmpp/plugins/xep_0332/stanza/data.py @@ -11,14 +11,14 @@ from sleekxmpp.xmlstream import ElementBase -class Data(ElementBase): +class HTTPData(ElementBase): """ The data element. """ name = 'data' namespace = 'urn:xmpp:http' interfaces = set(['data']) - plugin_attrib = 'data' + plugin_attrib = 'http-data' is_extension = True def get_data(self, encoding='text'): diff --git a/sleekxmpp/plugins/xep_0332/stanza/request.py b/sleekxmpp/plugins/xep_0332/stanza/request.py index 07618727..9a298e57 100644 --- a/sleekxmpp/plugins/xep_0332/stanza/request.py +++ b/sleekxmpp/plugins/xep_0332/stanza/request.py @@ -11,16 +11,19 @@ from sleekxmpp.xmlstream import ElementBase -class Request(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`. + 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: - +
b.com
@@ -28,7 +31,10 @@ class Request(ElementBase):
- +
b.com
text/html
@@ -44,7 +50,7 @@ class Request(ElementBase): name = 'request' namespace = 'urn:xmpp:http' interfaces = set(['method', 'resource', 'version']) - plugin_attrib = 'req' + plugin_attrib = 'http-req' def get_method(self): return self._get_attr('method', None) diff --git a/sleekxmpp/plugins/xep_0332/stanza/response.py b/sleekxmpp/plugins/xep_0332/stanza/response.py index 0fc46de8..6804ade9 100644 --- a/sleekxmpp/plugins/xep_0332/stanza/response.py +++ b/sleekxmpp/plugins/xep_0332/stanza/response.py @@ -11,7 +11,7 @@ from sleekxmpp.xmlstream import ElementBase -class Response(ElementBase): +class HTTPResponse(ElementBase): """ When the HTTP Server responds, it does so by sending an `iq` stanza @@ -21,8 +21,13 @@ class Response(ElementBase): in which the original requests were made. Examples: - - + +
Fri, 03 May 2013 16:39:54GMT-4
Clayster
@@ -42,7 +47,7 @@ class Response(ElementBase): name = 'response' namespace = 'urn:xmpp:http' interfaces = set(['code', 'message', 'version']) - plugin_attrib = 'resp' + plugin_attrib = 'http-resp' def get_code(self): code = self._get_attr('statusCode', None) -- cgit v1.2.3 From d60a652259c74aaea4a250c542ce5ceaeb81f177 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Fri, 1 May 2015 14:32:36 +0530 Subject: data need not be prefixed with http.. --- sleekxmpp/plugins/xep_0332/http.py | 4 ++-- sleekxmpp/plugins/xep_0332/stanza/data.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sleekxmpp/plugins/xep_0332/http.py b/sleekxmpp/plugins/xep_0332/http.py index acaef505..5c1cfc0d 100644 --- a/sleekxmpp/plugins/xep_0332/http.py +++ b/sleekxmpp/plugins/xep_0332/http.py @@ -109,7 +109,7 @@ class XEP_0332(BasePlugin): iq['http-req']['resource'] = resource iq['http-req']['version'] = '1.1' # TODO: set this implicitly if data is not None: - iq['http-req']['http-data'] = data + iq['http-req']['data'] = data return iq.send( timeout=kwargs.get('timeout', None), block=kwargs.get('block', True), @@ -128,7 +128,7 @@ class XEP_0332(BasePlugin): iq['http-resp']['message'] = message iq['http-resp']['version'] = '1.1' # TODO: set this implicitly if data is not None: - iq['http-resp']['http-data'] = data + iq['http-resp']['data'] = data return iq.send( timeout=kwargs.get('timeout', None), block=kwargs.get('block', True), diff --git a/sleekxmpp/plugins/xep_0332/stanza/data.py b/sleekxmpp/plugins/xep_0332/stanza/data.py index 765536eb..a3678038 100644 --- a/sleekxmpp/plugins/xep_0332/stanza/data.py +++ b/sleekxmpp/plugins/xep_0332/stanza/data.py @@ -18,7 +18,7 @@ class HTTPData(ElementBase): name = 'data' namespace = 'urn:xmpp:http' interfaces = set(['data']) - plugin_attrib = 'http-data' + plugin_attrib = 'data' is_extension = True def get_data(self, encoding='text'): -- cgit v1.2.3 From 1345b7c1d0b3c121471034521e9028cd43569cb7 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Fri, 1 May 2015 15:34:53 +0530 Subject: Misc updates for send_error() --- sleekxmpp/plugins/xep_0332/http.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sleekxmpp/plugins/xep_0332/http.py b/sleekxmpp/plugins/xep_0332/http.py index 5c1cfc0d..03d88b65 100644 --- a/sleekxmpp/plugins/xep_0332/http.py +++ b/sleekxmpp/plugins/xep_0332/http.py @@ -136,12 +136,12 @@ class XEP_0332(BasePlugin): timeout_callback=kwargs.get('timeout_callback', None) ) - def send_error(self, to=None, ecode=500, etype='wait', + def send_error(self, to=None, ecode='500', etype='wait', econd='internal-server-error', **kwargs): iq = self.xmpp.Iq() - iq['type'] = 'error' iq['from'] = self.xmpp.boundjid iq['to'] = to + iq['type'] = 'error' iq['error']['code'] = ecode iq['error']['type'] = etype iq['error']['condition'] = econd -- cgit v1.2.3 From a0c6bf15e97a71787ffa4fec1850a995ba1d5446 Mon Sep 17 00:00:00 2001 From: Joshua Downer Date: Wed, 13 May 2015 17:24:06 -0400 Subject: Fixed imports Removed unused modules/packages and added getpass, which was missing. --- examples/IoT_TestDevice.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/examples/IoT_TestDevice.py b/examples/IoT_TestDevice.py index cd80cee2..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): """ -- cgit v1.2.3 From ac50fdccfc9f46425c09abab9bffa9aac7f58812 Mon Sep 17 00:00:00 2001 From: Joshua Downer Date: Thu, 14 May 2015 06:26:54 -0400 Subject: xep-0323: unused import --- sleekxmpp/plugins/xep_0323/sensordata.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sleekxmpp/plugins/xep_0323/sensordata.py b/sleekxmpp/plugins/xep_0323/sensordata.py index 30c28504..d610d278 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 -- cgit v1.2.3 From a33bde9cc3df27f0c9ceeeeb9b77284649721b91 Mon Sep 17 00:00:00 2001 From: Joshua Downer Date: Thu, 14 May 2015 06:27:39 -0400 Subject: xep-0323: spelling --- sleekxmpp/plugins/xep_0323/sensordata.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sleekxmpp/plugins/xep_0323/sensordata.py b/sleekxmpp/plugins/xep_0323/sensordata.py index d610d278..3973873d 100644 --- a/sleekxmpp/plugins/xep_0323/sensordata.py +++ b/sleekxmpp/plugins/xep_0323/sensordata.py @@ -160,11 +160,11 @@ class XEP_0323(BasePlugin): 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()) @@ -403,7 +403,7 @@ class XEP_0323(BasePlugin): 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 @@ -447,7 +447,7 @@ class XEP_0323(BasePlugin): 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": @@ -535,14 +535,14 @@ class XEP_0323(BasePlugin): 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: @@ -672,7 +672,7 @@ class XEP_0323(BasePlugin): 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'] -- cgit v1.2.3 From 3590b663ed22a7e6d969b22f22a32bdabbfa98b6 Mon Sep 17 00:00:00 2001 From: Joshua Downer Date: Thu, 14 May 2015 06:27:59 -0400 Subject: xep-0323: removed deadcode --- sleekxmpp/plugins/xep_0323/sensordata.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/sleekxmpp/plugins/xep_0323/sensordata.py b/sleekxmpp/plugins/xep_0323/sensordata.py index 3973873d..87a62980 100644 --- a/sleekxmpp/plugins/xep_0323/sensordata.py +++ b/sleekxmpp/plugins/xep_0323/sensordata.py @@ -107,7 +107,6 @@ class XEP_0323(BasePlugin): default_config = { 'threaded': True -# 'session_db': None } def plugin_init(self): @@ -300,8 +299,6 @@ class XEP_0323(BasePlugin): self.sessions[session]["commTimers"] = {} self.sessions[session]["nodeDone"] = {} - #print("added session: " + str(self.sessions)) - iq.reply() iq['accepted']['seqnr'] = seqnr if not request_delay_sec is None: @@ -318,10 +315,8 @@ class XEP_0323(BasePlugin): 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) @@ -348,7 +343,6 @@ class XEP_0323(BasePlugin): 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) @@ -376,7 +370,6 @@ class XEP_0323(BasePlugin): msg['failure']['done'] = 'true' msg.send() # The session is complete, delete it - #print("del session " + session + " due to timeout") del self.sessions[session] def _event_delayed_req(self, session, process_fields, req_flags): @@ -466,7 +459,6 @@ class XEP_0323(BasePlugin): if (self._all_nodes_done(session)): msg['failure']['done'] = 'true' # The session is complete, delete it - # print("del session " + session + " due to error") del self.sessions[session] msg.send() else: @@ -493,7 +485,6 @@ class XEP_0323(BasePlugin): msg['fields']['done'] = 'true' if (self._all_nodes_done(session)): # The session is complete, delete it - # print("del session " + session + " due to complete") del self.sessions[session] else: # Restart comm timer @@ -663,7 +654,6 @@ class XEP_0323(BasePlugin): 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") -- cgit v1.2.3 From 92901637ec068bb529c7a86fa8e348930f951e78 Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Mon, 25 May 2015 01:01:08 +0200 Subject: Change to roster migration example I did have the chance to test the script yet, but it seems like that line should be outside the for loop. --- examples/migrate_roster.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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(): -- cgit v1.2.3 From 7a908ac07b1344dcbb5ee26e0ad4777decb88889 Mon Sep 17 00:00:00 2001 From: Joshua Downer Date: Thu, 28 May 2015 09:35:50 -0400 Subject: Removed duplicate property --- sleekxmpp/jid.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sleekxmpp/jid.py b/sleekxmpp/jid.py index ac5ba30d..613b272e 100644 --- a/sleekxmpp/jid.py +++ b/sleekxmpp/jid.py @@ -528,10 +528,6 @@ class JID(object): def username(self): 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 +552,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 -- cgit v1.2.3 From 9b25a7cf77b969a2cc923773855cb482c314b511 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Fri, 5 Jun 2015 12:25:41 +0530 Subject: Fixed typo. --- sleekxmpp/xmlstream/cert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) -- cgit v1.2.3 From 224d7ae133828ce2e1c2733f25f41dfce9a5f6ba Mon Sep 17 00:00:00 2001 From: Anirudh Date: Thu, 18 Jun 2015 00:21:19 +0530 Subject: add hash param to file metadata --- sleekxmpp/plugins/xep_0096/file_transfer.py | 1 + 1 file changed, 1 insertion(+) 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') -- cgit v1.2.3 From f00177c0cf5512a86b37ba69fc627ddf92abdc6b Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Fri, 3 Jul 2015 10:47:06 +0530 Subject: Added **kwargs to ClientXMPP, BaseXMPP and XMLStream so that certfile, keyfile and ca_certs can be initialized. --- sleekxmpp/basexmpp.py | 4 ++-- sleekxmpp/clientxmpp.py | 7 ++++--- sleekxmpp/xmlstream/xmlstream.py | 9 +++++---- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py index 8cd61b63..cb72b9bd 100644 --- a/sleekxmpp/basexmpp.py +++ b/sleekxmpp/basexmpp.py @@ -55,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' diff --git a/sleekxmpp/clientxmpp.py b/sleekxmpp/clientxmpp.py index 8db6ef17..31a5a70b 100644 --- a/sleekxmpp/clientxmpp.py +++ b/sleekxmpp/clientxmpp.py @@ -59,14 +59,15 @@ class ClientXMPP(BaseXMPP): :param escape_quotes: **Deprecated.** """ - def __init__(self, jid, password, plugin_config=None, plugin_whitelist=None, escape_quotes=True, sasl_mech=None, - lang='en'): + 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') + BaseXMPP.__init__(self, jid, 'jabber:client', **kwargs) self.escape_quotes = escape_quotes self.plugin_config = plugin_config diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py index f9ec4947..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 -- cgit v1.2.3 From edd9199be83a0d62c5e18f830021bc13c34f2e00 Mon Sep 17 00:00:00 2001 From: elya5 Date: Thu, 9 Jul 2015 17:15:36 +0200 Subject: Fix UnboundlocalError in disco_browser.py example If self.get is in self.info_types and self.items_types, only self['xep_0030'].get_info is executed and not self['xep_0030'].get_items. So the condition in line 129 is successful but items is not assigned. --- examples/disco_browser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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. -- cgit v1.2.3 From be14f0cc52a748134e2f1e77a4d10b11e8ff8b25 Mon Sep 17 00:00:00 2001 From: Robert Robinson Date: Wed, 15 Jul 2015 20:52:06 -0600 Subject: XEP_0050: Form not iterable in command Cannot pass in a form into the initial command and have it show up in the payload of the session. Line 344 makes this possible when following the standard workflow. --- sleekxmpp/plugins/xep_0050/adhoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sleekxmpp/plugins/xep_0050/adhoc.py b/sleekxmpp/plugins/xep_0050/adhoc.py index e5594c3f..5f4ea94c 100644 --- a/sleekxmpp/plugins/xep_0050/adhoc.py +++ b/sleekxmpp/plugins/xep_0050/adhoc.py @@ -101,7 +101,7 @@ class XEP_0050(BasePlugin): self._handle_command)) register_stanza_plugin(Iq, Command) - register_stanza_plugin(Command, Form) + register_stanza_plugin(Command, Form, iterable=True) self.xmpp.add_event_handler('command_execute', self._handle_command_start, -- cgit v1.2.3 From 2042e1a4d5f5f859f5fece69abd42c4bfc1c4087 Mon Sep 17 00:00:00 2001 From: Joshua Downer Date: Mon, 20 Jul 2015 17:34:09 -0400 Subject: iot: only add the 'done' field when all devices are done --- sleekxmpp/plugins/xep_0323/sensordata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sleekxmpp/plugins/xep_0323/sensordata.py b/sleekxmpp/plugins/xep_0323/sensordata.py index 87a62980..a3d4cf34 100644 --- a/sleekxmpp/plugins/xep_0323/sensordata.py +++ b/sleekxmpp/plugins/xep_0323/sensordata.py @@ -482,10 +482,10 @@ class XEP_0323(BasePlugin): if result == "done": self.sessions[session]["commTimers"][nodeId].cancel() self.sessions[session]["nodeDone"][nodeId] = True - msg['fields']['done'] = 'true' if (self._all_nodes_done(session)): # The session is complete, delete it del self.sessions[session] + msg['fields']['done'] = 'true' else: # Restart comm timer self.sessions[session]["commTimers"][nodeId].reset() -- cgit v1.2.3 From f54ebec654fa14ed1db85d141c5268e60ac6d737 Mon Sep 17 00:00:00 2001 From: Michele Cella Date: Fri, 31 Jul 2015 11:55:50 +0200 Subject: Fixes #378: must acquire JID_CACHE_LOCK before adding to JID_CACHE --- sleekxmpp/jid.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/sleekxmpp/jid.py b/sleekxmpp/jid.py index 613b272e..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, -- cgit v1.2.3 From eeab646bfa0d4787a2611f9c07514ddbb10ee441 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Sat, 1 Aug 2015 17:47:03 +0530 Subject: Retaining 'id' in the response and error stanzas --- sleekxmpp/plugins/xep_0332/http.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sleekxmpp/plugins/xep_0332/http.py b/sleekxmpp/plugins/xep_0332/http.py index 03d88b65..92e2546f 100644 --- a/sleekxmpp/plugins/xep_0332/http.py +++ b/sleekxmpp/plugins/xep_0332/http.py @@ -127,6 +127,8 @@ class XEP_0332(BasePlugin): 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( @@ -145,6 +147,8 @@ class XEP_0332(BasePlugin): 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), -- cgit v1.2.3 From a366482551f140e31d56c628eb830602fe049bb5 Mon Sep 17 00:00:00 2001 From: Aleksandr Draga Date: Mon, 10 Aug 2015 15:34:27 +0300 Subject: Add get users by affiliation. --- sleekxmpp/plugins/xep_0045.py | 10 ++++++++++ 1 file changed, 10 insertions(+) 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) -- cgit v1.2.3 From 53b56899a07d6586889cc3e6cce3d64229a7bbeb Mon Sep 17 00:00:00 2001 From: Richard Chan Date: Tue, 25 Aug 2015 18:11:54 +0800 Subject: Do not overwrite telephone numbers; otherwise all TEL/NUMBER received from a server will be blank. --- sleekxmpp/plugins/xep_0054/stanza.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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) -- cgit v1.2.3 From 1a75b7691697549b5e9adae4eba8ac1504ba3095 Mon Sep 17 00:00:00 2001 From: Richard Chan Date: Tue, 25 Aug 2015 18:21:58 +0800 Subject: Only send time if Iq type is get. --- sleekxmpp/plugins/xep_0202/time.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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): """ -- cgit v1.2.3 From 18e5abb9ddc1689e7e5963cf40259a2685eed9b5 Mon Sep 17 00:00:00 2001 From: Sangeeth Saravanaraj Date: Thu, 27 Aug 2015 13:24:01 +0530 Subject: adding 'id' to self['xep_0332'].send_request() --- sleekxmpp/plugins/xep_0332/http.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sleekxmpp/plugins/xep_0332/http.py b/sleekxmpp/plugins/xep_0332/http.py index 92e2546f..70bcafa6 100644 --- a/sleekxmpp/plugins/xep_0332/http.py +++ b/sleekxmpp/plugins/xep_0332/http.py @@ -108,6 +108,8 @@ class XEP_0332(BasePlugin): 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( -- cgit v1.2.3 From 4a24f58be26f8d5040dab2ddc3428bd989296fdb Mon Sep 17 00:00:00 2001 From: Robert Robinson Date: Thu, 3 Sep 2015 10:15:41 -0600 Subject: XEP0050: Add support for payload in completed response When sending the command to complete the task, the adhoc plugin does not provide the ability to send a payload from the _handle_command_complete method. --- sleekxmpp/plugins/xep_0050/adhoc.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sleekxmpp/plugins/xep_0050/adhoc.py b/sleekxmpp/plugins/xep_0050/adhoc.py index 5f4ea94c..7ab659f4 100644 --- a/sleekxmpp/plugins/xep_0050/adhoc.py +++ b/sleekxmpp/plugins/xep_0050/adhoc.py @@ -425,12 +425,25 @@ class XEP_0050(BasePlugin): del self.sessions[sessionid] + payload = session['payload'] + if payload is None: + payload = [] + if not isinstance(payload, list): + payload = [payload] + + for item in payload: + register_stanza_plugin(Command, item.__class__, iterable=True) + iq.reply() iq['command']['node'] = node iq['command']['sessionid'] = sessionid iq['command']['actions'] = [] iq['command']['status'] = 'completed' iq['command']['notes'] = session['notes'] + + for item in payload: + iq['command'].append(item) + iq.send() else: raise XMPPError('item-not-found') -- cgit v1.2.3 From ffb2b6bc0464a9646d4b4bca3943d3a89dfc13a6 Mon Sep 17 00:00:00 2001 From: Robert Robinson Date: Sat, 12 Sep 2015 20:08:21 -0600 Subject: Update test_stream_xep_0050.py Fix Unit Test for adhoc 50 stream. --- tests/test_stream_xep_0050.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_stream_xep_0050.py b/tests/test_stream_xep_0050.py index 261a0057..8fd09a8d 100644 --- a/tests/test_stream_xep_0050.py +++ b/tests/test_stream_xep_0050.py @@ -120,6 +120,7 @@ class TestAdHocCommands(SleekTest): def handle_form(form, session): results.append(form['values']['foo']) + session['payload'] = None form = self.xmpp['xep_0004'].makeForm('form') form.addField(var='foo', ftype='text-single', label='Foo') @@ -192,6 +193,7 @@ class TestAdHocCommands(SleekTest): def handle_step2(form, session): results.append(form['values']['bar']) + session['payload'] = None def handle_step1(form, session): results.append(form['values']['foo']) @@ -427,6 +429,7 @@ class TestAdHocCommands(SleekTest): def handle_form(forms, session): for form in forms: results.append(form['values']['FORM_TYPE']) + session['payload'] = None form1 = self.xmpp['xep_0004'].makeForm('form') form1.addField(var='FORM_TYPE', ftype='hidden', value='form_1') -- cgit v1.2.3 From 0724f623bb613007f9acd6540efd4f256c8b6503 Mon Sep 17 00:00:00 2001 From: Robert Robinson Date: Mon, 14 Sep 2015 14:31:17 -0600 Subject: Force forms and fields to use plugin resolution Instead of using the interface/subinterface code that was currently being implemented for the plugin. (cherry picked from commit 1467ec7) --- sleekxmpp/plugins/xep_0004/stanza/field.py | 4 +++- sleekxmpp/plugins/xep_0004/stanza/form.py | 13 +++++++------ tests/test_stanza_xep_0004.py | 14 +++++++------- 3 files changed, 17 insertions(+), 14 deletions(-) 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 1d733760..cca9d2e0 100644 --- a/sleekxmpp/plugins/xep_0004/stanza/form.py +++ b/sleekxmpp/plugins/xep_0004/stanza/form.py @@ -22,8 +22,8 @@ class Form(ElementBase): namespace = 'jabber:x:data' name = 'x' plugin_attrib = 'form' - interfaces = set(('fields', 'instructions', 'items', - 'reported', 'title', 'type', 'values')) + interfaces = set(('instructions', 'items', + 'reported', 'title', 'type', )) sub_interfaces = set(('title',)) form_types = set(('cancel', 'form', 'result', 'submit')) @@ -48,7 +48,7 @@ class Form(ElementBase): 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 +74,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) @@ -227,9 +228,9 @@ class Form(ElementBase): def set_values(self, values): fields = self['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/tests/test_stanza_xep_0004.py b/tests/test_stanza_xep_0004.py index 9056c663..e8bc6593 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, """ @@ -189,7 +189,7 @@ class TestDataForms(SleekTest): 'value': 'cool'}, {'label': 'Urgh!', 'value': 'urgh'}]} - form['fields'] = fields + form.set_fields(fields) form['type'] = 'cancel' -- cgit v1.2.3 From 93c705fb3133b27388c0845f925b0615c88414b1 Mon Sep 17 00:00:00 2001 From: Robert Robinson Date: Mon, 14 Sep 2015 17:00:53 -0600 Subject: Fix xep_0050 changes after form refactor. --- sleekxmpp/plugins/xep_0004/stanza/form.py | 9 ++++----- tests/test_stream_xep_0050.py | 8 ++++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/sleekxmpp/plugins/xep_0004/stanza/form.py b/sleekxmpp/plugins/xep_0004/stanza/form.py index cca9d2e0..f55e88a9 100644 --- a/sleekxmpp/plugins/xep_0004/stanza/form.py +++ b/sleekxmpp/plugins/xep_0004/stanza/form.py @@ -22,8 +22,7 @@ class Form(ElementBase): namespace = 'jabber:x:data' name = 'x' plugin_attrib = 'form' - interfaces = set(('instructions', 'items', - 'reported', 'title', 'type', )) + interfaces = set(('instructions', 'items', 'reported', 'title', 'type', )) sub_interfaces = set(('title',)) form_types = set(('cancel', 'form', 'result', 'submit')) @@ -43,7 +42,7 @@ class Form(ElementBase): @property def field(self): - return self['fields'] + return self.get_fields() def set_type(self, ftype): self._set_attr('type', ftype) @@ -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 @@ -226,7 +225,7 @@ class Form(ElementBase): self.add_reported(var, **field) def set_values(self, values): - fields = self['fields'] + fields = self.get_fields() for field in values: if field not in self.get_fields(): fields[field] = self.add_field(var=field) 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') -- cgit v1.2.3 From ea65b672e75d2b544a2d9c3ef0d497c1e66075bc Mon Sep 17 00:00:00 2001 From: Robert Robinson Date: Sat, 12 Sep 2015 22:10:28 -0600 Subject: Initial cut at getting the stanzas to work. (cherry picked from commit 8c7df49) --- sleekxmpp/plugins/xep_0122/__init__.py | 1 + sleekxmpp/plugins/xep_0122/stanza.py | 83 ++++++++++++++++++++ tests/test_stanza_xep_0122.py | 137 +++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 sleekxmpp/plugins/xep_0122/__init__.py create mode 100644 sleekxmpp/plugins/xep_0122/stanza.py create mode 100644 tests/test_stanza_xep_0122.py diff --git a/sleekxmpp/plugins/xep_0122/__init__.py b/sleekxmpp/plugins/xep_0122/__init__.py new file mode 100644 index 00000000..65ce96a1 --- /dev/null +++ b/sleekxmpp/plugins/xep_0122/__init__.py @@ -0,0 +1 @@ +from sleekxmpp.plugins.xep_0122.stanza import FormValidation diff --git a/sleekxmpp/plugins/xep_0122/stanza.py b/sleekxmpp/plugins/xep_0122/stanza.py new file mode 100644 index 00000000..1807b11c --- /dev/null +++ b/sleekxmpp/plugins/xep_0122/stanza.py @@ -0,0 +1,83 @@ + +from sleekxmpp.xmlstream import ElementBase, ET + + +class FormValidation(ElementBase): + """ + Validation values for form fields. + + Example: + + + + 2003-10-06T11:22:00-07:00 + + """ + + namespace = 'http://jabber.org/protocol/xdata-validate' + name = 'validate' + plugin_attrib = 'validate' + interfaces = set(('datatype', 'basic', 'open', 'range', 'regex', )) + sub_interfaces = set(('basic', 'open', 'range', 'regex', )) + plugin_attrib_map = {} + plugin_tag_map = {} + + def _add_field(self, name): + item_xml = ET.Element('{%s}%s' % (self.namespace, name)) + self.xml.append(item_xml) + return item_xml + + def set_basic(self, value): + self.remove_all() + if value: + self._add_field('basic') + + def set_open(self, value): + self.remove_all() + if value: + self._add_field('open') + + def set_regex(self, regex): + self.remove_all() + if regex: + _regex = self._add_field('regex') + _regex.text = regex + + def set_range(self, value, minimum=None, maximum=None): + self.remove_all() + if value: + _range = self._add_field('range') + _range.attrib['min'] = str(minimum) + _range.attrib['max'] = str(maximum) + + 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: + return present.text + + return False + + def get_range(self): + present = self.xml.find('{%s}regex' % self.namespace) + if present: + return dict(present.attrib) + + return False + + +FormValidation.getBasic = FormValidation.get_basic +FormValidation.setBasic = FormValidation.set_basic diff --git a/tests/test_stanza_xep_0122.py b/tests/test_stanza_xep_0122.py new file mode 100644 index 00000000..2330b6a2 --- /dev/null +++ b/tests/test_stanza_xep_0122.py @@ -0,0 +1,137 @@ +import unittest +from sleekxmpp import Message +from sleekxmpp.test import SleekTest +from sleekxmpp.thirdparty import OrderedDict + +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 using multiple instructions elements in a data form.""" + 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, """ + + + + A text field + + Some text! + + + + + + + """) + + def test_open_validation(self): + """Testing using multiple instructions elements in a data form.""" + 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, """ + + + + A text field + + Some text! + + + + + + + """) + + def test_regex_validation(self): + """Testing using multiple instructions elements in a data form.""" + 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_regex('[0-9]+') + + self.check(msg, """ + + + + A text field + + Some text! + + [0-9]+ + + + + + """) + + def test_range_validation(self): + """Testing using multiple instructions elements in a data form.""" + 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, """ + + + + A text field + + Some text! + + + + + + + """) + + +suite = unittest.TestLoader().loadTestsFromTestCase(TestDataForms) -- cgit v1.2.3 From 5f9abe2e0e06b4cda898db90ba1b524640411e42 Mon Sep 17 00:00:00 2001 From: Robert Robinson Date: Mon, 14 Sep 2015 17:04:16 -0600 Subject: Working through test case issues. (cherry picked from commit 6b58cef) --- sleekxmpp/plugins/xep_0122/stanza.py | 7 +- tests/test_stanza_xep_0122.py | 172 +++++++++++++++++------------------ 2 files changed, 89 insertions(+), 90 deletions(-) diff --git a/sleekxmpp/plugins/xep_0122/stanza.py b/sleekxmpp/plugins/xep_0122/stanza.py index 1807b11c..bf8545c8 100644 --- a/sleekxmpp/plugins/xep_0122/stanza.py +++ b/sleekxmpp/plugins/xep_0122/stanza.py @@ -24,28 +24,27 @@ class FormValidation(ElementBase): 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): - self.remove_all() if value: self._add_field('basic') + else: + self['basic'] = False def set_open(self, value): - self.remove_all() if value: self._add_field('open') def set_regex(self, regex): - self.remove_all() if regex: _regex = self._add_field('regex') _regex.text = regex def set_range(self, value, minimum=None, maximum=None): - self.remove_all() if value: _range = self._add_field('range') _range.attrib['min'] = str(minimum) diff --git a/tests/test_stanza_xep_0122.py b/tests/test_stanza_xep_0122.py index 2330b6a2..6e1e4d7b 100644 --- a/tests/test_stanza_xep_0122.py +++ b/tests/test_stanza_xep_0122.py @@ -46,92 +46,92 @@ class TestDataForms(SleekTest):
""") - def test_open_validation(self): - """Testing using multiple instructions elements in a data form.""" - 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, """ - - - - A text field - - Some text! - - - - - - - """) - - def test_regex_validation(self): - """Testing using multiple instructions elements in a data form.""" - 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_regex('[0-9]+') - - self.check(msg, """ - - - - A text field - - Some text! - - [0-9]+ - - - - - """) - - def test_range_validation(self): - """Testing using multiple instructions elements in a data form.""" - 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, """ - - - - A text field - - Some text! - - - - - - - """) + # def test_open_validation(self): + # """Testing using multiple instructions elements in a data form.""" + # 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, """ + # + # + # + # A text field + # + # Some text! + # + # + # + # + # + # + # """) + # + # def test_regex_validation(self): + # """Testing using multiple instructions elements in a data form.""" + # 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_regex('[0-9]+') + # + # self.check(msg, """ + # + # + # + # A text field + # + # Some text! + # + # [0-9]+ + # + # + # + # + # """) + # + # def test_range_validation(self): + # """Testing using multiple instructions elements in a data form.""" + # 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, """ + # + # + # + # A text field + # + # Some text! + # + # + # + # + # + # + # """) suite = unittest.TestLoader().loadTestsFromTestCase(TestDataForms) -- cgit v1.2.3 From f2bf6072ec447cf5cc331b4057b29078b1c932b5 Mon Sep 17 00:00:00 2001 From: Robert Robinson Date: Mon, 14 Sep 2015 16:35:47 -0600 Subject: Add plugin (cherry picked from commit 2296d56) --- sleekxmpp/plugins/xep_0122/data_validation.py | 19 +++ sleekxmpp/plugins/xep_0122/stanza.py | 34 +++-- tests/test_stanza_xep_0122.py | 196 ++++++++++++++------------ 3 files changed, 150 insertions(+), 99 deletions(-) create mode 100644 sleekxmpp/plugins/xep_0122/data_validation.py diff --git a/sleekxmpp/plugins/xep_0122/data_validation.py b/sleekxmpp/plugins/xep_0122/data_validation.py new file mode 100644 index 00000000..6f6cbe8c --- /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, iterable=True) diff --git a/sleekxmpp/plugins/xep_0122/stanza.py b/sleekxmpp/plugins/xep_0122/stanza.py index bf8545c8..bc3c177a 100644 --- a/sleekxmpp/plugins/xep_0122/stanza.py +++ b/sleekxmpp/plugins/xep_0122/stanza.py @@ -13,13 +13,17 @@ class FormValidation(ElementBase): datatype='xs:dateTime'/> 2003-10-06T11:22:00-07:00 + + 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 = set(('datatype', 'basic', 'open', 'range', 'regex', )) - sub_interfaces = set(('basic', 'open', 'range', 'regex', )) + interfaces = {'datatype', 'basic', 'open', 'range', 'regex', } + sub_interfaces = {'basic', 'open', 'range', 'regex', } plugin_attrib_map = {} plugin_tag_map = {} @@ -33,22 +37,28 @@ class FormValidation(ElementBase): if value: self._add_field('basic') else: - self['basic'] = False + 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: @@ -65,18 +75,20 @@ class FormValidation(ElementBase): def get_regex(self): present = self.xml.find('{%s}regex' % self.namespace) - if present: + if present is not None: return present.text return False def get_range(self): - present = self.xml.find('{%s}regex' % self.namespace) - if present: - return dict(present.attrib) + 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 - - -FormValidation.getBasic = FormValidation.get_basic -FormValidation.setBasic = FormValidation.set_basic diff --git a/tests/test_stanza_xep_0122.py b/tests/test_stanza_xep_0122.py index 6e1e4d7b..5576c45f 100644 --- a/tests/test_stanza_xep_0122.py +++ b/tests/test_stanza_xep_0122.py @@ -1,8 +1,7 @@ import unittest + from sleekxmpp import Message from sleekxmpp.test import SleekTest -from sleekxmpp.thirdparty import OrderedDict - import sleekxmpp.plugins.xep_0004 as xep_0004 import sleekxmpp.plugins.xep_0122 as xep_0122 from sleekxmpp.xmlstream import register_stanza_plugin @@ -46,92 +45,113 @@ class TestDataForms(SleekTest):
""") - # def test_open_validation(self): - # """Testing using multiple instructions elements in a data form.""" - # 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, """ - # - # - # - # A text field - # - # Some text! - # - # - # - # - # - # - # """) - # - # def test_regex_validation(self): - # """Testing using multiple instructions elements in a data form.""" - # 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_regex('[0-9]+') - # - # self.check(msg, """ - # - # - # - # A text field - # - # Some text! - # - # [0-9]+ - # - # - # - # - # """) - # - # def test_range_validation(self): - # """Testing using multiple instructions elements in a data form.""" - # 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, """ - # - # - # - # A text field - # - # Some text! - # - # - # - # - # - # - # """) + 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 using multiple instructions elements in a data form.""" + 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, """ + + + + A text field + + Some text! + + + + + + + """) + + 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 using multiple instructions elements in a data form.""" + 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, """ + + + + A text field + + Some text! + + [0-9]+ + + + + + """) + + 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 using multiple instructions elements in a data form.""" + 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, """ + + + + A text field + + Some text! + + + + + + + """) + + self.assertDictEqual(dict(minimum=str(0), maximum=str(10)), validation.get_range()) suite = unittest.TestLoader().loadTestsFromTestCase(TestDataForms) -- cgit v1.2.3 From 110cf25c6d6055e0decb6907bfc7d0df8434c839 Mon Sep 17 00:00:00 2001 From: Robert Robinson Date: Mon, 14 Sep 2015 17:06:07 -0600 Subject: Add plugin support --- sleekxmpp/plugins/xep_0122/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sleekxmpp/plugins/xep_0122/__init__.py b/sleekxmpp/plugins/xep_0122/__init__.py index 65ce96a1..c0a0bc35 100644 --- a/sleekxmpp/plugins/xep_0122/__init__.py +++ b/sleekxmpp/plugins/xep_0122/__init__.py @@ -1 +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_0121 = XEP_0122 -- cgit v1.2.3 From 83953af53d4537b119832518faa35352ca1202ec Mon Sep 17 00:00:00 2001 From: Robert Robinson Date: Mon, 14 Sep 2015 20:28:55 -0600 Subject: Missing xep_122 dir in setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 566683b7..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', -- cgit v1.2.3 From 0b14ef82d4058925e7e9db22038777d4654e8199 Mon Sep 17 00:00:00 2001 From: Robert Robinson Date: Tue, 15 Sep 2015 10:05:53 -0600 Subject: Add test case for reported and items Previous stanza test cases didn't have test cases for reported and item field types in forms. This fixes that issue. Modified stanzabase to use an ordered dict so that can guarentee the that 'items' in a form are added after reported. Also updated the set of interfaces that are stored in Form to be a ordered set. Used the order set implementation from: https://code.activestate.com/recipes/576694/ The OrderedSet implementation is licensed under the MIT license and is developed by the same developer of the ordereddict. --- sleekxmpp/plugins/xep_0004/stanza/form.py | 26 +++++++-- sleekxmpp/thirdparty/__init__.py | 1 + sleekxmpp/thirdparty/orderedset.py | 89 +++++++++++++++++++++++++++++++ sleekxmpp/xmlstream/stanzabase.py | 2 +- tests/test_stanza_xep_0004.py | 47 ++++++++++++++++ 5 files changed, 159 insertions(+), 6 deletions(-) create mode 100644 sleekxmpp/thirdparty/orderedset.py diff --git a/sleekxmpp/plugins/xep_0004/stanza/form.py b/sleekxmpp/plugins/xep_0004/stanza/form.py index f55e88a9..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,7 +22,7 @@ class Form(ElementBase): namespace = 'jabber:x:data' name = 'x' plugin_attrib = 'form' - interfaces = set(('instructions', 'items', 'reported', 'title', 'type', )) + interfaces = OrderedSet(('instructions', 'reported', 'title', 'type', 'items', )) sub_interfaces = set(('title',)) form_types = set(('cancel', 'form', 'result', 'submit')) @@ -169,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 @@ -219,10 +219,26 @@ 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.get_fields() 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/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index 11c8dd67..c2e0f718 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -563,7 +563,7 @@ class ElementBase(object): .. versionadded:: 1.0-Beta1 """ - values = {} + values = OrderedDict() values['lang'] = self['lang'] for interface in self.interfaces: if isinstance(self[interface], JID): diff --git a/tests/test_stanza_xep_0004.py b/tests/test_stanza_xep_0004.py index e8bc6593..b87afb24 100644 --- a/tests/test_stanza_xep_0004.py +++ b/tests/test_stanza_xep_0004.py @@ -197,5 +197,52 @@ class TestDataForms(SleekTest): """) + 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, """ + + + + + + + + username@example.org + + + + + """) + + 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, """ + + + + + + + + """) + suite = unittest.TestLoader().loadTestsFromTestCase(TestDataForms) -- cgit v1.2.3 From 2a127a57a7790626c8a0bb979ec6e10d96e4535d Mon Sep 17 00:00:00 2001 From: Robert Robinson Date: Tue, 15 Sep 2015 10:09:06 -0600 Subject: Add test case Reported->Data Form Validation Add a test case that will verify that reported fields can contain data form validation data. --- tests/test_stanza_xep_0122.py | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/tests/test_stanza_xep_0122.py b/tests/test_stanza_xep_0122.py index 5576c45f..fca49bbb 100644 --- a/tests/test_stanza_xep_0122.py +++ b/tests/test_stanza_xep_0122.py @@ -16,7 +16,7 @@ class TestDataForms(SleekTest): register_stanza_plugin(xep_0004.FormField, xep_0122.FormValidation) def test_basic_validation(self): - """Testing using multiple instructions elements in a data form.""" + """Testing basic validation setting and getting.""" msg = self.Message() form = msg['form'] field = form.addField(var='f1', @@ -51,7 +51,7 @@ class TestDataForms(SleekTest): self.assertFalse(validation.get_regex()) def test_open_validation(self): - """Testing using multiple instructions elements in a data form.""" + """Testing open validation setting and getting.""" msg = self.Message() form = msg['form'] field = form.addField(var='f1', @@ -85,7 +85,7 @@ class TestDataForms(SleekTest): self.assertFalse(validation.get_regex()) def test_regex_validation(self): - """Testing using multiple instructions elements in a data form.""" + """Testing regex validation setting and getting.""" msg = self.Message() form = msg['form'] field = form.addField(var='f1', @@ -123,7 +123,7 @@ class TestDataForms(SleekTest): self.assertEqual(regex_value, validation.get_regex()) def test_range_validation(self): - """Testing using multiple instructions elements in a data form.""" + """Testing range validation setting and getting.""" msg = self.Message() form = msg['form'] field = form.addField(var='f1', @@ -153,5 +153,37 @@ class TestDataForms(SleekTest): 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, """ + + + + + + + + + + + + Some text! + + + + + """) + suite = unittest.TestLoader().loadTestsFromTestCase(TestDataForms) -- cgit v1.2.3 From d9b47b33f5101b52e781780f1a9685c7281b3015 Mon Sep 17 00:00:00 2001 From: Robert Robinson Date: Tue, 15 Sep 2015 10:20:37 -0600 Subject: Update __init__.py changed xep_0121 to xep_0122 --- sleekxmpp/plugins/xep_0122/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sleekxmpp/plugins/xep_0122/__init__.py b/sleekxmpp/plugins/xep_0122/__init__.py index c0a0bc35..4b3e9483 100644 --- a/sleekxmpp/plugins/xep_0122/__init__.py +++ b/sleekxmpp/plugins/xep_0122/__init__.py @@ -8,4 +8,4 @@ register_plugin(XEP_0122) # Retain some backwards compatibility -xep_0121 = XEP_0122 +xep_0122 = XEP_0122 -- cgit v1.2.3 From 329cb5a9f846ca0ce38c0d5730ba7da8480e50ae Mon Sep 17 00:00:00 2001 From: Robert Robinson Date: Thu, 17 Sep 2015 16:21:13 -0600 Subject: Add 0122 to plugin/__init__.py __all__ --- sleekxmpp/plugins/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sleekxmpp/plugins/__init__.py b/sleekxmpp/plugins/__init__.py index 2c90d357..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 -- cgit v1.2.3 From a7ac9692153ef28f55279a000e106622df7ccabb Mon Sep 17 00:00:00 2001 From: Robert Robinson Date: Thu, 17 Sep 2015 16:21:54 -0600 Subject: register_Stanza_plugin shouldn't be iterable Should not use iterable for registering the stanza plugins. --- sleekxmpp/plugins/xep_0122/data_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sleekxmpp/plugins/xep_0122/data_validation.py b/sleekxmpp/plugins/xep_0122/data_validation.py index 6f6cbe8c..ec2cdfcc 100644 --- a/sleekxmpp/plugins/xep_0122/data_validation.py +++ b/sleekxmpp/plugins/xep_0122/data_validation.py @@ -16,4 +16,4 @@ class XEP_0122(BasePlugin): stanza = stanza def plugin_init(self): - register_stanza_plugin(FormField, FormValidation, iterable=True) + register_stanza_plugin(FormField, FormValidation) -- cgit v1.2.3