~bzr-pqm/bzr/bzr.dev

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