git-svn-id: http://ncclient.googlecode.com/svn/trunk@84 6dbcf712-26ac-11de-a2f3-13738...
[ncclient] / ncclient / content.py
index a82426c..cc073d4 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import logging
-from xml.etree import cElementTree as ElementTree
-from cStringIO import StringIO
+"TODO: docstring"
 
-logger = logging.getLogger('ncclient.content')
+from xml.etree import cElementTree as ET
 
+### Namespace-related ###
 
-def qualify(tag, ns=None):
-    if ns is None:
-        return tag
-    else:
-        return '{%s}%s' % (ns, tag)
-_ = qualify
+BASE_NS = 'urn:ietf:params:xml:ns:netconf:base:1.0'
 
+# cisco returns incorrectly namespaced xml
+CISCO_BS = 'urn:ietf:params:netconf:base:1.0'
 
-class RootElementParser:
-    
-    '''Parse the root element of an XML document. The tag and namespace of
-    recognized elements, and attributes of interest can be customized.
-    
-    RootElementParser does not parse any sub-elements.
-    '''
-    
-    def __init__(self, recognize=[]):
-        self._recognize = recognize
-    
-    def recognize(self, element):
-        '''Specify an element that should be successfully parsed.
-        
-        element should be a string that represents a qualified name of the form
-        *{namespace}tag*.
-        '''
-        self._recognize.append((element, attrs))
-    
-    def parse(self, raw):
-        '''Parse the root element from a string representing an XML document.
-        
-        Returns a (tag, attributes) tuple. tag is a string representing
-        the qualified name of the recognized element. attributes is a
-        {'attr': value} dictionary.
-        '''
-        fp = StringIO(raw)
-        for event, element in ElementTree.iterparse(fp, events=('start',)):
-            for e in self._recognize:
-                if element.tag == e:
-                    return (element.tag, element.attrib)
-            break
-        return None
+try:
+    register_namespace = ET.register_namespace
+except AttributeError:
+    def register_namespace(prefix, uri):
+        from xml.etree import ElementTree
+        # cElementTree uses ElementTree's _namespace_map, so that's ok
+        ElementTree._namespace_map[uri] = prefix
 
+# we'd like BASE_NS to be prefixed as "netconf"
+register_namespace('netconf', BASE_NS)
 
-###########
-
-class XMLBuilder:
-    
-    @staticmethod
-    def _element(node):
-        element = ElementTree.Element( _(node.get('tag'),
-                                         node.get('namespace', None)),
-                                      node.get('attributes', {}))
-        if node.has_key('children'):
-            for child in node['children']:
-                element.append(_make_element(child))
-        else:
-            return element
-    
-    @staticmethod
-    def _etree(tree_dict):
-        return ElementTree.ElementTree(XMLBuilder._element(tree_dict))
-    
-    @staticmethod
-    def to_xml(tree_dict, encoding='utf-8'):
-        fp = StringIO()
-        self._etree(tree_dict).write(fp, encoding)
-        return fp.get_value()
+qualify = lambda tag, ns: '{%s}%s' % (namespace, tag)
 
+unqualify = lambda tag: tag[tag.rfind('}')+1:]
 
-### Hello exchange
+### Build XML using Python data structures ###
 
-class Hello:
+class TreeBuilder:
+    """Build an ElementTree.Element instance from an XML tree specification
+    based on nested dictionaries. TODO: describe spec
+    """
     
-    NS = 'urn:ietf:params:xml:ns:netconf:base:1.0'
+    def __init__(self, spec):
+        "TODO: docstring"
+        self._root = TreeBuilder.build(spec)
+        
+    def to_string(self, encoding='utf-8'):
+        "TODO: docstring"
+        xml = ET.tostring(self._root, encoding)
+        # some etree versions don't always include xml decl
+        # this is a problem with some devices
+        if not xml.startswith('<?xml'):
+            return '<?xml version="1.0" encoding="%s"?>%s' % (encoding, xml)
+        else:
+            return xml
     
-    @staticmethod
-    def build(capabilities, encoding='utf-8'):
-        hello = ElementTree.Element(_('hello', Hello.NS))
-        caps = ElementTree.Element('capabilities')
-        for uri in capabilities:
-            cap = ElementTree.Element('capability')
-            cap.text = uri
-            caps.append(cap)
-        hello.append(caps)
-        tree = ElementTree.ElementTree(hello)
-        fp = StringIO()
-        tree.write(fp, encoding)
-        return fp.getvalue()
+    @property
+    def tree(self):
+        "TODO: docstring"
+        return self._root
     
     @staticmethod
-    def parse(raw):
-        'Returns tuple of (session-id, ["capability_uri", ...])'
-        id, capabilities = 0, []
-        root = ElementTree.fromstring(raw)
-        if root.tag == _('hello', Hello.NS):
-            for child in root.getchildren():
-                if child.tag == _('session-id', Hello.NS):
-                    id = int(child.text)
-                elif child.tag == _('capabilities', Hello.NS):
-                    for cap in child.getiterator(_('capability', Hello.NS)):
-                        capabilities.append(cap.text)
-        return id, capabilities
+    def build(spec):
+        "TODO: docstring"
+        if spec.has_key('tag'):
+            ele = ET.Element(spec.get('tag'), spec.get('attributes', {}))
+            ele.text = spec.get('text', '')
+            children = spec.get('children', [])
+            if isinstance(children, dict):
+                children = [children]
+            for child in children:
+                ele.append(TreeBuilder.build(child))
+            return ele
+        elif spec.has_key('comment'):
+            return ET.Comment(spec.get('comment'))
+        else:
+            raise ValueError('Invalid tree spec')