From fac3bca1f6fd2412b52c3e7ce9b0971d8a290083 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 19 Aug 2010 19:11:12 -0400 Subject: [PATCH 1/3] Updated ElementBase.__delitem__ and added unit tests. --- sleekxmpp/xmlstream/stanzabase.py | 52 ++++++++++++++++++++++--------- tests/test_elementbase.py | 41 ++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 14 deletions(-) diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index 83a8ddf..fb8e6be 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -269,6 +269,44 @@ class ElementBase(object): self.plugins[attrib][attrib] = value return self + def __delitem__(self, attrib): + """ + Delete the value of a stanza interface using dictionary-like syntax. + + Example: + >>> msg['body'] = "Hi!" + >>> msg['body'] + 'Hi!' + >>> del msg['body'] + >>> msg['body'] + '' + + Stanza interfaces are typically mapped directly to the underlyig XML + object, but can be overridden by the presence of a delAttrib method + (or delFoo where the interface is named foo, etc). + + The effect of deleting a stanza interface value named foo will be + one of: + 1. Call delFoo, if it exists. + 2. Delete foo element, if foo is in sub_interfaces. + 3. Delete top level XML attribute named foo. + 4. Remove the foo plugin, if it was loaded. + 5. Do nothing. + """ + if attrib in self.interfaces: + del_method = "del%s" % attrib.title() + if hasattr(self, del_method): + getattr(self, del_method)() + else: + if attrib in self.sub_interfaces: + return self._delSub(attrib) + else: + self._delAttr(attrib) + elif attrib in self.plugin_attrib_map: + if attrib in self.plugins: + del self.plugins[attrib] + return self + @property def attrib(self): #backwards compatibility return self @@ -352,20 +390,6 @@ class ElementBase(object): def findall(self, xpath): return self.xml.findall(xpath) - def __delitem__(self, attrib): - if attrib.lower() in self.interfaces: - if hasattr(self, "del%s" % attrib.title()): - getattr(self, "del%s" % attrib.title())() - else: - if attrib in self.sub_interfaces: - return self._delSub(attrib) - else: - self._delAttr(attrib) - elif attrib in self.plugin_attrib_map: - if attrib in self.plugins: - del self.plugins[attrib] - return self - def __eq__(self, other): if not isinstance(other, ElementBase): return False diff --git a/tests/test_elementbase.py b/tests/test_elementbase.py index 95502f5..bf86e59 100644 --- a/tests/test_elementbase.py +++ b/tests/test_elementbase.py @@ -189,5 +189,46 @@ class TestElementBase(SleekTest): """) + def testDelItem(self): + """Test deleting stanza interface values.""" + + class TestStanza(ElementBase): + name = "foo" + namespace = "foo" + interfaces = set(('bar', 'baz', 'qux')) + sub_interfaces = set(('bar',)) + + def delQux(self): + pass + + class TestStanzaPlugin(ElementBase): + name = "foobar" + namespace = "foo" + plugin_attrib = "foobar" + interfaces = set(('foobar',)) + + registerStanzaPlugin(TestStanza, TestStanzaPlugin) + + stanza = TestStanza() + stanza['bar'] = 'a' + stanza['baz'] = 'b' + stanza['qux'] = 'c' + stanza['foobar']['foobar'] = 'd' + + self.checkStanza(TestStanza, stanza, """ + + a + + + """) + + del stanza['bar'] + del stanza['baz'] + del stanza['qux'] + del stanza['foobar'] + + self.checkStanza(TestStanza, stanza, """ + + """) suite = unittest.TestLoader().loadTestsFromTestCase(TestElementBase) From b71cfe049285bf777b0e648cebb05c7da6c69e44 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 19 Aug 2010 19:14:18 -0400 Subject: [PATCH 2/3] Small cleanup in ElementBase.__setitem__ --- sleekxmpp/xmlstream/stanzabase.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index fb8e6be..dfa3736 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -254,8 +254,9 @@ class ElementBase(object): """ if attrib in self.interfaces: if value is not None: - if hasattr(self, "set%s" % attrib.title()): - getattr(self, "set%s" % attrib.title())(value,) + set_method = "set%s" % attrib.title() + if hasattr(self, set_method): + getattr(self, set_method)(value,) else: if attrib in self.sub_interfaces: return self._setSubText(attrib, text=value) From 8a0616b3e0c9b5b79ce9418a2494303b28863b4b Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 19 Aug 2010 20:41:26 -0400 Subject: [PATCH 3/3] Updated ElementBase methods _getAttr, _setAttr, and _delAttr with docs and tests. --- sleekxmpp/xmlstream/stanzabase.py | 59 ++++++++++++++++++++++++------- tests/test_elementbase.py | 37 +++++++++++++++++++ 2 files changed, 83 insertions(+), 13 deletions(-) diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index dfa3736..3223901 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -293,6 +293,9 @@ class ElementBase(object): 3. Delete top level XML attribute named foo. 4. Remove the foo plugin, if it was loaded. 5. Do nothing. + + Arguments: + attrib -- The name of the affected stanza interface. """ if attrib in self.interfaces: del_method = "del%s" % attrib.title() @@ -308,6 +311,49 @@ class ElementBase(object): del self.plugins[attrib] return self + def _setAttr(self, name, value): + """ + Set the value of a top level attribute of the underlying XML object. + + If the new value is None or an empty string, then the attribute will + be removed. + + Arguments: + name -- The name of the attribute. + value -- The new value of the attribute, or None or '' to + remove it. + """ + if value is None or value == '': + self.__delitem__(name) + else: + self.xml.attrib[name] = value + + def _delAttr(self, name): + """ + Remove a top level attribute of the underlying XML object. + + Arguments: + name -- The name of the attribute. + """ + if name in self.xml.attrib: + del self.xml.attrib[name] + + def _getAttr(self, name, default=''): + """ + Return the value of a top level attribute of the underlying + XML object. + + In case the attribute has not been set, a default value can be + returned instead. An empty string is returned if no other default + is supplied. + + Arguments: + name -- The name of the attribute. + default -- Optional value to return if the attribute has not + been set. An empty string is returned otherwise. + """ + return self.xml.attrib.get(name, default) + @property def attrib(self): #backwards compatibility return self @@ -400,19 +446,6 @@ class ElementBase(object): return False return True - def _setAttr(self, name, value): - if value is None or value == '': - self.__delitem__(name) - else: - self.xml.attrib[name] = value - - def _delAttr(self, name): - if name in self.xml.attrib: - del self.xml.attrib[name] - - def _getAttr(self, name, default=''): - return self.xml.attrib.get(name, default) - def _getSubText(self, name): if '}' not in name: name = "{%s}%s" % (self.namespace, name) diff --git a/tests/test_elementbase.py b/tests/test_elementbase.py index bf86e59..78277af 100644 --- a/tests/test_elementbase.py +++ b/tests/test_elementbase.py @@ -231,4 +231,41 @@ class TestElementBase(SleekTest): """) + def testModifyingAttributes(self): + """Test modifying top level attributes of a stanza's XML object.""" + + class TestStanza(ElementBase): + name = "foo" + namespace = "foo" + interfaces = set(('bar', 'baz')) + + stanza = TestStanza() + + self.checkStanza(TestStanza, stanza, """ + + """) + + self.failUnless(stanza._getAttr('bar') == '', + "Incorrect value returned for an unset XML attribute.") + + stanza._setAttr('bar', 'a') + stanza._setAttr('baz', 'b') + + self.checkStanza(TestStanza, stanza, """ + + """) + + self.failUnless(stanza._getAttr('bar') == 'a', + "Retrieved XML attribute value is incorrect.") + + stanza._setAttr('bar', None) + stanza._delAttr('baz') + + self.checkStanza(TestStanza, stanza, """ + + """) + + self.failUnless(stanza._getAttr('bar', 'c') == 'c', + "Incorrect default value returned for an unset XML attribute.") + suite = unittest.TestLoader().loadTestsFromTestCase(TestElementBase)