From da332365d4e97720ac182e0f66b73919d764a555 Mon Sep 17 00:00:00 2001
From: Lance Stout <lancestout@gmail.com>
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("<iq xmlns='jabber:client' foo='bar' />"))

>>> i1['foo'] = '3'
>>> i1
'3'
>>> i1
'<iq id="0" foo="3" />'
>>> i2
'<iq id="0" foo="bar" />'
>>> i2['foo']
'bar'
>>> del i2['foo']
>>> i2
'<iq id="0" />'
---
 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