Statistics
| Branch: | Tag: | Revision:

root / ncclient / content.py @ cc9af1c3

History | View | Annotate | Download (4.2 kB)

1
# Copyright 2009 Shikhar Bhushan
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
6
#
7
#    http://www.apache.org/licenses/LICENSE-2.0
8
#
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14

    
15
"TODO: docstring"
16

    
17
from xml.etree import cElementTree as ET
18

    
19

    
20
### Namespace-related ###
21

    
22
BASE_NS = 'urn:ietf:params:xml:ns:netconf:base:1.0'
23
# and this is BASE_NS according to cisco devices...
24
CISCO_BS = 'urn:ietf:params:netconf:base:1.0'
25

    
26
try:
27
    register_namespace = ET.register_namespace
28
except AttributeError:
29
    def register_namespace(prefix, uri):
30
        from xml.etree import ElementTree
31
        # cElementTree uses ElementTree's _namespace_map, so that's ok
32
        ElementTree._namespace_map[uri] = prefix
33

    
34
# we'd like BASE_NS to be prefixed as "netconf"
35
register_namespace('netconf', BASE_NS)
36

    
37
qualify = lambda tag, ns=BASE_NS: '{%s}%s' % (ns, tag)
38

    
39
# i would have written a def if lambdas weren't so much fun
40
multiqualify = lambda tag, nslist=(BASE_NS, CISCO_BS): [qualify(tag, ns)
41
                                                        for ns in nslist]
42

    
43
unqualify = lambda tag: tag[tag.rfind('}')+1:]
44

    
45
def namespaced_find(ele, tag, workaround=True):
46
    """`workaround` is for Cisco implementations (at least the one tested), 
47
    which uses an incorrect namespace.
48
    """
49
    found = None
50
    if not workaround:
51
        found = ele.find(tag)
52
    else:
53
        for qname in multiqualify(tag):
54
            found = ele.find(qname)
55
            if found is not None:
56
                break
57
    return found
58

    
59

    
60
### Build XML using Python data structures ###
61

    
62
class XMLConverter:
63
    """Build an ElementTree.Element instance from an XML tree specification
64
    based on nested dictionaries. TODO: describe spec
65
    """
66
    
67
    def __init__(self, spec):
68
        "TODO: docstring"
69
        self._root = XMLConverter.build(spec)
70
    
71
    def tostring(self, encoding='utf-8'):
72
        "TODO: docstring"
73
        xml = ET.tostring(self._root, encoding)
74
        # some etree versions don't include xml decl with utf-8
75
        # this is a problem with some devices
76
        return (xml if xml.startswith('<?xml')
77
                else '<?xml version="1.0" encoding="%s"?>%s' % (encoding, xml))
78
    
79
    @property
80
    def tree(self):
81
        "TODO: docstring"
82
        return self._root
83
    
84
    @staticmethod
85
    def build(spec):
86
        "TODO: docstring"
87
        if iselement(spec):
88
            return spec
89
        elif isinstance(spec, basestring):
90
            return ET.XML(spec)
91
        # assume isinstance(spec, dict)
92
        if 'tag' in spec:
93
            ele = ET.Element(spec.get('tag'), spec.get('attributes', {}))
94
            ele.text = spec.get('text', '')
95
            ele.tail = spec.get('tail', '')
96
            subtree = spec.get('subtree', [])
97
            # might not be properly specified as list but may be dict
98
            if isinstance(subtree, dict):
99
                subtree = [subtree]
100
            for subele in subtree:
101
                ele.append(XMLConverter.build(subele))
102
            return ele
103
        elif 'comment' in spec:
104
            return ET.Comment(spec.get('comment'))
105
        # TODO elif DOM rep
106
        else:
107
            raise ContentError('Invalid tree spec')
108
    
109
    @staticmethod
110
    def fromstring(xml):
111
        return XMLConverter.parse(ET.fromstring(xml))
112
    
113
    @staticmethod
114
    def parse(root):
115
        return {
116
            'tag': root.tag,
117
            'attributes': root.attrib,
118
            'text': root.text,
119
            'tail': root.tail,
120
            'subtree': [ XMLConverter.parse(child) for child in root.getchildren() ]
121
        }
122

    
123
## utility functions
124

    
125
iselement = ET.iselement
126

    
127
def isdom(x): return True # TODO
128

    
129
def root_ensured(rep, tag):
130
    if isinstance(rep, basestring):
131
        rep = ET.XML(rep)
132
    err = False
133
    if ((iselement(rep) and (rep.tag not in (tag, qualify(tag))) or (isdom(x)))): 
134
        raise ArgumentError("Expected root element [%s] not found" % tag)
135
    else:
136
        return rep