Statistics
| Branch: | Tag: | Revision:

root / ncclient / content.py @ a7cb58ce

History | View | Annotate | Download (6.6 kB)

1 94dd4342 Shikhar Bhushan
# Copyright 2009 Shikhar Bhushan
2 94dd4342 Shikhar Bhushan
#
3 94dd4342 Shikhar Bhushan
# Licensed under the Apache License, Version 2.0 (the "License");
4 94dd4342 Shikhar Bhushan
# you may not use this file except in compliance with the License.
5 94dd4342 Shikhar Bhushan
# You may obtain a copy of the License at
6 94dd4342 Shikhar Bhushan
#
7 94dd4342 Shikhar Bhushan
#    http://www.apache.org/licenses/LICENSE-2.0
8 94dd4342 Shikhar Bhushan
#
9 94dd4342 Shikhar Bhushan
# Unless required by applicable law or agreed to in writing, software
10 94dd4342 Shikhar Bhushan
# distributed under the License is distributed on an "AS IS" BASIS,
11 94dd4342 Shikhar Bhushan
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 94dd4342 Shikhar Bhushan
# See the License for the specific language governing permissions and
13 94dd4342 Shikhar Bhushan
# limitations under the License.
14 94dd4342 Shikhar Bhushan
15 a7cb58ce Shikhar Bhushan
16 a7cb58ce Shikhar Bhushan
"""The :mod:`content` module provides methods for creating XML documents, parsing XML, and converting between different XML representations. It uses :mod:`~xml.etree.ElementTree` internally.
17 a7cb58ce Shikhar Bhushan
"""
18 a7cb58ce Shikhar Bhushan
19 4de03d63 Shikhar Bhushan
from cStringIO import StringIO
20 94dd4342 Shikhar Bhushan
from xml.etree import cElementTree as ET
21 94dd4342 Shikhar Bhushan
22 ebf2bbc6 Shikhar Bhushan
from ncclient import NCClientError
23 ebf2bbc6 Shikhar Bhushan
24 a14c36f9 Shikhar Bhushan
class ContentError(NCClientError):
25 614a698d Shikhar Bhushan
    "Raised by methods of the :mod:`content` module in case of an error."
26 a14c36f9 Shikhar Bhushan
    pass
27 bf31e33e Shikhar Bhushan
28 65c6a607 Shikhar Bhushan
### Namespace-related
29 94dd4342 Shikhar Bhushan
30 a7cb58ce Shikhar Bhushan
#: Base NETCONf namespace
31 94dd4342 Shikhar Bhushan
BASE_NS = 'urn:ietf:params:xml:ns:netconf:base:1.0'
32 a7cb58ce Shikhar Bhushan
#: ... and this is BASE_NS according to Cisco devices tested
33 94dd4342 Shikhar Bhushan
CISCO_BS = 'urn:ietf:params:netconf:base:1.0'
34 94dd4342 Shikhar Bhushan
35 94dd4342 Shikhar Bhushan
try:
36 94dd4342 Shikhar Bhushan
    register_namespace = ET.register_namespace
37 94dd4342 Shikhar Bhushan
except AttributeError:
38 94dd4342 Shikhar Bhushan
    def register_namespace(prefix, uri):
39 94dd4342 Shikhar Bhushan
        from xml.etree import ElementTree
40 94dd4342 Shikhar Bhushan
        # cElementTree uses ElementTree's _namespace_map, so that's ok
41 94dd4342 Shikhar Bhushan
        ElementTree._namespace_map[uri] = prefix
42 94dd4342 Shikhar Bhushan
43 e91a5349 Shikhar Bhushan
# we'd like BASE_NS to be prefixed as "netconf"
44 94dd4342 Shikhar Bhushan
register_namespace('netconf', BASE_NS)
45 94dd4342 Shikhar Bhushan
46 65c6a607 Shikhar Bhushan
qualify = lambda tag, ns=BASE_NS: tag if ns is None else '{%s}%s' % (ns, tag)
47 bf31e33e Shikhar Bhushan
48 0cdb8b3c Shikhar Bhushan
# deprecated
49 65c6a607 Shikhar Bhushan
multiqualify = lambda tag, nslist=(BASE_NS, CISCO_BS): [qualify(tag, ns) for ns in nslist]
50 94dd4342 Shikhar Bhushan
51 94dd4342 Shikhar Bhushan
unqualify = lambda tag: tag[tag.rfind('}')+1:]
52 94dd4342 Shikhar Bhushan
53 65c6a607 Shikhar Bhushan
### XML with Python data structures
54 65c6a607 Shikhar Bhushan
55 65c6a607 Shikhar Bhushan
class DictTree:
56 65c6a607 Shikhar Bhushan
57 65c6a607 Shikhar Bhushan
    @staticmethod
58 65c6a607 Shikhar Bhushan
    def Element(spec):
59 614a698d Shikhar Bhushan
        """DictTree -> Element
60 4f650d54 Shikhar Bhushan

61 614a698d Shikhar Bhushan
        :type spec: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
62 4f650d54 Shikhar Bhushan

63 614a698d Shikhar Bhushan
        :rtype: :class:`~xml.etree.ElementTree.Element`
64 614a698d Shikhar Bhushan
        """
65 65c6a607 Shikhar Bhushan
        if iselement(spec):
66 65c6a607 Shikhar Bhushan
            return spec
67 65c6a607 Shikhar Bhushan
        elif isinstance(spec, basestring):
68 65c6a607 Shikhar Bhushan
            return XML.Element(spec)
69 65c6a607 Shikhar Bhushan
        if not isinstance(spec, dict):
70 65c6a607 Shikhar Bhushan
            raise ContentError("Invalid tree spec")
71 65c6a607 Shikhar Bhushan
        if 'tag' in spec:
72 4f650d54 Shikhar Bhushan
            ele = ET.Element(spec.get('tag'), spec.get('attrib', {}))
73 65c6a607 Shikhar Bhushan
            ele.text = spec.get('text', '')
74 65c6a607 Shikhar Bhushan
            ele.tail = spec.get('tail', '')
75 65c6a607 Shikhar Bhushan
            subtree = spec.get('subtree', [])
76 65c6a607 Shikhar Bhushan
            # might not be properly specified as list but may be dict
77 65c6a607 Shikhar Bhushan
            if isinstance(subtree, dict):
78 65c6a607 Shikhar Bhushan
                subtree = [subtree]
79 65c6a607 Shikhar Bhushan
            for subele in subtree:
80 65c6a607 Shikhar Bhushan
                ele.append(DictTree.Element(subele))
81 65c6a607 Shikhar Bhushan
            return ele
82 65c6a607 Shikhar Bhushan
        elif 'comment' in spec:
83 65c6a607 Shikhar Bhushan
            return ET.Comment(spec.get('comment'))
84 65c6a607 Shikhar Bhushan
        else:
85 65c6a607 Shikhar Bhushan
            raise ContentError('Invalid tree spec')
86 4f650d54 Shikhar Bhushan
87 65c6a607 Shikhar Bhushan
    @staticmethod
88 0cdb8b3c Shikhar Bhushan
    def XML(spec, encoding='UTF-8'):
89 614a698d Shikhar Bhushan
        """DictTree -> XML
90 4f650d54 Shikhar Bhushan

91 614a698d Shikhar Bhushan
        :type spec: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
92 4f650d54 Shikhar Bhushan

93 614a698d Shikhar Bhushan
        :arg encoding: chraracter encoding
94 4f650d54 Shikhar Bhushan

95 614a698d Shikhar Bhushan
        :rtype: string
96 614a698d Shikhar Bhushan
        """
97 e52e8478 Shikhar Bhushan
        return Element.XML(DictTree.Element(spec), encoding)
98 65c6a607 Shikhar Bhushan
99 65c6a607 Shikhar Bhushan
class Element:
100 4f650d54 Shikhar Bhushan
101 65c6a607 Shikhar Bhushan
    @staticmethod
102 65c6a607 Shikhar Bhushan
    def DictTree(ele):
103 614a698d Shikhar Bhushan
        """DictTree -> Element
104 4f650d54 Shikhar Bhushan

105 614a698d Shikhar Bhushan
        :type spec: :class:`~xml.etree.ElementTree.Element`
106 614a698d Shikhar Bhushan
        :rtype: :obj:`dict`
107 614a698d Shikhar Bhushan
        """
108 65c6a607 Shikhar Bhushan
        return {
109 65c6a607 Shikhar Bhushan
            'tag': ele.tag,
110 65c6a607 Shikhar Bhushan
            'attributes': ele.attrib,
111 65c6a607 Shikhar Bhushan
            'text': ele.text,
112 65c6a607 Shikhar Bhushan
            'tail': ele.tail,
113 65c6a607 Shikhar Bhushan
            'subtree': [ Element.DictTree(child) for child in root.getchildren() ]
114 65c6a607 Shikhar Bhushan
        }
115 4f650d54 Shikhar Bhushan
116 65c6a607 Shikhar Bhushan
    @staticmethod
117 0cdb8b3c Shikhar Bhushan
    def XML(ele, encoding='UTF-8'):
118 614a698d Shikhar Bhushan
        """Element -> XML
119 4f650d54 Shikhar Bhushan

120 614a698d Shikhar Bhushan
        :type spec: :class:`~xml.etree.ElementTree.Element`
121 614a698d Shikhar Bhushan
        :arg encoding: character encoding
122 614a698d Shikhar Bhushan
        :rtype: :obj:`string`
123 614a698d Shikhar Bhushan
        """
124 65c6a607 Shikhar Bhushan
        xml = ET.tostring(ele, encoding)
125 e52e8478 Shikhar Bhushan
        if xml.startswith('<?xml'):
126 e52e8478 Shikhar Bhushan
            return xml
127 e52e8478 Shikhar Bhushan
        else:
128 e52e8478 Shikhar Bhushan
            return '<?xml version="1.0" encoding="%s"?>%s' % (encoding, xml)
129 65c6a607 Shikhar Bhushan
130 65c6a607 Shikhar Bhushan
class XML:
131 4f650d54 Shikhar Bhushan
132 65c6a607 Shikhar Bhushan
    @staticmethod
133 4de03d63 Shikhar Bhushan
    def DictTree(xml):
134 614a698d Shikhar Bhushan
        """XML -> DictTree
135 4f650d54 Shikhar Bhushan

136 614a698d Shikhar Bhushan
        :type spec: :obj:`string`
137 614a698d Shikhar Bhushan
        :rtype: :obj:`dict`
138 614a698d Shikhar Bhushan
        """
139 4de03d63 Shikhar Bhushan
        return Element.DictTree(XML.Element(xml))
140 4f650d54 Shikhar Bhushan
141 65c6a607 Shikhar Bhushan
    @staticmethod
142 65c6a607 Shikhar Bhushan
    def Element(xml):
143 614a698d Shikhar Bhushan
        """XML -> Element
144 4f650d54 Shikhar Bhushan

145 614a698d Shikhar Bhushan
        :type xml: :obj:`string`
146 614a698d Shikhar Bhushan
        :rtype: :class:`~xml.etree.ElementTree.Element`
147 614a698d Shikhar Bhushan
        """
148 65c6a607 Shikhar Bhushan
        return ET.fromstring(xml)
149 d771dffc Shikhar Bhushan
150 4de03d63 Shikhar Bhushan
dtree2ele = DictTree.Element
151 4de03d63 Shikhar Bhushan
dtree2xml = DictTree.XML
152 4de03d63 Shikhar Bhushan
ele2dtree = Element.DictTree
153 4de03d63 Shikhar Bhushan
ele2xml = Element.XML
154 4de03d63 Shikhar Bhushan
xml2dtree = XML.DictTree
155 4de03d63 Shikhar Bhushan
xml2ele = XML.Element
156 4de03d63 Shikhar Bhushan
157 d771dffc Shikhar Bhushan
### Other utility functions
158 d771dffc Shikhar Bhushan
159 d771dffc Shikhar Bhushan
iselement = ET.iselement
160 d771dffc Shikhar Bhushan
161 614a698d Shikhar Bhushan
def find(ele, tag, nslist=[]):
162 614a698d Shikhar Bhushan
    """If `nslist` is empty, same as :meth:`xml.etree.ElementTree.Element.find`. If it is not, `tag` is interpreted as an unqualified name and qualified using each item in `nslist`. The first match is returned.
163 4f650d54 Shikhar Bhushan

164 614a698d Shikhar Bhushan
    :arg nslist: optional list of namespaces
165 d771dffc Shikhar Bhushan
    """
166 614a698d Shikhar Bhushan
    if nslist:
167 d771dffc Shikhar Bhushan
        for qname in multiqualify(tag):
168 d771dffc Shikhar Bhushan
            found = ele.find(qname)
169 d771dffc Shikhar Bhushan
            if found is not None:
170 614a698d Shikhar Bhushan
                return found
171 614a698d Shikhar Bhushan
    else:
172 614a698d Shikhar Bhushan
        return ele.find(tag)
173 d771dffc Shikhar Bhushan
174 d771dffc Shikhar Bhushan
def parse_root(raw):
175 614a698d Shikhar Bhushan
    """Efficiently parses the root element of an XML document.
176 4f650d54 Shikhar Bhushan

177 614a698d Shikhar Bhushan
    :type raw: string
178 614a698d Shikhar Bhushan
    :returns: a tuple of `(tag, attributes)`, where `tag` is the (qualified) name of the element and `attributes` is a dictionary of its attributes.
179 0cdb8b3c Shikhar Bhushan
    """
180 d771dffc Shikhar Bhushan
    fp = StringIO(raw[:1024]) # this is a guess but start element beyond 1024 bytes would be a bit absurd
181 d771dffc Shikhar Bhushan
    for event, element in ET.iterparse(fp, events=('start',)):
182 d771dffc Shikhar Bhushan
        return (element.tag, element.attrib)
183 d771dffc Shikhar Bhushan
184 614a698d Shikhar Bhushan
def validated_element(rep, tag=None, attrs=None, text=None):
185 614a698d Shikhar Bhushan
    """Checks if the root element meets the supplied criteria. Returns a :class:`~xml.etree.ElementTree.Element` instance if so, otherwise raises :exc:`ContentError`.
186 4f650d54 Shikhar Bhushan

187 614a698d Shikhar Bhushan
    :arg tag: tag name or a list of allowable tag names
188 614a698d Shikhar Bhushan
    :arg attrs: list of required attribute names, each item may be a list of allowable alternatives
189 614a698d Shikhar Bhushan
    :arg text: textual content to match
190 614a698d Shikhar Bhushan
    :type rep: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
191 0cdb8b3c Shikhar Bhushan
    """
192 d771dffc Shikhar Bhushan
    ele = dtree2ele(rep)
193 614a698d Shikhar Bhushan
    err = False
194 614a698d Shikhar Bhushan
    if tag:
195 614a698d Shikhar Bhushan
        if isinstance(tag, basestring): tag = [tag]
196 614a698d Shikhar Bhushan
        if ele.tag not in tags:
197 614a698d Shikhar Bhushan
            err = True
198 614a698d Shikhar Bhushan
    if attrs:
199 d771dffc Shikhar Bhushan
        for req in attrs:
200 614a698d Shikhar Bhushan
            if isinstance(req, basestring): req = [req]
201 614a698d Shikhar Bhushan
            for alt in req:
202 614a698d Shikhar Bhushan
                if alt in ele.attrib:
203 d771dffc Shikhar Bhushan
                    break
204 d771dffc Shikhar Bhushan
            else:
205 614a698d Shikhar Bhushan
                err = True
206 614a698d Shikhar Bhushan
    if text and ele.text != text:
207 614a698d Shikhar Bhushan
        err = True
208 614a698d Shikhar Bhushan
    if err:
209 614a698d Shikhar Bhushan
        raise ContentError("Element [%s] does not meet requirements" % ele.tag)
210 d771dffc Shikhar Bhushan
    return ele