From ca6ce26b0dd4975d3f5e0b5209a6a66a7dadbed5 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 26 Aug 2010 18:40:58 -0400 Subject: Added comments to _fix_ns to clarify the cleaning procedure. --- sleekxmpp/xmlstream/stanzabase.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sleekxmpp/xmlstream/stanzabase.py') diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index 8814df78..9785da0b 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -765,17 +765,24 @@ class ElementBase(object): False, which returns a flat string path. """ fixed = [] + # Split the XPath into a series of blocks, where a block + # is started by an element with a namespace. ns_blocks = xpath.split('{') for ns_block in ns_blocks: if '}' in ns_block: + # Apply the found namespace to following elements + # that do not have namespaces. namespace = ns_block.split('}')[0] elements = ns_block.split('}')[1].split('/') else: + # Apply the stanza's namespace to the following + # elements since no namespace was provided. namespace = self.namespace elements = ns_block.split('/') for element in elements: if element: + # Skip empty entry artifacts from splitting. fixed.append('{%s}%s' % (namespace, element)) if split: -- cgit v1.2.3 From a2c515bc978cbabb1e6ac4398a8f0a11bbc9d9f8 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Fri, 27 Aug 2010 11:07:20 -0400 Subject: Updated StanzaBase with documentation. --- sleekxmpp/xmlstream/stanzabase.py | 139 ++++++++++++++++++++++++++++++++++---- 1 file changed, 126 insertions(+), 13 deletions(-) (limited to 'sleekxmpp/xmlstream/stanzabase.py') diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index 9785da0b..51966a4e 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -893,6 +893,48 @@ class ElementBase(object): class StanzaBase(ElementBase): + + """ + StanzaBase provides the foundation for all other stanza objects used by + SleekXMPP, and defines a basic set of interfaces common to nearly + all stanzas. These interfaces are the 'id', 'type', 'to', and 'from' + attributes. An additional interface, 'payload', is available to access + the XML contents of the stanza. Most stanza objects will provided more + specific interfaces, however. + + Stanza Interface: + from -- A JID object representing the sender's JID. + id -- An optional id value that can be used to associate stanzas + with their replies. + payload -- The XML contents of the stanza. + to -- A JID object representing the recipient's JID. + type -- The type of stanza, typically will be 'normal', 'error', + 'get', or 'set', etc. + + Attributes: + stream -- The XMLStream instance that will handle sending this stanza. + tag -- The namespaced version of the stanza's name. + + Methods: + setType -- Set the type of the stanza. + getTo -- Return the stanza recipients JID. + setTo -- Set the stanza recipient's JID. + getFrom -- Return the stanza sender's JID. + setFrom -- Set the stanza sender's JID. + getPayload -- Return the stanza's XML contents. + setPayload -- Append to the stanza's XML contents. + delPayload -- 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'. + unhandled -- Callback for when the stanza is not handled by a + stream handler. + exception -- Callback for if an exception is raised while + handling the stanza. + send -- Send the stanza using the stanza's stream. + """ + name = 'stanza' namespace = 'jabber:client' interfaces = set(('type', 'to', 'from', 'id', 'payload')) @@ -901,6 +943,17 @@ class StanzaBase(ElementBase): def __init__(self, stream=None, xml=None, stype=None, sto=None, sfrom=None, sid=None): + """ + Create a new stanza. + + Arguments: + stream -- Optional XMLStream responsible for sending this stanza. + xml -- Optional XML contents to initialize stanza values. + stype -- Optional stanza type value. + sto -- Optional string or JID object of the recipient's JID. + sfrom -- Optional string or JID object of the sender's JID. + sid -- Optional ID value for the stanza. + """ self.stream = stream if stream is not None: self.namespace = stream.default_ns @@ -914,22 +967,69 @@ class StanzaBase(ElementBase): self.tag = "{%s}%s" % (self.namespace, self.name) def setType(self, value): + """ + Set the stanza's 'type' attribute. + + Only type values contained in StanzaBase.types are accepted. + + Arguments: + value -- One of the values contained in StanzaBase.types + """ if value in self.types: self.xml.attrib['type'] = value return self + def getTo(self): + """Return the value of the stanza's 'to' attribute.""" + return JID(self._getAttr('to')) + + def setTo(self, value): + """ + Set the 'to' attribute of the stanza. + + Arguments: + value -- A string or JID object representing the recipient's JID. + """ + return self._setAttr('to', str(value)) + + def getFrom(self): + """Return the value of the stanza's 'from' attribute.""" + return JID(self._getAttr('from')) + + def setFrom(self, value): + """ + Set the 'from' attribute of the stanza. + + Arguments: + from -- A string or JID object representing the sender's JID. + """ + return self._setAttr('from', str(value)) + def getPayload(self): + """Return a list of XML objects contained in the stanza.""" return self.xml.getchildren() def setPayload(self, value): + """ + Add XML content to the stanza. + + Arguments: + value -- An XML object, or a stanza object. + """ self.xml.append(value) return self def delPayload(self): + """Remove the XML contents of the stanza.""" 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()): @@ -937,6 +1037,12 @@ class StanzaBase(ElementBase): return self def reply(self): + """ + Reset the stanza and swap its 'from' and 'to' attributes to prepare + for sending a reply stanza. + + For client streams, the 'from' attribute is removed. + """ # if it's a component, use from if self.stream and hasattr(self.stream, "is_component") and \ self.stream.is_component: @@ -948,35 +1054,42 @@ class StanzaBase(ElementBase): return self def error(self): + """Set the stanza's type to 'error'.""" self['type'] = 'error' return self - def getTo(self): - return JID(self._getAttr('to')) - - def setTo(self, value): - return self._setAttr('to', str(value)) - - def getFrom(self): - return JID(self._getAttr('from')) - - def setFrom(self, value): - return self._setAttr('from', str(value)) - def unhandled(self): + """ + Called when no handlers have been registered to process this + stanza. + + Meant to be overridden. + """ pass def exception(self, e): + """ + Handle exceptions raised during stanza processing. + + Meant to be overridden. + """ logging.exception('Error handling {%s}%s stanza' % (self.namespace, self.name)) def send(self): + """Queue the stanza to be sent on the XML stream.""" self.stream.sendRaw(self.__str__()) def __copy__(self): - return self.__class__(xml=copy.deepcopy(self.xml), stream=self.stream) + """ + Return a copy of the stanza object that does not share the + same underlying XML object, but does share the same XML stream. + """ + return self.__class__(xml=copy.deepcopy(self.xml), + stream=self.stream) def __str__(self): + """Serialize the stanza's XML to a string.""" return tostring(self.xml, xmlns='', stanza_ns=self.namespace, stream=self.stream) -- cgit v1.2.3 From bb6f4af8e24f940a837c227b0f2fab2b64e4dc7e Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Fri, 27 Aug 2010 12:22:35 -0400 Subject: Added unit tests for StanzaBase. --- sleekxmpp/xmlstream/stanzabase.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'sleekxmpp/xmlstream/stanzabase.py') diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index 51966a4e..d7c7c7b4 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -1014,9 +1014,13 @@ class StanzaBase(ElementBase): Add XML content to the stanza. Arguments: - value -- An XML object, or a stanza object. + value -- Either an XML or a stanza object, or a list + of XML or stanza objects. """ - self.xml.append(value) + if not isinstance(value, list): + value = [value] + for val in value: + self.append(val) return self def delPayload(self): -- cgit v1.2.3 From 9c62bce2060e30e41c3710587f6bd9992625b245 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Mon, 30 Aug 2010 14:55:30 -0400 Subject: Updated ElementBase.match to respect namespaces with slashes. Required adding option to _fix_ns to not propagate namespaces to child elements. --- sleekxmpp/xmlstream/stanzabase.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'sleekxmpp/xmlstream/stanzabase.py') diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index d7c7c7b4..8f4874cd 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -586,7 +586,8 @@ class ElementBase(object): string or a list of element names with attribute checks. """ if isinstance(xpath, str): - xpath = xpath.split('/') + xpath = self._fix_ns(xpath, split=True, propagate_ns=False) + # Extract the tag name and attribute checks for the first XPath node. components = xpath[0].split('@') @@ -754,15 +755,20 @@ class ElementBase(object): """ return self - def _fix_ns(self, xpath, split=False): + def _fix_ns(self, xpath, split=False, propagate_ns=True): """ Apply the stanza's namespace to elements in an XPath expression. Arguments: - xpath -- The XPath expression to fix with namespaces. - split -- Indicates if the fixed XPath should be left as a - list of element names with namespaces. Defaults to - False, which returns a flat string path. + xpath -- The XPath expression to fix with namespaces. + split -- Indicates if the fixed XPath should be left as a + list of element names with namespaces. Defaults to + False, which returns a flat string path. + propagate_ns -- Overrides propagating parent element namespaces + to child elements. Useful if you wish to simply + split an XPath that has non-specified namespaces, + and child and parent namespaces are known not to + always match. Defaults to True. """ fixed = [] # Split the XPath into a series of blocks, where a block @@ -774,17 +780,23 @@ class ElementBase(object): # that do not have namespaces. namespace = ns_block.split('}')[0] elements = ns_block.split('}')[1].split('/') - else: + elif use_ns: # Apply the stanza's namespace to the following # elements since no namespace was provided. namespace = self.namespace elements = ns_block.split('/') + else: + # We don't want to propagate namespaces. + elements = ns_block.split('/') for element in elements: if element: # Skip empty entry artifacts from splitting. - fixed.append('{%s}%s' % (namespace, - element)) + if use_ns: + tag = '{%s}%s' % (namespace, element) + else: + tag = element + fixed.append(tag) if split: return fixed return '/'.join(fixed) -- cgit v1.2.3 From 998741b87e7babc6e0af9bcf79f10f4422ba96f1 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Mon, 30 Aug 2010 15:25:59 -0400 Subject: Fixed typos in ElementBase._fix_ns --- sleekxmpp/xmlstream/stanzabase.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sleekxmpp/xmlstream/stanzabase.py') diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index 8f4874cd..7eb3f978 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -780,7 +780,7 @@ class ElementBase(object): # that do not have namespaces. namespace = ns_block.split('}')[0] elements = ns_block.split('}')[1].split('/') - elif use_ns: + elif propagate_ns: # Apply the stanza's namespace to the following # elements since no namespace was provided. namespace = self.namespace @@ -792,7 +792,7 @@ class ElementBase(object): for element in elements: if element: # Skip empty entry artifacts from splitting. - if use_ns: + if propagate_ns: tag = '{%s}%s' % (namespace, element) else: tag = element -- cgit v1.2.3 From 3749c1b88c4774b85fcee1e26e8bce8dbeef23c7 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Mon, 30 Aug 2010 17:12:10 -0400 Subject: Fixed ElementBase.match to match using sub_interface elements. --- sleekxmpp/xmlstream/stanzabase.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sleekxmpp/xmlstream/stanzabase.py') diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index 7eb3f978..86b528d1 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -614,6 +614,12 @@ class ElementBase(object): if self[name] != value: return False + # Check sub interfaces. + if len(xpath) > 1: + next_tag = xpath[1] + if next_tag in self.sub_interfaces and self[next_tag]: + return True + # Attempt to continue matching the XPath using the stanza's plugins. if not matched_substanzas and len(xpath) > 1: # Convert {namespace}tag@attribs to just tag -- cgit v1.2.3 From aebd115ba2a8dfab7b345dfc121ec73b5dc7a5a1 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Wed, 1 Sep 2010 14:20:34 -0400 Subject: A few cleanups to make things simpler. --- sleekxmpp/xmlstream/stanzabase.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'sleekxmpp/xmlstream/stanzabase.py') diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index 86b528d1..f8242005 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -594,8 +594,8 @@ class ElementBase(object): tag = components[0] attributes = components[1:] - if tag not in (self.name, "{%s}%s" % (self.namespace, self.name), - self.plugins, self.plugin_attrib): + if tag not in (self.name, "{%s}%s" % (self.namespace, self.name)) and \ + tag not in self.plugins and tag not in self.plugin_attrib: # The requested tag is not in this stanza, so no match. return False @@ -786,14 +786,11 @@ class ElementBase(object): # that do not have namespaces. namespace = ns_block.split('}')[0] elements = ns_block.split('}')[1].split('/') - elif propagate_ns: + else: # Apply the stanza's namespace to the following # elements since no namespace was provided. namespace = self.namespace elements = ns_block.split('/') - else: - # We don't want to propagate namespaces. - elements = ns_block.split('/') for element in elements: if element: -- cgit v1.2.3