Statistics
| Branch: | Tag: | Revision:

root / ncclient / xml_.py @ bbd4ce54

History | View | Annotate | Download (7.1 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 57b5f922 Shikhar Bhushan
"""The :mod:`xml` module provides methods for creating XML documents, parsing
17 57b5f922 Shikhar Bhushan
XML, and converting between different XML representations. It uses
18 57b5f922 Shikhar Bhushan
:mod:`~xml.etree.ElementTree` internally.
19 a7cb58ce Shikhar Bhushan
"""
20 a7cb58ce Shikhar Bhushan
21 4de03d63 Shikhar Bhushan
from cStringIO import StringIO
22 94dd4342 Shikhar Bhushan
from xml.etree import cElementTree as ET
23 94dd4342 Shikhar Bhushan
24 ebf2bbc6 Shikhar Bhushan
from ncclient import NCClientError
25 ebf2bbc6 Shikhar Bhushan
26 a14c36f9 Shikhar Bhushan
class ContentError(NCClientError):
27 614a698d Shikhar Bhushan
    "Raised by methods of the :mod:`content` module in case of an error."
28 a14c36f9 Shikhar Bhushan
    pass
29 bf31e33e Shikhar Bhushan
30 65c6a607 Shikhar Bhushan
### Namespace-related
31 94dd4342 Shikhar Bhushan
32 57b5f922 Shikhar Bhushan
#: Base NETCONF namespace
33 94dd4342 Shikhar Bhushan
BASE_NS = 'urn:ietf:params:xml:ns:netconf:base:1.0'
34 a7cb58ce Shikhar Bhushan
#: ... and this is BASE_NS according to Cisco devices tested
35 94dd4342 Shikhar Bhushan
CISCO_BS = 'urn:ietf:params:netconf:base:1.0'
36 408abf6d Shikhar Bhushan
#: namespace for Tail-f data model
37 408abf6d Shikhar Bhushan
TAILF_AAA_1_1 = 'http://tail-f.com/ns/aaa/1.1'
38 57b5f922 Shikhar Bhushan
#: namespace for Tail-f data model
39 408abf6d Shikhar Bhushan
TAILF_EXECD_1_1 = 'http://tail-f.com/ns/execd/1.1'
40 57b5f922 Shikhar Bhushan
#: namespace for Cisco data model
41 57b5f922 Shikhar Bhushan
CISCO_CPI_10 = 'http://www.cisco.com/cpi_10/schema'
42 94dd4342 Shikhar Bhushan
43 94dd4342 Shikhar Bhushan
try:
44 94dd4342 Shikhar Bhushan
    register_namespace = ET.register_namespace
45 94dd4342 Shikhar Bhushan
except AttributeError:
46 94dd4342 Shikhar Bhushan
    def register_namespace(prefix, uri):
47 94dd4342 Shikhar Bhushan
        from xml.etree import ElementTree
48 94dd4342 Shikhar Bhushan
        # cElementTree uses ElementTree's _namespace_map, so that's ok
49 94dd4342 Shikhar Bhushan
        ElementTree._namespace_map[uri] = prefix
50 94dd4342 Shikhar Bhushan
51 94dd4342 Shikhar Bhushan
register_namespace('netconf', BASE_NS)
52 408abf6d Shikhar Bhushan
register_namespace('aaa', TAILF_AAA_1_1)
53 408abf6d Shikhar Bhushan
register_namespace('execd', TAILF_EXECD_1_1)
54 57b5f922 Shikhar Bhushan
register_namespace('cpi', CISCO_CPI_10)
55 57b5f922 Shikhar Bhushan
56 94dd4342 Shikhar Bhushan
57 65c6a607 Shikhar Bhushan
qualify = lambda tag, ns=BASE_NS: tag if ns is None else '{%s}%s' % (ns, tag)
58 bf31e33e Shikhar Bhushan
59 408abf6d Shikhar Bhushan
#: Deprecated
60 65c6a607 Shikhar Bhushan
multiqualify = lambda tag, nslist=(BASE_NS, CISCO_BS): [qualify(tag, ns) for ns in nslist]
61 94dd4342 Shikhar Bhushan
62 94dd4342 Shikhar Bhushan
unqualify = lambda tag: tag[tag.rfind('}')+1:]
63 94dd4342 Shikhar Bhushan
64 65c6a607 Shikhar Bhushan
### XML with Python data structures
65 65c6a607 Shikhar Bhushan
66 65c6a607 Shikhar Bhushan
class DictTree:
67 65c6a607 Shikhar Bhushan
68 65c6a607 Shikhar Bhushan
    @staticmethod
69 65c6a607 Shikhar Bhushan
    def Element(spec):
70 614a698d Shikhar Bhushan
        """DictTree -> Element
71 4f650d54 Shikhar Bhushan

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

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

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

104 614a698d Shikhar Bhushan
        :arg encoding: chraracter encoding
105 4f650d54 Shikhar Bhushan

106 614a698d Shikhar Bhushan
        :rtype: string
107 614a698d Shikhar Bhushan
        """
108 e52e8478 Shikhar Bhushan
        return Element.XML(DictTree.Element(spec), encoding)
109 65c6a607 Shikhar Bhushan
110 65c6a607 Shikhar Bhushan
class Element:
111 4f650d54 Shikhar Bhushan
112 65c6a607 Shikhar Bhushan
    @staticmethod
113 65c6a607 Shikhar Bhushan
    def DictTree(ele):
114 614a698d Shikhar Bhushan
        """DictTree -> Element
115 4f650d54 Shikhar Bhushan

116 614a698d Shikhar Bhushan
        :type spec: :class:`~xml.etree.ElementTree.Element`
117 614a698d Shikhar Bhushan
        :rtype: :obj:`dict`
118 614a698d Shikhar Bhushan
        """
119 65c6a607 Shikhar Bhushan
        return {
120 65c6a607 Shikhar Bhushan
            'tag': ele.tag,
121 65c6a607 Shikhar Bhushan
            'attributes': ele.attrib,
122 65c6a607 Shikhar Bhushan
            'text': ele.text,
123 65c6a607 Shikhar Bhushan
            'tail': ele.tail,
124 b58bb60b Shikhar Bhushan
            'subtree': [ Element.DictTree(child) for child in ele.getchildren() ]
125 65c6a607 Shikhar Bhushan
        }
126 4f650d54 Shikhar Bhushan
127 65c6a607 Shikhar Bhushan
    @staticmethod
128 0cdb8b3c Shikhar Bhushan
    def XML(ele, encoding='UTF-8'):
129 614a698d Shikhar Bhushan
        """Element -> XML
130 4f650d54 Shikhar Bhushan

131 614a698d Shikhar Bhushan
        :type spec: :class:`~xml.etree.ElementTree.Element`
132 614a698d Shikhar Bhushan
        :arg encoding: character encoding
133 614a698d Shikhar Bhushan
        :rtype: :obj:`string`
134 614a698d Shikhar Bhushan
        """
135 65c6a607 Shikhar Bhushan
        xml = ET.tostring(ele, encoding)
136 e52e8478 Shikhar Bhushan
        if xml.startswith('<?xml'):
137 e52e8478 Shikhar Bhushan
            return xml
138 e52e8478 Shikhar Bhushan
        else:
139 e52e8478 Shikhar Bhushan
            return '<?xml version="1.0" encoding="%s"?>%s' % (encoding, xml)
140 65c6a607 Shikhar Bhushan
141 65c6a607 Shikhar Bhushan
class XML:
142 4f650d54 Shikhar Bhushan
143 65c6a607 Shikhar Bhushan
    @staticmethod
144 4de03d63 Shikhar Bhushan
    def DictTree(xml):
145 614a698d Shikhar Bhushan
        """XML -> DictTree
146 4f650d54 Shikhar Bhushan

147 614a698d Shikhar Bhushan
        :type spec: :obj:`string`
148 614a698d Shikhar Bhushan
        :rtype: :obj:`dict`
149 614a698d Shikhar Bhushan
        """
150 4de03d63 Shikhar Bhushan
        return Element.DictTree(XML.Element(xml))
151 4f650d54 Shikhar Bhushan
152 65c6a607 Shikhar Bhushan
    @staticmethod
153 65c6a607 Shikhar Bhushan
    def Element(xml):
154 614a698d Shikhar Bhushan
        """XML -> Element
155 4f650d54 Shikhar Bhushan

156 614a698d Shikhar Bhushan
        :type xml: :obj:`string`
157 614a698d Shikhar Bhushan
        :rtype: :class:`~xml.etree.ElementTree.Element`
158 614a698d Shikhar Bhushan
        """
159 65c6a607 Shikhar Bhushan
        return ET.fromstring(xml)
160 d771dffc Shikhar Bhushan
161 4de03d63 Shikhar Bhushan
dtree2ele = DictTree.Element
162 4de03d63 Shikhar Bhushan
dtree2xml = DictTree.XML
163 4de03d63 Shikhar Bhushan
ele2dtree = Element.DictTree
164 4de03d63 Shikhar Bhushan
ele2xml = Element.XML
165 4de03d63 Shikhar Bhushan
xml2dtree = XML.DictTree
166 4de03d63 Shikhar Bhushan
xml2ele = XML.Element
167 4de03d63 Shikhar Bhushan
168 d771dffc Shikhar Bhushan
### Other utility functions
169 d771dffc Shikhar Bhushan
170 d771dffc Shikhar Bhushan
iselement = ET.iselement
171 d771dffc Shikhar Bhushan
172 614a698d Shikhar Bhushan
def find(ele, tag, nslist=[]):
173 57b5f922 Shikhar Bhushan
    """If *nslist* is empty, same as :meth:`xml.etree.ElementTree.Element.find`.
174 57b5f922 Shikhar Bhushan
    If it is not, *tag* is interpreted as an unqualified name and qualified
175 57b5f922 Shikhar Bhushan
    using each item in *nslist* (with a :const:`None` item in *nslit* meaning no
176 57b5f922 Shikhar Bhushan
    qualification is done). The first match is returned.
177 4f650d54 Shikhar Bhushan

178 614a698d Shikhar Bhushan
    :arg nslist: optional list of namespaces
179 216bb34c Shikhar Bhushan
    :type nslit: `string` `list`
180 d771dffc Shikhar Bhushan
    """
181 614a698d Shikhar Bhushan
    if nslist:
182 d771dffc Shikhar Bhushan
        for qname in multiqualify(tag):
183 d771dffc Shikhar Bhushan
            found = ele.find(qname)
184 d771dffc Shikhar Bhushan
            if found is not None:
185 614a698d Shikhar Bhushan
                return found
186 614a698d Shikhar Bhushan
    else:
187 614a698d Shikhar Bhushan
        return ele.find(tag)
188 d771dffc Shikhar Bhushan
189 d771dffc Shikhar Bhushan
def parse_root(raw):
190 614a698d Shikhar Bhushan
    """Efficiently parses the root element of an XML document.
191 4f650d54 Shikhar Bhushan

192 216bb34c Shikhar Bhushan
    :arg raw: XML document
193 614a698d Shikhar Bhushan
    :type raw: string
194 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.
195 216bb34c Shikhar Bhushan
    :rtype: `tuple`
196 0cdb8b3c Shikhar Bhushan
    """
197 d771dffc Shikhar Bhushan
    fp = StringIO(raw[:1024]) # this is a guess but start element beyond 1024 bytes would be a bit absurd
198 d771dffc Shikhar Bhushan
    for event, element in ET.iterparse(fp, events=('start',)):
199 d771dffc Shikhar Bhushan
        return (element.tag, element.attrib)
200 d771dffc Shikhar Bhushan
201 0b7d3b31 Shikhar Bhushan
def validated_element(rep, tags=None, attrs=None, text=None):
202 57b5f922 Shikhar Bhushan
    """Checks if the root element meets the supplied criteria. Returns a
203 57b5f922 Shikhar Bhushan
    :class:`~xml.etree.ElementTree.Element` instance if so, otherwise raises
204 57b5f922 Shikhar Bhushan
    :exc:`ContentError`.
205 4f650d54 Shikhar Bhushan

206 0b7d3b31 Shikhar Bhushan
    :arg tags: tag name or a list of allowable tag names
207 614a698d Shikhar Bhushan
    :arg attrs: list of required attribute names, each item may be a list of allowable alternatives
208 614a698d Shikhar Bhushan
    :arg text: textual content to match
209 614a698d Shikhar Bhushan
    :type rep: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
210 0cdb8b3c Shikhar Bhushan
    """
211 d771dffc Shikhar Bhushan
    ele = dtree2ele(rep)
212 614a698d Shikhar Bhushan
    err = False
213 0b7d3b31 Shikhar Bhushan
    if tags:
214 0b7d3b31 Shikhar Bhushan
        if isinstance(tags, basestring):
215 0b7d3b31 Shikhar Bhushan
            tags = [tags]
216 614a698d Shikhar Bhushan
        if ele.tag not in tags:
217 614a698d Shikhar Bhushan
            err = True
218 614a698d Shikhar Bhushan
    if attrs:
219 d771dffc Shikhar Bhushan
        for req in attrs:
220 614a698d Shikhar Bhushan
            if isinstance(req, basestring): req = [req]
221 614a698d Shikhar Bhushan
            for alt in req:
222 614a698d Shikhar Bhushan
                if alt in ele.attrib:
223 d771dffc Shikhar Bhushan
                    break
224 d771dffc Shikhar Bhushan
            else:
225 614a698d Shikhar Bhushan
                err = True
226 614a698d Shikhar Bhushan
    if text and ele.text != text:
227 614a698d Shikhar Bhushan
        err = True
228 614a698d Shikhar Bhushan
    if err:
229 614a698d Shikhar Bhushan
        raise ContentError("Element [%s] does not meet requirements" % ele.tag)
230 d771dffc Shikhar Bhushan
    return ele