From d7dea0c6ccdd769fb32229e8b719a7556b8e0643 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Fri, 14 Jan 2011 12:07:25 -0500 Subject: Add a note for debug statement when running scheduled events. Fixes the intermittent DEBUG ((),) messages that give no explanation. Will now show as: DEBUG Scheduled event: ((), ) --- sleekxmpp/xmlstream/xmlstream.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sleekxmpp/xmlstream') diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py index d5c1043b..3cf28ed8 100644 --- a/sleekxmpp/xmlstream/xmlstream.py +++ b/sleekxmpp/xmlstream/xmlstream.py @@ -899,7 +899,7 @@ class XMLStream(object): args[0].exception(e) elif etype == 'schedule': try: - log.debug(args) + log.debug('Scheduled event: %s' % args) handler(*args[0]) except: log.exception('Error processing scheduled task') -- cgit v1.2.3 From cb85d4a5297b0eb6cc24052321b38f9f29f62004 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sun, 16 Jan 2011 13:07:39 -0500 Subject: Raise the event 'socket_error' when a socket error occurs. Will be most useful for debugging and responding to failed connection attempts. --- sleekxmpp/xmlstream/xmlstream.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sleekxmpp/xmlstream') diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py index 3cf28ed8..39b10a2a 100644 --- a/sleekxmpp/xmlstream/xmlstream.py +++ b/sleekxmpp/xmlstream/xmlstream.py @@ -292,6 +292,7 @@ class XMLStream(object): return True except Socket.error as serr: error_msg = "Could not connect to %s:%s. Socket Error #%s: %s" + self.event('socket_error', serr) log.error(error_msg % (self.address[0], self.address[1], serr.errno, serr.strerror)) time.sleep(1) @@ -327,7 +328,7 @@ class XMLStream(object): self.filesocket.close() self.socket.shutdown(Socket.SHUT_RDWR) except Socket.error as serr: - pass + self.event('socket_error', serr) finally: #clear your application state self.event("disconnected", direct=True) @@ -734,7 +735,8 @@ class XMLStream(object): except SystemExit: log.debug("SystemExit in _process") self.stop.set() - except Socket.error: + except Socket.error as serr: + self.event('socket_error', serr) log.exception('Socket Error') except: if not self.stop.isSet(): -- cgit v1.2.3 From da332365d4e97720ac182e0f66b73919d764a555 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Wed, 19 Jan 2011 19:49:13 -0500 Subject: Make extending stanza objects nicer. A stanza object may add is_extension = True to its class definition to provide a single new interface to a parent stanza. For example: import sleekxmpp from sleekxmpp import Iq from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin, ET class Foo(ElementBase): """ Test adding just an attribute to a parent stanza. Adding subelements works as expected. """ is_extension = True interfaces = set(('foo',)) plugin_attrib = 'foo' def setup(self, xml): # Don't include an XML element in the parent stanza # since we're adding just an attribute. # If adding a regular subelement, no need to do this. self.xml = ET.Element('') def set_foo(self, val): self.parent()._set_attr('foo', val) def get_foo(self): return self.parent()._get_attr('foo') def del_foo(self): self.parent()._del_attr('foo') register_stanza_plugin(Iq, Foo) i1 = Iq() i2 = Iq(xml=ET.fromstring("")) >>> i1['foo'] = '3' >>> i1 '3' >>> i1 '' >>> i2 '' >>> i2['foo'] 'bar' >>> del i2['foo'] >>> i2 '' --- sleekxmpp/xmlstream/stanzabase.py | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) (limited to 'sleekxmpp/xmlstream') diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index 5551d439..2c008668 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -95,10 +95,22 @@ class ElementBase(object): >>> message['custom']['useful_thing'] = 'foo' If a plugin provides an interface that is the same as the plugin's - plugin_attrib value, then the plugin's interface may be accessed - directly from the parent stanza, as so: + plugin_attrib value, then the plugin's interface may be assigned + directly from the parent stanza, as shown below, but retrieving + information will require all interfaces to be used, as so: >>> message['custom'] = 'bar' # Same as using message['custom']['custom'] + >>> message['custom']['custom'] # Must use all interfaces + 'bar' + + If the plugin sets the value is_extension = True, then both setting + and getting an interface value that is the same as the plugin's + plugin_attrib value will work, as so: + + >>> message['custom'] = 'bar' # Using is_extension=True + >>> message['custom'] + 'bar' + Class Attributes: name -- The name of the stanza's main element. @@ -116,6 +128,10 @@ class ElementBase(object): associated plugin stanza classes. plugin_tag_map -- A mapping of plugin stanza tag names with the associated plugin stanza classes. + is_extension -- When True, allows the stanza to provide one + additional interface to the parent stanza, + extending the interfaces supported by the + parent. Defaults to False. xml_ns -- The XML namespace, http://www.w3.org/XML/1998/namespace, for use with xml:lang values. @@ -173,6 +189,7 @@ class ElementBase(object): plugin_attrib_map = {} plugin_tag_map = {} subitem = None + is_extension = False xml_ns = 'http://www.w3.org/XML/1998/namespace' def __init__(self, xml=None, parent=None): @@ -371,6 +388,8 @@ class ElementBase(object): elif attrib in self.plugin_attrib_map: if attrib not in self.plugins: self.init_plugin(attrib) + if self.plugins[attrib].is_extension: + return self.plugins[attrib][attrib] return self.plugins[attrib] else: return '' @@ -467,8 +486,13 @@ class ElementBase(object): elif attrib in self.plugin_attrib_map: if attrib in self.plugins: xml = self.plugins[attrib].xml + if self.plugins[attrib].is_extension: + del self.plugins[attrib][attrib] del self.plugins[attrib] - self.xml.remove(xml) + try: + self.xml.remove(xml) + except: + pass return self def _set_attr(self, name, value): -- cgit v1.2.3 From 4e757c2b56e7c4ded0a3de9e49c550db36da72d2 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Wed, 26 Jan 2011 10:04:36 -0500 Subject: Upgraded how subitem works. May now use register_stanza_plugin(Foo, Bar, iterable=True) to add to the set of stanza classes used for iterable substanzas. It is no longer necessary to manually specify the contents of subitem if the new method is used. --- sleekxmpp/xmlstream/stanzabase.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'sleekxmpp/xmlstream') diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index 2c008668..764aaca7 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -23,17 +23,23 @@ log = logging.getLogger(__name__) XML_TYPE = type(ET.Element('xml')) -def register_stanza_plugin(stanza, plugin): +def register_stanza_plugin(stanza, plugin, iterable=False): """ Associate a stanza object as a plugin for another stanza. Arguments: - stanza -- The class of the parent stanza. - plugin -- The class of the plugin stanza. + stanza -- The class of the parent stanza. + plugin -- The class of the plugin stanza. + iterable -- Indicates if the plugin stanza + should be included in the parent + stanza's iterable 'substanzas' + interface results. """ tag = "{%s}%s" % (plugin.namespace, plugin.name) stanza.plugin_attrib_map[plugin.plugin_attrib] = plugin stanza.plugin_tag_map[tag] = plugin + if iterable: + stanza.plugin_iterables.add(plugin) # To maintain backwards compatibility for now, preserve the camel case name. @@ -120,12 +126,15 @@ class ElementBase(object): sub_interfaces -- A subset of the set of interfaces which map to subelements instead of attributes. subitem -- A set of stanza classes which are allowed to - be added as substanzas. + be added as substanzas. Deprecated version + of plugin_iterables. types -- A set of generic type attribute values. plugin_attrib -- The interface name that the stanza uses to be accessed as a plugin from another stanza. plugin_attrib_map -- A mapping of plugin attribute names with the associated plugin stanza classes. + plugin_iterables -- A set of stanza classes which are allowed to + be added as substanzas. plugin_tag_map -- A mapping of plugin stanza tag names with the associated plugin stanza classes. is_extension -- When True, allows the stanza to provide one @@ -187,6 +196,7 @@ class ElementBase(object): types = set(('get', 'set', 'error', None, 'unavailable', 'normal', 'chat')) sub_interfaces = tuple() plugin_attrib_map = {} + plugin_iterables = set() plugin_tag_map = {} subitem = None is_extension = False @@ -235,9 +245,11 @@ class ElementBase(object): self.plugins[plugin.plugin_attrib] = plugin(child, self) if self.subitem is not None: for sub in self.subitem: - if child.tag == "{%s}%s" % (sub.namespace, sub.name): - self.iterables.append(sub(child, self)) - break + self.plugin_iterables.add(sub) + for sub in self.plugin_iterables: + if child.tag == "{%s}%s" % (sub.namespace, sub.name): + self.iterables.append(sub(child, self)) + break def setup(self, xml=None): """ -- cgit v1.2.3 From 0c8a8314b2e70d4a82b1c199b7e5bc16b494e275 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Wed, 26 Jan 2011 11:27:41 -0500 Subject: Cleanup for stanzabase. Use stanza.values instead of _get/set_stanza_values where used. ElementBase stanzas can now use .tag May use class method tag_name() for stanza classes. ElementBase now has .clear() method. --- sleekxmpp/xmlstream/stanzabase.py | 60 ++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 23 deletions(-) (limited to 'sleekxmpp/xmlstream') diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index 764aaca7..558ab743 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -129,6 +129,8 @@ class ElementBase(object): be added as substanzas. Deprecated version of plugin_iterables. types -- A set of generic type attribute values. + tag -- The namespaced name of the stanza's root + element. Example: "{foo_ns}bar" plugin_attrib -- The interface name that the stanza uses to be accessed as a plugin from another stanza. plugin_attrib_map -- A mapping of plugin attribute names with the @@ -153,6 +155,10 @@ class ElementBase(object): values -- A dictionary of the stanza's interfaces and interface values, including plugins. + Class Methods + tag_name -- Return the namespaced version of the stanza's + root element's name. + Methods: setup -- Initialize the stanza's XML contents. enable -- Instantiate a stanza plugin. @@ -185,6 +191,7 @@ class ElementBase(object): appendxml -- Add XML content to the stanza. pop -- Remove a substanza. next -- Return the next iterable substanza. + clear -- Reset the stanza's XML contents. _fix_ns -- Apply the stanza's namespace to non-namespaced elements in an XPath expression. """ @@ -226,6 +233,7 @@ class ElementBase(object): self.plugins = {} self.iterables = [] self._index = 0 + self.tag = self.tag_name() if parent is None: self.parent = None else: @@ -316,14 +324,12 @@ class ElementBase(object): for interface in self.interfaces: values[interface] = self[interface] for plugin, stanza in self.plugins.items(): - values[plugin] = stanza._get_stanza_values() + values[plugin] = stanza.values if self.iterables: iterables = [] for stanza in self.iterables: - iterables.append(stanza._get_stanza_values()) - iterables[-1].update({ - '__childtag__': "{%s}%s" % (stanza.namespace, - stanza.name)}) + iterables.append(stanza.values) + iterables[-1]['__childtag__'] = stanza.tag values['substanzas'] = iterables return values @@ -347,7 +353,7 @@ class ElementBase(object): subclass.name) if subdict['__childtag__'] == child_tag: sub = subclass(parent=self) - sub._set_stanza_values(subdict) + sub.values = subdict self.iterables.append(sub) break elif interface in self.interfaces: @@ -355,7 +361,7 @@ class ElementBase(object): elif interface in self.plugin_attrib_map: if interface not in self.plugins: self.init_plugin(interface) - self.plugins[interface]._set_stanza_values(value) + self.plugins[interface].values = value return self def __getitem__(self, attrib): @@ -826,6 +832,28 @@ class ElementBase(object): """ return self.__next__() + def clear(self): + """ + Remove all XML element contents and plugins. + + Any attribute values will be preserved. + """ + for child in self.xml.getchildren(): + self.xml.remove(child) + for plugin in list(self.plugins.keys()): + del self.plugins[plugin] + return self + + @classmethod + def tag_name(cls): + """ + Return the namespaced name of the stanza's root element. + + For example, for the stanza , + stanza.tag would return "{bar}foo". + """ + return "{%s}%s" % (cls.namespace, cls.name) + @property def attrib(self): """ @@ -898,13 +926,13 @@ class ElementBase(object): return False # Check that this stanza is a superset of the other stanza. - values = self._get_stanza_values() + values = self.values for key in other.keys(): if key not in values or values[key] != other[key]: return False # Check that the other stanza is a superset of this stanza. - values = other._get_stanza_values() + values = other.values for key in self.keys(): if key not in values or values[key] != self[key]: return False @@ -1008,7 +1036,6 @@ class StanzaBase(ElementBase): Attributes: stream -- The XMLStream instance that will handle sending this stanza. - tag -- The namespaced version of the stanza's name. Methods: set_type -- Set the type of the stanza. @@ -1019,7 +1046,6 @@ class StanzaBase(ElementBase): get_payload -- Return the stanza's XML contents. set_payload -- Append to the stanza's XML contents. del_payload -- Remove the stanza's XML contents. - clear -- Reset the stanza's XML contents. reply -- Reset the stanza and modify the 'to' and 'from' attributes to prepare for sending a reply. error -- Set the stanza's type to 'error'. @@ -1134,18 +1160,6 @@ class StanzaBase(ElementBase): self.clear() return self - def clear(self): - """ - Remove all XML element contents and plugins. - - Any attribute values will be preserved. - """ - for child in self.xml.getchildren(): - self.xml.remove(child) - for plugin in list(self.plugins.keys()): - del self.plugins[plugin] - return self - def reply(self): """ Reset the stanza and swap its 'from' and 'to' attributes to prepare -- cgit v1.2.3 From 38dc35840e5653ad99ecb5c65fef464b751a32e2 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 27 Jan 2011 15:59:50 -0500 Subject: Recognize stanzas that don't use the default namespace. --- sleekxmpp/xmlstream/xmlstream.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sleekxmpp/xmlstream') diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py index 39b10a2a..1cd23fba 100644 --- a/sleekxmpp/xmlstream/xmlstream.py +++ b/sleekxmpp/xmlstream/xmlstream.py @@ -802,7 +802,8 @@ class XMLStream(object): default_ns = self.default_ns stanza_type = StanzaBase for stanza_class in self.__root_stanza: - if xml.tag == "{%s}%s" % (default_ns, stanza_class.name): + if xml.tag == "{%s}%s" % (default_ns, stanza_class.name) or \ + xml.tag == stanza_class.tag_name(): stanza_type = stanza_class break stanza = stanza_type(self, xml) @@ -827,7 +828,8 @@ class XMLStream(object): # stanza type applies, a generic StanzaBase stanza will be used. stanza_type = StanzaBase for stanza_class in self.__root_stanza: - if xml.tag == "{%s}%s" % (self.default_ns, stanza_class.name): + if xml.tag == "{%s}%s" % (self.default_ns, stanza_class.name) or \ + xml.tag == stanza_class.tag_name(): stanza_type = stanza_class break stanza = stanza_type(self, xml) -- cgit v1.2.3 From 35ef8f909077fa700ebd90e5ccbd3fd71a7c2f7e Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 27 Jan 2011 16:01:35 -0500 Subject: Make stanza.plugins an OrderedDict. This allows you to determine the order in which substanzas were added in the original XML. --- sleekxmpp/xmlstream/stanzabase.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sleekxmpp/xmlstream') diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index 558ab743..3937a7a9 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -14,6 +14,7 @@ from xml.etree import cElementTree as ET from sleekxmpp.xmlstream import JID from sleekxmpp.xmlstream.tostring import tostring +from sleekxmpp.thirdparty import OrderedDict log = logging.getLogger(__name__) @@ -230,7 +231,7 @@ class ElementBase(object): self.setStanzaValues = self._set_stanza_values self.xml = xml - self.plugins = {} + self.plugins = OrderedDict() self.iterables = [] self._index = 0 self.tag = self.tag_name() -- cgit v1.2.3