diff options
Diffstat (limited to 'sleekxmpp/xmlstream')
-rw-r--r-- | sleekxmpp/xmlstream/jid.py | 8 | ||||
-rw-r--r-- | sleekxmpp/xmlstream/stanzabase.py | 119 | ||||
-rw-r--r-- | sleekxmpp/xmlstream/xmlstream.py | 14 |
3 files changed, 101 insertions, 40 deletions
diff --git a/sleekxmpp/xmlstream/jid.py b/sleekxmpp/xmlstream/jid.py index d8f45b92..5019a25e 100644 --- a/sleekxmpp/xmlstream/jid.py +++ b/sleekxmpp/xmlstream/jid.py @@ -71,7 +71,7 @@ class JID(object): if self._domain is None: self._domain = self._jid.split('@', 1)[-1].split('/', 1)[0] return self._domain or "" - elif name == 'full': + elif name in ('full', 'jid'): return self._jid or "" elif name == 'bare': if self._bare is None: @@ -124,3 +124,9 @@ class JID(object): def __repr__(self): return str(self) + + def __eq__(self, other): + """ + Two JIDs are considered equal if they have the same full JID value. + """ + return str(other) == str(self) diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index 5551d439..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__) @@ -23,17 +24,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. @@ -95,10 +102,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. @@ -108,14 +127,23 @@ 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. + 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 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 + 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. @@ -128,6 +156,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. @@ -160,6 +192,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. """ @@ -171,8 +204,10 @@ 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 xml_ns = 'http://www.w3.org/XML/1998/namespace' def __init__(self, xml=None, parent=None): @@ -196,9 +231,10 @@ 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() if parent is None: self.parent = None else: @@ -218,9 +254,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): """ @@ -287,14 +325,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 @@ -318,7 +354,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: @@ -326,7 +362,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): @@ -371,6 +407,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 +505,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): @@ -790,6 +833,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 <foo xmlns="bar" />, + stanza.tag would return "{bar}foo". + """ + return "{%s}%s" % (cls.namespace, cls.name) + @property def attrib(self): """ @@ -862,13 +927,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 @@ -972,7 +1037,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. @@ -983,7 +1047,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'. @@ -1098,18 +1161,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 diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py index d5c1043b..1cd23fba 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(): @@ -800,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) @@ -825,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) @@ -899,7 +903,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') |