Revision 614a698d

b/ncclient/content.py
18 18
from ncclient import NCClientError
19 19

  
20 20
class ContentError(NCClientError):
21
    "Raised by methods of the :mod:`content` module in case of an error."
21 22
    pass
22 23

  
23 24
### Namespace-related
24 25

  
26
# : Base NETCONf namespace
25 27
BASE_NS = 'urn:ietf:params:xml:ns:netconf:base:1.0'
26
NOTIFICATION_NS = 'urn:ietf:params:xml:ns:netconf:notification:1.0'
27
# and this is BASE_NS according to cisco devices...
28
# : ... and this is BASE_NS according to Cisco devices tested
28 29
CISCO_BS = 'urn:ietf:params:netconf:base:1.0'
29 30

  
30 31
try:
......
51 52

  
52 53
    @staticmethod
53 54
    def Element(spec):
55
        """DictTree -> Element
56
        
57
        :type spec: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
58
        :rtype: :class:`~xml.etree.ElementTree.Element`
59
        """
54 60
        if iselement(spec):
55 61
            return spec
56 62
        elif isinstance(spec, basestring):
......
75 81
    
76 82
    @staticmethod
77 83
    def XML(spec, encoding='UTF-8'):
84
        """DictTree -> XML
85
        
86
        :type spec: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
87
        :arg encoding: chraracter encoding
88
        :rtype: string
89
        """
78 90
        return Element.XML(DictTree.Element(spec), encoding)
79 91

  
80 92
class Element:
81 93
    
82 94
    @staticmethod
83 95
    def DictTree(ele):
96
        """DictTree -> Element
97
        
98
        :type spec: :class:`~xml.etree.ElementTree.Element`
99
        :rtype: :obj:`dict`
100
        """
84 101
        return {
85 102
            'tag': ele.tag,
86 103
            'attributes': ele.attrib,
......
91 108
    
92 109
    @staticmethod
93 110
    def XML(ele, encoding='UTF-8'):
111
        """Element -> XML
112
        
113
        :type spec: :class:`~xml.etree.ElementTree.Element`
114
        :arg encoding: character encoding
115
        :rtype: :obj:`string`
116
        """
94 117
        xml = ET.tostring(ele, encoding)
95 118
        if xml.startswith('<?xml'):
96 119
            return xml
......
101 124
    
102 125
    @staticmethod
103 126
    def DictTree(xml):
127
        """XML -> DictTree
128
        
129
        :type spec: :obj:`string`
130
        :rtype: :obj:`dict`
131
        """
104 132
        return Element.DictTree(XML.Element(xml))
105 133
    
106 134
    @staticmethod
107 135
    def Element(xml):
136
        """XML -> Element
137
        
138
        :type xml: :obj:`string`
139
        :rtype: :class:`~xml.etree.ElementTree.Element`
140
        """
108 141
        return ET.fromstring(xml)
109 142

  
110 143
dtree2ele = DictTree.Element
......
118 151

  
119 152
iselement = ET.iselement
120 153

  
121
def find(ele, tag, strict=True, nslist=[BASE_NS, CISCO_BS]):
122
    """In strict mode, doesn't work around Cisco implementations sending incorrectly namespaced XML. Supply qualified tag name if using strict mode.
154
def find(ele, tag, nslist=[]):
155
    """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.
156
    
157
    :arg nslist: optional list of namespaces
123 158
    """
124
    if strict:
125
        return ele.find(tag)
126
    else:
159
    if nslist:
127 160
        for qname in multiqualify(tag):
128 161
            found = ele.find(qname)
129 162
            if found is not None:
130
                return found        
163
                return found
164
    else:
165
        return ele.find(tag)
131 166

  
132 167
def parse_root(raw):
133
    """
168
    """Efficiently parses the root element of an XML document.
169
    
170
    :type raw: string
171
    :returns: a tuple of `(tag, attributes)`, where `tag` is the (qualified) name of the element and `attributes` is a dictionary of its attributes.
134 172
    """
135 173
    fp = StringIO(raw[:1024]) # this is a guess but start element beyond 1024 bytes would be a bit absurd
136 174
    for event, element in ET.iterparse(fp, events=('start',)):
137 175
        return (element.tag, element.attrib)
138 176

  
139
def validated_element(rep, tag, attrs=None):
140
    """
177
def validated_element(rep, tag=None, attrs=None, text=None):
178
    """Checks if the root element meets the supplied criteria. Returns a :class:`~xml.etree.ElementTree.Element` instance if so, otherwise raises :exc:`ContentError`.
179
    
180
    :arg tag: tag name or a list of allowable tag names
181
    :arg attrs: list of required attribute names, each item may be a list of allowable alternatives
182
    :arg text: textual content to match
183
    :type rep: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
184
    :see: :ref:`dtree`
141 185
    """
142 186
    ele = dtree2ele(rep)
143
    if ele.tag not in (tag, qualify(tag)):
144
        raise ContentError("Required root element [%s] not found" % tag)
145
    if attrs is not None:
187
    err = False
188
    if tag:
189
        if isinstance(tag, basestring): tag = [tag]
190
        if ele.tag not in tags:
191
            err = True
192
    if attrs:
146 193
        for req in attrs:
147
            for attr in ele.attrib:
148
                if unqualify(attr) == req:
194
            if isinstance(req, basestring): req = [req]
195
            for alt in req:
196
                if alt in ele.attrib:
149 197
                    break
150 198
            else:
151
                raise ContentError("Required attribute [%s] not found in element [%s]" % (req, req_tag))
199
                err = True
200
    if text and ele.text != text:
201
        err = True
202
    if err:
203
        raise ContentError("Element [%s] does not meet requirements" % ele.tag)
152 204
    return ele

Also available in: Unified diff