/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/util/elementtree/ElementTree.py

(jelmer) Use the absolute_import feature everywhere in bzrlib,
 and add a source test to make sure it's used everywhere. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
# ElementTree
 
3
# $Id: ElementTree.py 2326 2005-03-17 07:45:21Z fredrik $
 
4
#
 
5
# light-weight XML support for Python 1.5.2 and later.
 
6
#
 
7
# history:
 
8
# 2001-10-20 fl   created (from various sources)
 
9
# 2001-11-01 fl   return root from parse method
 
10
# 2002-02-16 fl   sort attributes in lexical order
 
11
# 2002-04-06 fl   TreeBuilder refactoring, added PythonDoc markup
 
12
# 2002-05-01 fl   finished TreeBuilder refactoring
 
13
# 2002-07-14 fl   added basic namespace support to ElementTree.write
 
14
# 2002-07-25 fl   added QName attribute support
 
15
# 2002-10-20 fl   fixed encoding in write
 
16
# 2002-11-24 fl   changed default encoding to ascii; fixed attribute encoding
 
17
# 2002-11-27 fl   accept file objects or file names for parse/write
 
18
# 2002-12-04 fl   moved XMLTreeBuilder back to this module
 
19
# 2003-01-11 fl   fixed entity encoding glitch for us-ascii
 
20
# 2003-02-13 fl   added XML literal factory
 
21
# 2003-02-21 fl   added ProcessingInstruction/PI factory
 
22
# 2003-05-11 fl   added tostring/fromstring helpers
 
23
# 2003-05-26 fl   added ElementPath support
 
24
# 2003-07-05 fl   added makeelement factory method
 
25
# 2003-07-28 fl   added more well-known namespace prefixes
 
26
# 2003-08-15 fl   fixed typo in ElementTree.findtext (Thomas Dartsch)
 
27
# 2003-09-04 fl   fall back on emulator if ElementPath is not installed
 
28
# 2003-10-31 fl   markup updates
 
29
# 2003-11-15 fl   fixed nested namespace bug
 
30
# 2004-03-28 fl   added XMLID helper
 
31
# 2004-06-02 fl   added default support to findtext
 
32
# 2004-06-08 fl   fixed encoding of non-ascii element/attribute names
 
33
# 2004-08-23 fl   take advantage of post-2.1 expat features
 
34
# 2005-02-01 fl   added iterparse implementation
 
35
# 2005-03-02 fl   fixed iterparse support for pre-2.2 versions
 
36
#
 
37
# Copyright (c) 1999-2005 by Fredrik Lundh.  All rights reserved.
 
38
#
 
39
# fredrik@pythonware.com
 
40
# http://www.pythonware.com
 
41
#
 
42
# --------------------------------------------------------------------
 
43
# The ElementTree toolkit is
 
44
#
 
45
# Copyright (c) 1999-2005 by Fredrik Lundh
 
46
#
 
47
# By obtaining, using, and/or copying this software and/or its
 
48
# associated documentation, you agree that you have read, understood,
 
49
# and will comply with the following terms and conditions:
 
50
#
 
51
# Permission to use, copy, modify, and distribute this software and
 
52
# its associated documentation for any purpose and without fee is
 
53
# hereby granted, provided that the above copyright notice appears in
 
54
# all copies, and that both that copyright notice and this permission
 
55
# notice appear in supporting documentation, and that the name of
 
56
# Secret Labs AB or the author not be used in advertising or publicity
 
57
# pertaining to distribution of the software without specific, written
 
58
# prior permission.
 
59
#
 
60
# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
 
61
# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
 
62
# ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
 
63
# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
 
64
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 
65
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 
66
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 
67
# OF THIS SOFTWARE.
 
68
# --------------------------------------------------------------------
 
69
 
 
70
from __future__ import absolute_import
 
71
 
 
72
__all__ = [
 
73
    # public symbols
 
74
    "Comment",
 
75
    "dump",
 
76
    "Element", "ElementTree",
 
77
    "fromstring",
 
78
    "iselement", "iterparse",
 
79
    "parse",
 
80
    "PI", "ProcessingInstruction",
 
81
    "QName",
 
82
    "SubElement",
 
83
    "tostring",
 
84
    "TreeBuilder",
 
85
    "VERSION", "XML",
 
86
    "XMLTreeBuilder",
 
87
    ]
 
88
 
 
89
##
 
90
# The <b>Element</b> type is a flexible container object, designed to
 
91
# store hierarchical data structures in memory. The type can be
 
92
# described as a cross between a list and a dictionary.
 
93
# <p>
 
94
# Each element has a number of properties associated with it:
 
95
# <ul>
 
96
# <li>a <i>tag</i>. This is a string identifying what kind of data
 
97
# this element represents (the element type, in other words).</li>
 
98
# <li>a number of <i>attributes</i>, stored in a Python dictionary.</li>
 
99
# <li>a <i>text</i> string.</li>
 
100
# <li>an optional <i>tail</i> string.</li>
 
101
# <li>a number of <i>child elements</i>, stored in a Python sequence</li>
 
102
# </ul>
 
103
#
 
104
# To create an element instance, use the {@link #Element} or {@link
 
105
# #SubElement} factory functions.
 
106
# <p>
 
107
# The {@link #ElementTree} class can be used to wrap an element
 
108
# structure, and convert it from and to XML.
 
109
##
 
110
 
 
111
import string, sys, re
 
112
 
 
113
class _SimpleElementPath:
 
114
    # emulate pre-1.2 find/findtext/findall behaviour
 
115
    def find(self, element, tag):
 
116
        for elem in element:
 
117
            if elem.tag == tag:
 
118
                return elem
 
119
        return None
 
120
    def findtext(self, element, tag, default=None):
 
121
        for elem in element:
 
122
            if elem.tag == tag:
 
123
                return elem.text or ""
 
124
        return default
 
125
    def findall(self, element, tag):
 
126
        if tag[:3] == ".//":
 
127
            return element.getiterator(tag[3:])
 
128
        result = []
 
129
        for elem in element:
 
130
            if elem.tag == tag:
 
131
                result.append(elem)
 
132
        return result
 
133
 
 
134
try:
 
135
    import ElementPath
 
136
except ImportError:
 
137
    # FIXME: issue warning in this case?
 
138
    ElementPath = _SimpleElementPath()
 
139
 
 
140
# TODO: add support for custom namespace resolvers/default namespaces
 
141
# TODO: add improved support for incremental parsing
 
142
 
 
143
VERSION = "1.2.6"
 
144
 
 
145
##
 
146
# Internal element class.  This class defines the Element interface,
 
147
# and provides a reference implementation of this interface.
 
148
# <p>
 
149
# You should not create instances of this class directly.  Use the
 
150
# appropriate factory functions instead, such as {@link #Element}
 
151
# and {@link #SubElement}.
 
152
#
 
153
# @see Element
 
154
# @see SubElement
 
155
# @see Comment
 
156
# @see ProcessingInstruction
 
157
 
 
158
class _ElementInterface:
 
159
    # <tag attrib>text<child/>...</tag>tail
 
160
 
 
161
    ##
 
162
    # (Attribute) Element tag.
 
163
 
 
164
    tag = None
 
165
 
 
166
    ##
 
167
    # (Attribute) Element attribute dictionary.  Where possible, use
 
168
    # {@link #_ElementInterface.get},
 
169
    # {@link #_ElementInterface.set},
 
170
    # {@link #_ElementInterface.keys}, and
 
171
    # {@link #_ElementInterface.items} to access
 
172
    # element attributes.
 
173
 
 
174
    attrib = None
 
175
 
 
176
    ##
 
177
    # (Attribute) Text before first subelement.  This is either a
 
178
    # string or the value None, if there was no text.
 
179
 
 
180
    text = None
 
181
 
 
182
    ##
 
183
    # (Attribute) Text after this element's end tag, but before the
 
184
    # next sibling element's start tag.  This is either a string or
 
185
    # the value None, if there was no text.
 
186
 
 
187
    tail = None # text after end tag, if any
 
188
 
 
189
    def __init__(self, tag, attrib):
 
190
        self.tag = tag
 
191
        self.attrib = attrib
 
192
        self._children = []
 
193
 
 
194
    def __repr__(self):
 
195
        return "<Element %s at %x>" % (self.tag, id(self))
 
196
 
 
197
    ##
 
198
    # Creates a new element object of the same type as this element.
 
199
    #
 
200
    # @param tag Element tag.
 
201
    # @param attrib Element attributes, given as a dictionary.
 
202
    # @return A new element instance.
 
203
 
 
204
    def makeelement(self, tag, attrib):
 
205
        return Element(tag, attrib)
 
206
 
 
207
    ##
 
208
    # Returns the number of subelements.
 
209
    #
 
210
    # @return The number of subelements.
 
211
 
 
212
    def __len__(self):
 
213
        return len(self._children)
 
214
 
 
215
    ##
 
216
    # Returns the given subelement.
 
217
    #
 
218
    # @param index What subelement to return.
 
219
    # @return The given subelement.
 
220
    # @exception IndexError If the given element does not exist.
 
221
 
 
222
    def __getitem__(self, index):
 
223
        return self._children[index]
 
224
 
 
225
    ##
 
226
    # Replaces the given subelement.
 
227
    #
 
228
    # @param index What subelement to replace.
 
229
    # @param element The new element value.
 
230
    # @exception IndexError If the given element does not exist.
 
231
    # @exception AssertionError If element is not a valid object.
 
232
 
 
233
    def __setitem__(self, index, element):
 
234
        assert iselement(element)
 
235
        self._children[index] = element
 
236
 
 
237
    ##
 
238
    # Deletes the given subelement.
 
239
    #
 
240
    # @param index What subelement to delete.
 
241
    # @exception IndexError If the given element does not exist.
 
242
 
 
243
    def __delitem__(self, index):
 
244
        del self._children[index]
 
245
 
 
246
    ##
 
247
    # Returns a list containing subelements in the given range.
 
248
    #
 
249
    # @param start The first subelement to return.
 
250
    # @param stop The first subelement that shouldn't be returned.
 
251
    # @return A sequence object containing subelements.
 
252
 
 
253
    def __getslice__(self, start, stop):
 
254
        return self._children[start:stop]
 
255
 
 
256
    ##
 
257
    # Replaces a number of subelements with elements from a sequence.
 
258
    #
 
259
    # @param start The first subelement to replace.
 
260
    # @param stop The first subelement that shouldn't be replaced.
 
261
    # @param elements A sequence object with zero or more elements.
 
262
    # @exception AssertionError If a sequence member is not a valid object.
 
263
 
 
264
    def __setslice__(self, start, stop, elements):
 
265
        for element in elements:
 
266
            assert iselement(element)
 
267
        self._children[start:stop] = list(elements)
 
268
 
 
269
    ##
 
270
    # Deletes a number of subelements.
 
271
    #
 
272
    # @param start The first subelement to delete.
 
273
    # @param stop The first subelement to leave in there.
 
274
 
 
275
    def __delslice__(self, start, stop):
 
276
        del self._children[start:stop]
 
277
 
 
278
    ##
 
279
    # Adds a subelement to the end of this element.
 
280
    #
 
281
    # @param element The element to add.
 
282
    # @exception AssertionError If a sequence member is not a valid object.
 
283
 
 
284
    def append(self, element):
 
285
        assert iselement(element)
 
286
        self._children.append(element)
 
287
 
 
288
    ##
 
289
    # Inserts a subelement at the given position in this element.
 
290
    #
 
291
    # @param index Where to insert the new subelement.
 
292
    # @exception AssertionError If the element is not a valid object.
 
293
 
 
294
    def insert(self, index, element):
 
295
        assert iselement(element)
 
296
        self._children.insert(index, element)
 
297
 
 
298
    ##
 
299
    # Removes a matching subelement.  Unlike the <b>find</b> methods,
 
300
    # this method compares elements based on identity, not on tag
 
301
    # value or contents.
 
302
    #
 
303
    # @param element What element to remove.
 
304
    # @exception ValueError If a matching element could not be found.
 
305
    # @exception AssertionError If the element is not a valid object.
 
306
 
 
307
    def remove(self, element):
 
308
        assert iselement(element)
 
309
        self._children.remove(element)
 
310
 
 
311
    ##
 
312
    # Returns all subelements.  The elements are returned in document
 
313
    # order.
 
314
    #
 
315
    # @return A list of subelements.
 
316
    # @defreturn list of Element instances
 
317
 
 
318
    def getchildren(self):
 
319
        return self._children
 
320
 
 
321
    ##
 
322
    # Finds the first matching subelement, by tag name or path.
 
323
    #
 
324
    # @param path What element to look for.
 
325
    # @return The first matching element, or None if no element was found.
 
326
    # @defreturn Element or None
 
327
 
 
328
    def find(self, path):
 
329
        return ElementPath.find(self, path)
 
330
 
 
331
    ##
 
332
    # Finds text for the first matching subelement, by tag name or path.
 
333
    #
 
334
    # @param path What element to look for.
 
335
    # @param default What to return if the element was not found.
 
336
    # @return The text content of the first matching element, or the
 
337
    #     default value no element was found.  Note that if the element
 
338
    #     has is found, but has no text content, this method returns an
 
339
    #     empty string.
 
340
    # @defreturn string
 
341
 
 
342
    def findtext(self, path, default=None):
 
343
        return ElementPath.findtext(self, path, default)
 
344
 
 
345
    ##
 
346
    # Finds all matching subelements, by tag name or path.
 
347
    #
 
348
    # @param path What element to look for.
 
349
    # @return A list or iterator containing all matching elements,
 
350
    #    in document order.
 
351
    # @defreturn list of Element instances
 
352
 
 
353
    def findall(self, path):
 
354
        return ElementPath.findall(self, path)
 
355
 
 
356
    ##
 
357
    # Resets an element.  This function removes all subelements, clears
 
358
    # all attributes, and sets the text and tail attributes to None.
 
359
 
 
360
    def clear(self):
 
361
        self.attrib.clear()
 
362
        self._children = []
 
363
        self.text = self.tail = None
 
364
 
 
365
    ##
 
366
    # Gets an element attribute.
 
367
    #
 
368
    # @param key What attribute to look for.
 
369
    # @param default What to return if the attribute was not found.
 
370
    # @return The attribute value, or the default value, if the
 
371
    #     attribute was not found.
 
372
    # @defreturn string or None
 
373
 
 
374
    def get(self, key, default=None):
 
375
        return self.attrib.get(key, default)
 
376
 
 
377
    ##
 
378
    # Sets an element attribute.
 
379
    #
 
380
    # @param key What attribute to set.
 
381
    # @param value The attribute value.
 
382
 
 
383
    def set(self, key, value):
 
384
        self.attrib[key] = value
 
385
 
 
386
    ##
 
387
    # Gets a list of attribute names.  The names are returned in an
 
388
    # arbitrary order (just like for an ordinary Python dictionary).
 
389
    #
 
390
    # @return A list of element attribute names.
 
391
    # @defreturn list of strings
 
392
 
 
393
    def keys(self):
 
394
        return self.attrib.keys()
 
395
 
 
396
    ##
 
397
    # Gets element attributes, as a sequence.  The attributes are
 
398
    # returned in an arbitrary order.
 
399
    #
 
400
    # @return A list of (name, value) tuples for all attributes.
 
401
    # @defreturn list of (string, string) tuples
 
402
 
 
403
    def items(self):
 
404
        return self.attrib.items()
 
405
 
 
406
    ##
 
407
    # Creates a tree iterator.  The iterator loops over this element
 
408
    # and all subelements, in document order, and returns all elements
 
409
    # with a matching tag.
 
410
    # <p>
 
411
    # If the tree structure is modified during iteration, the result
 
412
    # is undefined.
 
413
    #
 
414
    # @param tag What tags to look for (default is to return all elements).
 
415
    # @return A list or iterator containing all the matching elements.
 
416
    # @defreturn list or iterator
 
417
 
 
418
    def getiterator(self, tag=None):
 
419
        nodes = []
 
420
        if tag == "*":
 
421
            tag = None
 
422
        if tag is None or self.tag == tag:
 
423
            nodes.append(self)
 
424
        for node in self._children:
 
425
            nodes.extend(node.getiterator(tag))
 
426
        return nodes
 
427
 
 
428
# compatibility
 
429
_Element = _ElementInterface
 
430
 
 
431
##
 
432
# Element factory.  This function returns an object implementing the
 
433
# standard Element interface.  The exact class or type of that object
 
434
# is implementation dependent, but it will always be compatible with
 
435
# the {@link #_ElementInterface} class in this module.
 
436
# <p>
 
437
# The element name, attribute names, and attribute values can be
 
438
# either 8-bit ASCII strings or Unicode strings.
 
439
#
 
440
# @param tag The element name.
 
441
# @param attrib An optional dictionary, containing element attributes.
 
442
# @param **extra Additional attributes, given as keyword arguments.
 
443
# @return An element instance.
 
444
# @defreturn Element
 
445
 
 
446
def Element(tag, attrib={}, **extra):
 
447
    attrib = attrib.copy()
 
448
    attrib.update(extra)
 
449
    return _ElementInterface(tag, attrib)
 
450
 
 
451
##
 
452
# Subelement factory.  This function creates an element instance, and
 
453
# appends it to an existing element.
 
454
# <p>
 
455
# The element name, attribute names, and attribute values can be
 
456
# either 8-bit ASCII strings or Unicode strings.
 
457
#
 
458
# @param parent The parent element.
 
459
# @param tag The subelement name.
 
460
# @param attrib An optional dictionary, containing element attributes.
 
461
# @param **extra Additional attributes, given as keyword arguments.
 
462
# @return An element instance.
 
463
# @defreturn Element
 
464
 
 
465
def SubElement(parent, tag, attrib={}, **extra):
 
466
    attrib = attrib.copy()
 
467
    attrib.update(extra)
 
468
    element = parent.makeelement(tag, attrib)
 
469
    parent.append(element)
 
470
    return element
 
471
 
 
472
##
 
473
# Comment element factory.  This factory function creates a special
 
474
# element that will be serialized as an XML comment.
 
475
# <p>
 
476
# The comment string can be either an 8-bit ASCII string or a Unicode
 
477
# string.
 
478
#
 
479
# @param text A string containing the comment string.
 
480
# @return An element instance, representing a comment.
 
481
# @defreturn Element
 
482
 
 
483
def Comment(text=None):
 
484
    element = Element(Comment)
 
485
    element.text = text
 
486
    return element
 
487
 
 
488
##
 
489
# PI element factory.  This factory function creates a special element
 
490
# that will be serialized as an XML processing instruction.
 
491
#
 
492
# @param target A string containing the PI target.
 
493
# @param text A string containing the PI contents, if any.
 
494
# @return An element instance, representing a PI.
 
495
# @defreturn Element
 
496
 
 
497
def ProcessingInstruction(target, text=None):
 
498
    element = Element(ProcessingInstruction)
 
499
    element.text = target
 
500
    if text:
 
501
        element.text = element.text + " " + text
 
502
    return element
 
503
 
 
504
PI = ProcessingInstruction
 
505
 
 
506
##
 
507
# QName wrapper.  This can be used to wrap a QName attribute value, in
 
508
# order to get proper namespace handling on output.
 
509
#
 
510
# @param text A string containing the QName value, in the form {uri}local,
 
511
#     or, if the tag argument is given, the URI part of a QName.
 
512
# @param tag Optional tag.  If given, the first argument is interpreted as
 
513
#     an URI, and this argument is interpreted as a local name.
 
514
# @return An opaque object, representing the QName.
 
515
 
 
516
class QName:
 
517
    def __init__(self, text_or_uri, tag=None):
 
518
        if tag:
 
519
            text_or_uri = "{%s}%s" % (text_or_uri, tag)
 
520
        self.text = text_or_uri
 
521
    def __str__(self):
 
522
        return self.text
 
523
    def __hash__(self):
 
524
        return hash(self.text)
 
525
    def __cmp__(self, other):
 
526
        if isinstance(other, QName):
 
527
            return cmp(self.text, other.text)
 
528
        return cmp(self.text, other)
 
529
 
 
530
##
 
531
# ElementTree wrapper class.  This class represents an entire element
 
532
# hierarchy, and adds some extra support for serialization to and from
 
533
# standard XML.
 
534
#
 
535
# @param element Optional root element.
 
536
# @keyparam file Optional file handle or name.  If given, the
 
537
#     tree is initialized with the contents of this XML file.
 
538
 
 
539
class ElementTree:
 
540
 
 
541
    def __init__(self, element=None, file=None):
 
542
        assert element is None or iselement(element)
 
543
        self._root = element # first node
 
544
        if file:
 
545
            self.parse(file)
 
546
 
 
547
    ##
 
548
    # Gets the root element for this tree.
 
549
    #
 
550
    # @return An element instance.
 
551
    # @defreturn Element
 
552
 
 
553
    def getroot(self):
 
554
        return self._root
 
555
 
 
556
    ##
 
557
    # Replaces the root element for this tree.  This discards the
 
558
    # current contents of the tree, and replaces it with the given
 
559
    # element.  Use with care.
 
560
    #
 
561
    # @param element An element instance.
 
562
 
 
563
    def _setroot(self, element):
 
564
        assert iselement(element)
 
565
        self._root = element
 
566
 
 
567
    ##
 
568
    # Loads an external XML document into this element tree.
 
569
    #
 
570
    # @param source A file name or file object.
 
571
    # @param parser An optional parser instance.  If not given, the
 
572
    #     standard {@link XMLTreeBuilder} parser is used.
 
573
    # @return The document root element.
 
574
    # @defreturn Element
 
575
 
 
576
    def parse(self, source, parser=None):
 
577
        if getattr(source, "read", None) is None:
 
578
            source = open(source, "rb")
 
579
        if not parser:
 
580
            parser = XMLTreeBuilder()
 
581
        while 1:
 
582
            data = source.read(32768)
 
583
            if not data:
 
584
                break
 
585
            parser.feed(data)
 
586
        self._root = parser.close()
 
587
        return self._root
 
588
 
 
589
    ##
 
590
    # Creates a tree iterator for the root element.  The iterator loops
 
591
    # over all elements in this tree, in document order.
 
592
    #
 
593
    # @param tag What tags to look for (default is to return all elements)
 
594
    # @return An iterator.
 
595
    # @defreturn iterator
 
596
 
 
597
    def getiterator(self, tag=None):
 
598
        assert self._root is not None
 
599
        return self._root.getiterator(tag)
 
600
 
 
601
    ##
 
602
    # Finds the first toplevel element with given tag.
 
603
    # Same as getroot().find(path).
 
604
    #
 
605
    # @param path What element to look for.
 
606
    # @return The first matching element, or None if no element was found.
 
607
    # @defreturn Element or None
 
608
 
 
609
    def find(self, path):
 
610
        assert self._root is not None
 
611
        if path[:1] == "/":
 
612
            path = "." + path
 
613
        return self._root.find(path)
 
614
 
 
615
    ##
 
616
    # Finds the element text for the first toplevel element with given
 
617
    # tag.  Same as getroot().findtext(path).
 
618
    #
 
619
    # @param path What toplevel element to look for.
 
620
    # @param default What to return if the element was not found.
 
621
    # @return The text content of the first matching element, or the
 
622
    #     default value no element was found.  Note that if the element
 
623
    #     has is found, but has no text content, this method returns an
 
624
    #     empty string.
 
625
    # @defreturn string
 
626
 
 
627
    def findtext(self, path, default=None):
 
628
        assert self._root is not None
 
629
        if path[:1] == "/":
 
630
            path = "." + path
 
631
        return self._root.findtext(path, default)
 
632
 
 
633
    ##
 
634
    # Finds all toplevel elements with the given tag.
 
635
    # Same as getroot().findall(path).
 
636
    #
 
637
    # @param path What element to look for.
 
638
    # @return A list or iterator containing all matching elements,
 
639
    #    in document order.
 
640
    # @defreturn list of Element instances
 
641
 
 
642
    def findall(self, path):
 
643
        assert self._root is not None
 
644
        if path[:1] == "/":
 
645
            path = "." + path
 
646
        return self._root.findall(path)
 
647
 
 
648
    ##
 
649
    # Writes the element tree to a file, as XML.
 
650
    #
 
651
    # @param file A file name, or a file object opened for writing.
 
652
    # @param encoding Optional output encoding (default is US-ASCII).
 
653
 
 
654
    def write(self, file, encoding="us-ascii"):
 
655
        assert self._root is not None
 
656
        if getattr(file, "write", None) is None:
 
657
            file = open(file, "wb")
 
658
        if not encoding:
 
659
            encoding = "us-ascii"
 
660
        elif encoding != "utf-8" and encoding != "us-ascii":
 
661
            file.write("<?xml version='1.0' encoding='%s'?>\n" % encoding)
 
662
        self._write(file, self._root, encoding, {})
 
663
 
 
664
    def _write(self, file, node, encoding, namespaces):
 
665
        # write XML to file
 
666
        tag = node.tag
 
667
        if tag is Comment:
 
668
            file.write("<!-- %s -->" % _escape_cdata(node.text, encoding))
 
669
        elif tag is ProcessingInstruction:
 
670
            file.write("<?%s?>" % _escape_cdata(node.text, encoding))
 
671
        else:
 
672
            items = node.items()
 
673
            xmlns_items = [] # new namespaces in this scope
 
674
            try:
 
675
                if isinstance(tag, QName) or tag[:1] == "{":
 
676
                    tag, xmlns = fixtag(tag, namespaces)
 
677
                    if xmlns: xmlns_items.append(xmlns)
 
678
            except TypeError:
 
679
                _raise_serialization_error(tag)
 
680
            file.write("<" + _encode(tag, encoding))
 
681
            if items or xmlns_items:
 
682
                items.sort() # lexical order
 
683
                for k, v in items:
 
684
                    try:
 
685
                        if isinstance(k, QName) or k[:1] == "{":
 
686
                            k, xmlns = fixtag(k, namespaces)
 
687
                            if xmlns: xmlns_items.append(xmlns)
 
688
                    except TypeError:
 
689
                        _raise_serialization_error(k)
 
690
                    try:
 
691
                        if isinstance(v, QName):
 
692
                            v, xmlns = fixtag(v, namespaces)
 
693
                            if xmlns: xmlns_items.append(xmlns)
 
694
                    except TypeError:
 
695
                        _raise_serialization_error(v)
 
696
                    file.write(" %s=\"%s\"" % (_encode(k, encoding),
 
697
                                               _escape_attrib(v, encoding)))
 
698
                for k, v in xmlns_items:
 
699
                    file.write(" %s=\"%s\"" % (_encode(k, encoding),
 
700
                                               _escape_attrib(v, encoding)))
 
701
            if node.text or len(node):
 
702
                file.write(">")
 
703
                if node.text:
 
704
                    file.write(_escape_cdata(node.text, encoding))
 
705
                for n in node:
 
706
                    self._write(file, n, encoding, namespaces)
 
707
                file.write("</" + _encode(tag, encoding) + ">")
 
708
            else:
 
709
                file.write(" />")
 
710
            for k, v in xmlns_items:
 
711
                del namespaces[v]
 
712
        if node.tail:
 
713
            file.write(_escape_cdata(node.tail, encoding))
 
714
 
 
715
# --------------------------------------------------------------------
 
716
# helpers
 
717
 
 
718
##
 
719
# Checks if an object appears to be a valid element object.
 
720
#
 
721
# @param An element instance.
 
722
# @return A true value if this is an element object.
 
723
# @defreturn flag
 
724
 
 
725
def iselement(element):
 
726
    # FIXME: not sure about this; might be a better idea to look
 
727
    # for tag/attrib/text attributes
 
728
    return isinstance(element, _ElementInterface) or (getattr(element, "tag", None) is not None)
 
729
 
 
730
##
 
731
# Writes an element tree or element structure to sys.stdout.  This
 
732
# function should be used for debugging only.
 
733
# <p>
 
734
# The exact output format is implementation dependent.  In this
 
735
# version, it's written as an ordinary XML file.
 
736
#
 
737
# @param elem An element tree or an individual element.
 
738
 
 
739
def dump(elem):
 
740
    # debugging
 
741
    if not isinstance(elem, ElementTree):
 
742
        elem = ElementTree(elem)
 
743
    elem.write(sys.stdout)
 
744
    tail = elem.getroot().tail
 
745
    if not tail or tail[-1] != "\n":
 
746
        sys.stdout.write("\n")
 
747
 
 
748
def _encode(s, encoding):
 
749
    try:
 
750
        return s.encode(encoding)
 
751
    except AttributeError:
 
752
        return s # 1.5.2: assume the string uses the right encoding
 
753
 
 
754
if sys.version[:3] == "1.5":
 
755
    _escape = re.compile(r"[&<>\"\x80-\xff]+") # 1.5.2
 
756
else:
 
757
    _escape = re.compile(eval(r'u"[&<>\"\u0080-\uffff]+"'))
 
758
 
 
759
_escape_map = {
 
760
    "&": "&amp;",
 
761
    "<": "&lt;",
 
762
    ">": "&gt;",
 
763
    '"': "&quot;",
 
764
}
 
765
 
 
766
_namespace_map = {
 
767
    # "well-known" namespace prefixes
 
768
    "http://www.w3.org/XML/1998/namespace": "xml",
 
769
    "http://www.w3.org/1999/xhtml": "html",
 
770
    "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf",
 
771
    "http://schemas.xmlsoap.org/wsdl/": "wsdl",
 
772
}
 
773
 
 
774
def _raise_serialization_error(text):
 
775
    raise TypeError(
 
776
        "cannot serialize %r (type %s)" % (text, type(text).__name__)
 
777
        )
 
778
 
 
779
def _encode_entity(text, pattern=_escape):
 
780
    # map reserved and non-ascii characters to numerical entities
 
781
    def escape_entities(m, map=_escape_map):
 
782
        out = []
 
783
        append = out.append
 
784
        for char in m.group():
 
785
            text = map.get(char)
 
786
            if text is None:
 
787
                text = "&#%d;" % ord(char)
 
788
            append(text)
 
789
        return string.join(out, "")
 
790
    try:
 
791
        return _encode(pattern.sub(escape_entities, text), "ascii")
 
792
    except TypeError:
 
793
        _raise_serialization_error(text)
 
794
 
 
795
#
 
796
# the following functions assume an ascii-compatible encoding
 
797
# (or "utf-16")
 
798
 
 
799
def _escape_cdata(text, encoding=None, replace=string.replace):
 
800
    # escape character data
 
801
    try:
 
802
        if encoding:
 
803
            try:
 
804
                text = _encode(text, encoding)
 
805
            except UnicodeError:
 
806
                return _encode_entity(text)
 
807
        text = replace(text, "&", "&amp;")
 
808
        text = replace(text, "<", "&lt;")
 
809
        text = replace(text, ">", "&gt;")
 
810
        return text
 
811
    except (TypeError, AttributeError):
 
812
        _raise_serialization_error(text)
 
813
 
 
814
def _escape_attrib(text, encoding=None, replace=string.replace):
 
815
    # escape attribute value
 
816
    try:
 
817
        if encoding:
 
818
            try:
 
819
                text = _encode(text, encoding)
 
820
            except UnicodeError:
 
821
                return _encode_entity(text)
 
822
        text = replace(text, "&", "&amp;")
 
823
        text = replace(text, "'", "&apos;") # FIXME: overkill
 
824
        text = replace(text, "\"", "&quot;")
 
825
        text = replace(text, "<", "&lt;")
 
826
        text = replace(text, ">", "&gt;")
 
827
        return text
 
828
    except (TypeError, AttributeError):
 
829
        _raise_serialization_error(text)
 
830
 
 
831
def fixtag(tag, namespaces):
 
832
    # given a decorated tag (of the form {uri}tag), return prefixed
 
833
    # tag and namespace declaration, if any
 
834
    if isinstance(tag, QName):
 
835
        tag = tag.text
 
836
    namespace_uri, tag = string.split(tag[1:], "}", 1)
 
837
    prefix = namespaces.get(namespace_uri)
 
838
    if prefix is None:
 
839
        prefix = _namespace_map.get(namespace_uri)
 
840
        if prefix is None:
 
841
            prefix = "ns%d" % len(namespaces)
 
842
        namespaces[namespace_uri] = prefix
 
843
        if prefix == "xml":
 
844
            xmlns = None
 
845
        else:
 
846
            xmlns = ("xmlns:%s" % prefix, namespace_uri)
 
847
    else:
 
848
        xmlns = None
 
849
    return "%s:%s" % (prefix, tag), xmlns
 
850
 
 
851
##
 
852
# Parses an XML document into an element tree.
 
853
#
 
854
# @param source A filename or file object containing XML data.
 
855
# @param parser An optional parser instance.  If not given, the
 
856
#     standard {@link XMLTreeBuilder} parser is used.
 
857
# @return An ElementTree instance
 
858
 
 
859
def parse(source, parser=None):
 
860
    tree = ElementTree()
 
861
    tree.parse(source, parser)
 
862
    return tree
 
863
 
 
864
##
 
865
# Parses an XML document into an element tree incrementally, and reports
 
866
# what's going on to the user.
 
867
#
 
868
# @param source A filename or file object containing XML data.
 
869
# @param events A list of events to report back.  If omitted, only "end"
 
870
#     events are reported.
 
871
# @return A (event, elem) iterator.
 
872
 
 
873
class iterparse:
 
874
 
 
875
    def __init__(self, source, events=None):
 
876
        if getattr(source, "read", None) is None:
 
877
            source = open(source, "rb")
 
878
        self._file = source
 
879
        self._events = []
 
880
        self._index = 0
 
881
        self.root = self._root = None
 
882
        self._parser = XMLTreeBuilder()
 
883
        # wire up the parser for event reporting
 
884
        parser = self._parser._parser
 
885
        append = self._events.append
 
886
        if events is None:
 
887
            events = ["end"]
 
888
        for event in events:
 
889
            if event == "start":
 
890
                try:
 
891
                    parser.ordered_attributes = 1
 
892
                    parser.specified_attributes = 1
 
893
                    def handler(tag, attrib_in, event=event, append=append,
 
894
                                start=self._parser._start_list):
 
895
                        append((event, start(tag, attrib_in)))
 
896
                    parser.StartElementHandler = handler
 
897
                except AttributeError:
 
898
                    def handler(tag, attrib_in, event=event, append=append,
 
899
                                start=self._parser._start):
 
900
                        append((event, start(tag, attrib_in)))
 
901
                    parser.StartElementHandler = handler
 
902
            elif event == "end":
 
903
                def handler(tag, event=event, append=append,
 
904
                            end=self._parser._end):
 
905
                    append((event, end(tag)))
 
906
                parser.EndElementHandler = handler
 
907
            elif event == "start-ns":
 
908
                def handler(prefix, uri, event=event, append=append):
 
909
                    try:
 
910
                        uri = _encode(uri, "ascii")
 
911
                    except UnicodeError:
 
912
                        pass
 
913
                    append((event, (prefix or "", uri)))
 
914
                parser.StartNamespaceDeclHandler = handler
 
915
            elif event == "end-ns":
 
916
                def handler(prefix, event=event, append=append):
 
917
                    append((event, None))
 
918
                parser.EndNamespaceDeclHandler = handler
 
919
 
 
920
    def next(self):
 
921
        while 1:
 
922
            try:
 
923
                item = self._events[self._index]
 
924
            except IndexError:
 
925
                if self._parser is None:
 
926
                    self.root = self._root
 
927
                    try:
 
928
                        raise StopIteration
 
929
                    except NameError:
 
930
                        raise IndexError
 
931
                # load event buffer
 
932
                del self._events[:]
 
933
                self._index = 0
 
934
                data = self._file.read(16384)
 
935
                if data:
 
936
                    self._parser.feed(data)
 
937
                else:
 
938
                    self._root = self._parser.close()
 
939
                    self._parser = None
 
940
            else:
 
941
                self._index = self._index + 1
 
942
                return item
 
943
 
 
944
    try:
 
945
        iter
 
946
        def __iter__(self):
 
947
            return self
 
948
    except NameError:
 
949
        def __getitem__(self, index):
 
950
            return self.next()
 
951
 
 
952
##
 
953
# Parses an XML document from a string constant.  This function can
 
954
# be used to embed "XML literals" in Python code.
 
955
#
 
956
# @param source A string containing XML data.
 
957
# @return An Element instance.
 
958
# @defreturn Element
 
959
 
 
960
def XML(text):
 
961
    parser = XMLTreeBuilder()
 
962
    parser.feed(text)
 
963
    return parser.close()
 
964
 
 
965
##
 
966
# Parses an XML document from a string constant, and also returns
 
967
# a dictionary which maps from element id:s to elements.
 
968
#
 
969
# @param source A string containing XML data.
 
970
# @return A tuple containing an Element instance and a dictionary.
 
971
# @defreturn (Element, dictionary)
 
972
 
 
973
def XMLID(text):
 
974
    parser = XMLTreeBuilder()
 
975
    parser.feed(text)
 
976
    tree = parser.close()
 
977
    ids = {}
 
978
    for elem in tree.getiterator():
 
979
        id = elem.get("id")
 
980
        if id:
 
981
            ids[id] = elem
 
982
    return tree, ids
 
983
 
 
984
##
 
985
# Parses an XML document from a string constant.  Same as {@link #XML}.
 
986
#
 
987
# @def fromstring(text)
 
988
# @param source A string containing XML data.
 
989
# @return An Element instance.
 
990
# @defreturn Element
 
991
 
 
992
fromstring = XML
 
993
 
 
994
##
 
995
# Generates a string representation of an XML element, including all
 
996
# subelements.
 
997
#
 
998
# @param element An Element instance.
 
999
# @return An encoded string containing the XML data.
 
1000
# @defreturn string
 
1001
 
 
1002
def tostring(element, encoding=None):
 
1003
    class dummy:
 
1004
        pass
 
1005
    data = []
 
1006
    file = dummy()
 
1007
    file.write = data.append
 
1008
    ElementTree(element).write(file, encoding)
 
1009
    return string.join(data, "")
 
1010
 
 
1011
##
 
1012
# Generic element structure builder.  This builder converts a sequence
 
1013
# of {@link #TreeBuilder.start}, {@link #TreeBuilder.data}, and {@link
 
1014
# #TreeBuilder.end} method calls to a well-formed element structure.
 
1015
# <p>
 
1016
# You can use this class to build an element structure using a custom XML
 
1017
# parser, or a parser for some other XML-like format.
 
1018
#
 
1019
# @param element_factory Optional element factory.  This factory
 
1020
#    is called to create new Element instances, as necessary.
 
1021
 
 
1022
class TreeBuilder:
 
1023
 
 
1024
    def __init__(self, element_factory=None):
 
1025
        self._data = [] # data collector
 
1026
        self._elem = [] # element stack
 
1027
        self._last = None # last element
 
1028
        self._tail = None # true if we're after an end tag
 
1029
        if element_factory is None:
 
1030
            element_factory = _ElementInterface
 
1031
        self._factory = element_factory
 
1032
 
 
1033
    ##
 
1034
    # Flushes the parser buffers, and returns the toplevel documen
 
1035
    # element.
 
1036
    #
 
1037
    # @return An Element instance.
 
1038
    # @defreturn Element
 
1039
 
 
1040
    def close(self):
 
1041
        assert len(self._elem) == 0, "missing end tags"
 
1042
        assert self._last is not None, "missing toplevel element"
 
1043
        return self._last
 
1044
 
 
1045
    def _flush(self):
 
1046
        if self._data:
 
1047
            if self._last is not None:
 
1048
                text = string.join(self._data, "")
 
1049
                if self._tail:
 
1050
                    assert self._last.tail is None, "internal error (tail)"
 
1051
                    self._last.tail = text
 
1052
                else:
 
1053
                    assert self._last.text is None, "internal error (text)"
 
1054
                    self._last.text = text
 
1055
            self._data = []
 
1056
 
 
1057
    ##
 
1058
    # Adds text to the current element.
 
1059
    #
 
1060
    # @param data A string.  This should be either an 8-bit string
 
1061
    #    containing ASCII text, or a Unicode string.
 
1062
 
 
1063
    def data(self, data):
 
1064
        self._data.append(data)
 
1065
 
 
1066
    ##
 
1067
    # Opens a new element.
 
1068
    #
 
1069
    # @param tag The element name.
 
1070
    # @param attrib A dictionary containing element attributes.
 
1071
    # @return The opened element.
 
1072
    # @defreturn Element
 
1073
 
 
1074
    def start(self, tag, attrs):
 
1075
        self._flush()
 
1076
        self._last = elem = self._factory(tag, attrs)
 
1077
        if self._elem:
 
1078
            self._elem[-1].append(elem)
 
1079
        self._elem.append(elem)
 
1080
        self._tail = 0
 
1081
        return elem
 
1082
 
 
1083
    ##
 
1084
    # Closes the current element.
 
1085
    #
 
1086
    # @param tag The element name.
 
1087
    # @return The closed element.
 
1088
    # @defreturn Element
 
1089
 
 
1090
    def end(self, tag):
 
1091
        self._flush()
 
1092
        self._last = self._elem.pop()
 
1093
        assert self._last.tag == tag,\
 
1094
               "end tag mismatch (expected %s, got %s)" % (
 
1095
                   self._last.tag, tag)
 
1096
        self._tail = 1
 
1097
        return self._last
 
1098
 
 
1099
##
 
1100
# Element structure builder for XML source data, based on the
 
1101
# <b>expat</b> parser.
 
1102
#
 
1103
# @keyparam target Target object.  If omitted, the builder uses an
 
1104
#     instance of the standard {@link #TreeBuilder} class.
 
1105
# @keyparam html Predefine HTML entities.  This flag is not supported
 
1106
#     by the current implementation.
 
1107
# @see #ElementTree
 
1108
# @see #TreeBuilder
 
1109
 
 
1110
class XMLTreeBuilder:
 
1111
 
 
1112
    def __init__(self, html=0, target=None):
 
1113
        try:
 
1114
            from xml.parsers import expat
 
1115
        except ImportError:
 
1116
            raise ImportError(
 
1117
                "No module named expat; use SimpleXMLTreeBuilder instead"
 
1118
                )
 
1119
        self._parser = parser = expat.ParserCreate(None, "}")
 
1120
        if target is None:
 
1121
            target = TreeBuilder()
 
1122
        self._target = target
 
1123
        self._names = {} # name memo cache
 
1124
        # callbacks
 
1125
        parser.DefaultHandlerExpand = self._default
 
1126
        parser.StartElementHandler = self._start
 
1127
        parser.EndElementHandler = self._end
 
1128
        parser.CharacterDataHandler = self._data
 
1129
        # let expat do the buffering, if supported
 
1130
        try:
 
1131
            self._parser.buffer_text = 1
 
1132
        except AttributeError:
 
1133
            pass
 
1134
        # use new-style attribute handling, if supported
 
1135
        try:
 
1136
            self._parser.ordered_attributes = 1
 
1137
            self._parser.specified_attributes = 1
 
1138
            parser.StartElementHandler = self._start_list
 
1139
        except AttributeError:
 
1140
            pass
 
1141
        encoding = None
 
1142
        if not parser.returns_unicode:
 
1143
            encoding = "utf-8"
 
1144
        # target.xml(encoding, None)
 
1145
        self._doctype = None
 
1146
        self.entity = {}
 
1147
 
 
1148
    def _fixtext(self, text):
 
1149
        # convert text string to ascii, if possible
 
1150
        try:
 
1151
            return _encode(text, "ascii")
 
1152
        except UnicodeError:
 
1153
            return text
 
1154
 
 
1155
    def _fixname(self, key):
 
1156
        # expand qname, and convert name string to ascii, if possible
 
1157
        try:
 
1158
            name = self._names[key]
 
1159
        except KeyError:
 
1160
            name = key
 
1161
            if "}" in name:
 
1162
                name = "{" + name
 
1163
            self._names[key] = name = self._fixtext(name)
 
1164
        return name
 
1165
 
 
1166
    def _start(self, tag, attrib_in):
 
1167
        fixname = self._fixname
 
1168
        tag = fixname(tag)
 
1169
        attrib = {}
 
1170
        for key, value in attrib_in.items():
 
1171
            attrib[fixname(key)] = self._fixtext(value)
 
1172
        return self._target.start(tag, attrib)
 
1173
 
 
1174
    def _start_list(self, tag, attrib_in):
 
1175
        fixname = self._fixname
 
1176
        tag = fixname(tag)
 
1177
        attrib = {}
 
1178
        if attrib_in:
 
1179
            for i in range(0, len(attrib_in), 2):
 
1180
                attrib[fixname(attrib_in[i])] = self._fixtext(attrib_in[i+1])
 
1181
        return self._target.start(tag, attrib)
 
1182
 
 
1183
    def _data(self, text):
 
1184
        return self._target.data(self._fixtext(text))
 
1185
 
 
1186
    def _end(self, tag):
 
1187
        return self._target.end(self._fixname(tag))
 
1188
 
 
1189
    def _default(self, text):
 
1190
        prefix = text[:1]
 
1191
        if prefix == "&":
 
1192
            # deal with undefined entities
 
1193
            try:
 
1194
                self._target.data(self.entity[text[1:-1]])
 
1195
            except KeyError:
 
1196
                from xml.parsers import expat
 
1197
                raise expat.error(
 
1198
                    "undefined entity %s: line %d, column %d" %
 
1199
                    (text, self._parser.ErrorLineNumber,
 
1200
                    self._parser.ErrorColumnNumber)
 
1201
                    )
 
1202
        elif prefix == "<" and text[:9] == "<!DOCTYPE":
 
1203
            self._doctype = [] # inside a doctype declaration
 
1204
        elif self._doctype is not None:
 
1205
            # parse doctype contents
 
1206
            if prefix == ">":
 
1207
                self._doctype = None
 
1208
                return
 
1209
            text = string.strip(text)
 
1210
            if not text:
 
1211
                return
 
1212
            self._doctype.append(text)
 
1213
            n = len(self._doctype)
 
1214
            if n > 2:
 
1215
                type = self._doctype[1]
 
1216
                if type == "PUBLIC" and n == 4:
 
1217
                    name, type, pubid, system = self._doctype
 
1218
                elif type == "SYSTEM" and n == 3:
 
1219
                    name, type, system = self._doctype
 
1220
                    pubid = None
 
1221
                else:
 
1222
                    return
 
1223
                if pubid:
 
1224
                    pubid = pubid[1:-1]
 
1225
                self.doctype(name, pubid, system[1:-1])
 
1226
                self._doctype = None
 
1227
 
 
1228
    ##
 
1229
    # Handles a doctype declaration.
 
1230
    #
 
1231
    # @param name Doctype name.
 
1232
    # @param pubid Public identifier.
 
1233
    # @param system System identifier.
 
1234
 
 
1235
    def doctype(self, name, pubid, system):
 
1236
        pass
 
1237
 
 
1238
    ##
 
1239
    # Feeds data to the parser.
 
1240
    #
 
1241
    # @param data Encoded data.
 
1242
 
 
1243
    def feed(self, data):
 
1244
        self._parser.Parse(data, 0)
 
1245
 
 
1246
    ##
 
1247
    # Finishes feeding data to the parser.
 
1248
    #
 
1249
    # @return An element structure.
 
1250
    # @defreturn Element
 
1251
 
 
1252
    def close(self):
 
1253
        self._parser.Parse("", 1) # end of data
 
1254
        tree = self._target.close()
 
1255
        del self._target, self._parser # get rid of circular references
 
1256
        return tree