Salome HOME
37ed4674ea05898ebb585af555396b25c8e0b8d9
[tools/sat.git] / src / ElementTree.py
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, platform
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         if ElementPath.find(self, path) == None:
328             return ElementPath.find(self, path.encode())
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         res = self.attrib.get(key, default)
376         if not res:
377             res = self.attrib.get(key.encode(), default)
378         if isinstance(res, bytes):
379             return res.decode()
380         else:
381             return res
382
383     ##
384     # Sets an element attribute.
385     #
386     # @param key What attribute to set.
387     # @param value The attribute value.
388
389     def set(self, key, value):
390         self.attrib[key] = value
391
392     ##
393     # Gets a list of attribute names.  The names are returned in an
394     # arbitrary order (just like for an ordinary Python dictionary).
395     #
396     # @return A list of element attribute names.
397     # @defreturn list of strings
398
399     def keys(self):
400         res = []
401         for key in self.attrib.keys():
402             if isinstance(key, bytes):
403                 res.append(key.decode())
404             else:
405                 res.append(key)
406         return res
407                 
408     ##
409     # Gets element attributes, as a sequence.  The attributes are
410     # returned in an arbitrary order.
411     #
412     # @return A list of (name, value) tuples for all attributes.
413     # @defreturn list of (string, string) tuples
414
415     def items(self):
416         return self.attrib.items()
417
418     ##
419     # Creates a tree iterator.  The iterator loops over this element
420     # and all subelements, in document order, and returns all elements
421     # with a matching tag.
422     # <p>
423     # If the tree structure is modified during iteration, the result
424     # is undefined.
425     #
426     # @param tag What tags to look for (default is to return all elements).
427     # @return A list or iterator containing all the matching elements.
428     # @defreturn list or iterator
429
430     def getiterator(self, tag=None):
431         nodes = []
432         if tag == "*":
433             tag = None
434         if tag is None or self.tag == tag:
435             nodes.append(self)
436         for node in self._children:
437             nodes.extend(node.getiterator(tag))
438         return nodes
439
440 # compatibility
441 _Element = _ElementInterface
442
443 ##
444 # Element factory.  This function returns an object implementing the
445 # standard Element interface.  The exact class or type of that object
446 # is implementation dependent, but it will always be compatible with
447 # the {@link #_ElementInterface} class in this module.
448 # <p>
449 # The element name, attribute names, and attribute values can be
450 # either 8-bit ASCII strings or Unicode strings.
451 #
452 # @param tag The element name.
453 # @param attrib An optional dictionary, containing element attributes.
454 # @param **extra Additional attributes, given as keyword arguments.
455 # @return An element instance.
456 # @defreturn Element
457
458 def Element(tag, attrib={}, **extra):
459     attrib = attrib.copy()
460     attrib.update(extra)
461     return _ElementInterface(tag, attrib)
462
463 ##
464 # Subelement factory.  This function creates an element instance, and
465 # appends it to an existing element.
466 # <p>
467 # The element name, attribute names, and attribute values can be
468 # either 8-bit ASCII strings or Unicode strings.
469 #
470 # @param parent The parent element.
471 # @param tag The subelement name.
472 # @param attrib An optional dictionary, containing element attributes.
473 # @param **extra Additional attributes, given as keyword arguments.
474 # @return An element instance.
475 # @defreturn Element
476
477 def SubElement(parent, tag, attrib={}, **extra):
478     attrib = attrib.copy()
479     attrib.update(extra)
480     element = parent.makeelement(tag, attrib)
481     parent.append(element)
482     return element
483
484 ##
485 # Comment element factory.  This factory function creates a special
486 # element that will be serialized as an XML comment.
487 # <p>
488 # The comment string can be either an 8-bit ASCII string or a Unicode
489 # string.
490 #
491 # @param text A string containing the comment string.
492 # @return An element instance, representing a comment.
493 # @defreturn Element
494
495 def Comment(text=None):
496     element = Element(Comment)
497     element.text = text
498     return element
499
500 ##
501 # PI element factory.  This factory function creates a special element
502 # that will be serialized as an XML processing instruction.
503 #
504 # @param target A string containing the PI target.
505 # @param text A string containing the PI contents, if any.
506 # @return An element instance, representing a PI.
507 # @defreturn Element
508
509 def ProcessingInstruction(target, text=None):
510     element = Element(ProcessingInstruction)
511     element.text = target
512     if text:
513         element.text = element.text + " " + text
514     return element
515
516 PI = ProcessingInstruction
517
518 ##
519 # QName wrapper.  This can be used to wrap a QName attribute value, in
520 # order to get proper namespace handling on output.
521 #
522 # @param text A string containing the QName value, in the form {uri}local,
523 #     or, if the tag argument is given, the URI part of a QName.
524 # @param tag Optional tag.  If given, the first argument is interpreted as
525 #     an URI, and this argument is interpreted as a local name.
526 # @return An opaque object, representing the QName.
527
528 class QName:
529     def __init__(self, text_or_uri, tag=None):
530         if tag:
531             text_or_uri = "{%s}%s" % (text_or_uri, tag)
532         self.text = text_or_uri
533     def __str__(self):
534         return self.text
535     def __hash__(self):
536         return hash(self.text)
537     def __cmp__(self, other):
538         if isinstance(other, QName):
539             return cmp(self.text, other.text)
540         return cmp(self.text, other)
541
542 ##
543 # ElementTree wrapper class.  This class represents an entire element
544 # hierarchy, and adds some extra support for serialization to and from
545 # standard XML.
546 #
547 # @param element Optional root element.
548 # @keyparam file Optional file handle or name.  If given, the
549 #     tree is initialized with the contents of this XML file.
550
551 class ElementTree:
552
553     def __init__(self, element=None, file=None):
554         assert element is None or iselement(element)
555         self._root = element # first node
556         if file:
557             self.parse(file)
558
559     ##
560     # Gets the root element for this tree.
561     #
562     # @return An element instance.
563     # @defreturn Element
564
565     def getroot(self):
566         return self._root
567
568     ##
569     # Replaces the root element for this tree.  This discards the
570     # current contents of the tree, and replaces it with the given
571     # element.  Use with care.
572     #
573     # @param element An element instance.
574
575     def _setroot(self, element):
576         assert iselement(element)
577         self._root = element
578
579     ##
580     # Loads an external XML document into this element tree.
581     #
582     # @param source A file name or file object.
583     # @param parser An optional parser instance.  If not given, the
584     #     standard {@link XMLTreeBuilder} parser is used.
585     # @return The document root element.
586     # @defreturn Element
587
588     def parse(self, source, parser=None):
589         if not hasattr(source, "read"):
590             source = open(source, "rb")
591         if not parser:
592             parser = XMLTreeBuilder()
593         while 1:
594             data = source.read(32768)
595             if not data:
596                 break
597             parser.feed(data)
598         self._root = parser.close()
599         return self._root
600
601     ##
602     # Creates a tree iterator for the root element.  The iterator loops
603     # over all elements in this tree, in document order.
604     #
605     # @param tag What tags to look for (default is to return all elements)
606     # @return An iterator.
607     # @defreturn iterator
608
609     def getiterator(self, tag=None):
610         assert self._root is not None
611         return self._root.getiterator(tag)
612
613     ##
614     # Finds the first toplevel element with given tag.
615     # Same as getroot().find(path).
616     #
617     # @param path What element to look for.
618     # @return The first matching element, or None if no element was found.
619     # @defreturn Element or None
620
621     def find(self, path):
622         assert self._root is not None
623         if path[:1] == "/":
624             path = "." + path
625         return self._root.find(path)
626
627     ##
628     # Finds the element text for the first toplevel element with given
629     # tag.  Same as getroot().findtext(path).
630     #
631     # @param path What toplevel element to look for.
632     # @param default What to return if the element was not found.
633     # @return The text content of the first matching element, or the
634     #     default value no element was found.  Note that if the element
635     #     has is found, but has no text content, this method returns an
636     #     empty string.
637     # @defreturn string
638
639     def findtext(self, path, default=None):
640         assert self._root is not None
641         if path[:1] == "/":
642             path = "." + path
643         return self._root.findtext(path, default)
644
645     ##
646     # Finds all toplevel elements with the given tag.
647     # Same as getroot().findall(path).
648     #
649     # @param path What element to look for.
650     # @return A list or iterator containing all matching elements,
651     #    in document order.
652     # @defreturn list of Element instances
653
654     def findall(self, path):
655         assert self._root is not None
656         if path[:1] == "/":
657             path = "." + path
658         return self._root.findall(path)
659
660     ##
661     # Writes the element tree to a file, as XML.
662     #
663     # @param file A file name, or a file object opened for writing.
664     # @param encoding Optional output encoding (default is US-ASCII).
665
666     def write(self, file, encoding="us-ascii"):
667         assert self._root is not None
668         if not hasattr(file, "write"):
669             file = open(file, "wb")
670         if not encoding:
671             encoding = "us-ascii"
672         elif encoding != "utf-8" and encoding != "us-ascii":
673             file.write("<?xml version='1.0' encoding='%s'?>\n" % encoding)
674         self._write(file, self._root, encoding, {})
675
676     def _write(self, file, node, encoding, namespaces, margin=0):
677         # write XML to file
678         tag = node.tag
679         if tag is Comment:
680             file.write("<!-- %s -->\n" % _escape_cdata(node.text, encoding))
681         elif tag is ProcessingInstruction:
682             file.write("<?%s?>\n" % _escape_cdata(node.text, encoding))
683         else:
684             items = node.items()
685             xmlns_items = [] # new namespaces in this scope
686             try:
687                 if isinstance(tag, QName) or tag[:1] == "{":
688                     tag, xmlns = fixtag(tag, namespaces)
689                     if xmlns: xmlns_items.append(xmlns)
690             except TypeError:
691                 _raise_serialization_error(tag)
692             file.write(' ' * margin)
693             file.write(_encode("<", encoding) + _encode(tag, encoding))
694             if items or xmlns_items:
695                 items = sorted(items) # lexical order
696                 for k, v in items:
697                     try:
698                         if isinstance(k, QName) or k[:1] == "{":
699                             k, xmlns = fixtag(k, namespaces)
700                             if xmlns: xmlns_items.append(xmlns)
701                     except TypeError:
702                         _raise_serialization_error(k)
703                     try:
704                         if isinstance(v, QName):
705                             v, xmlns = fixtag(v, namespaces)
706                             if xmlns: xmlns_items.append(xmlns)
707                     except TypeError:
708                         _raise_serialization_error(v)
709                     file.write(" %s=\"%s\"" % (k,v))
710                 for k, v in xmlns_items:
711                     file.write(" %s=\"%s\"" % (k,v))
712             if node.text or len(node):
713                 file.write(">")
714                 if node.text:
715                     file.write(_escape_cdata(node.text, encoding))
716                 if len(node) > 0: file.write("\n")
717                 for n in node:
718                     self._write(file, n, encoding, namespaces, margin + 2)
719                 if len(node) > 0: file.write(' ' * margin)
720                 file.write(_encode("</", encoding) + _encode(tag, encoding) + _encode(">\n", encoding))
721             else:
722                 file.write("/>\n")
723             for k, v in xmlns_items:
724                 del namespaces[v]
725         if node.tail:
726             file.write(_escape_cdata(node.tail, encoding))
727
728 # --------------------------------------------------------------------
729 # helpers
730
731 ##
732 # Checks if an object appears to be a valid element object.
733 #
734 # @param An element instance.
735 # @return A true value if this is an element object.
736 # @defreturn flag
737
738 def iselement(element):
739     # FIXME: not sure about this; might be a better idea to look
740     # for tag/attrib/text attributes
741     return isinstance(element, _ElementInterface) or hasattr(element, "tag")
742
743 ##
744 # Writes an element tree or element structure to sys.stdout.  This
745 # function should be used for debugging only.
746 # <p>
747 # The exact output format is implementation dependent.  In this
748 # version, it's written as an ordinary XML file.
749 #
750 # @param elem An element tree or an individual element.
751
752 def dump(elem):
753     # debugging
754     if not isinstance(elem, ElementTree):
755         elem = ElementTree(elem)
756     elem.write(sys.stdout)
757     tail = elem.getroot().tail
758     if not tail or tail[-1] != "\n":
759         sys.stdout.write("\n")
760
761 def _encode(s, encoding):
762     try:
763         return s.encode(encoding)
764     except AttributeError:
765         return s # 1.5.2: assume the string uses the right encoding
766
767 if sys.version[:3] == "1.5":
768     _escape = re.compile(r"[&<>\"\x80-\xff]+") # 1.5.2
769 else:
770     _escape = re.compile(eval(r'u"[&<>\"\u0080-\uffff]+"'))
771
772 _escape_map = {
773     "&": "&amp;",
774     "<": "&lt;",
775     ">": "&gt;",
776     '"': "&quot;",
777 }
778
779 _namespace_map = {
780     # "well-known" namespace prefixes
781     "http://www.w3.org/XML/1998/namespace": "xml",
782     "http://www.w3.org/1999/xhtml": "html",
783     "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf",
784     "http://schemas.xmlsoap.org/wsdl/": "wsdl",
785 }
786
787 def _raise_serialization_error(text):
788     raise TypeError(
789         "cannot serialize %r (type %s)" % (text, type(text).__name__)
790         )
791
792 def _encode_entity(text, pattern=_escape):
793     # map reserved and non-ascii characters to numerical entities
794     def escape_entities(m, map=_escape_map):
795         out = []
796         append = out.append
797         for char in m.group():
798             text = map.get(char)
799             if text is None:
800                 text = "&#%d;" % ord(char)
801             append(text)
802         return string.join(out, "")
803     try:
804         return _encode(pattern.sub(escape_entities, text), "ascii")
805     except TypeError:
806         _raise_serialization_error(text)
807
808 #
809 # the following functions assume an ascii-compatible encoding
810 # (or "utf-16")
811
812 def _escape_cdata(text, encoding=None, replace=str.replace):
813     # escape character data
814     try:
815         if platform.python_version()[0] == '2': # python 2.x.y
816             if encoding:
817                 try:
818                     text = _encode(text, encoding)
819                 except UnicodeError:
820                     return _encode_entity(text)
821             
822         text = replace(text, "&", "&amp;")
823         text = replace(text, "<", "&lt;")
824         text = replace(text, ">", "&gt;")
825         text = replace(text, "####newLine####", "<br \>")
826         if encoding:
827             try:
828                 text = _encode(text, encoding)
829             except UnicodeError:
830                 return _encode_entity(text)
831         return text
832     except (TypeError, AttributeError):
833         _raise_serialization_error(text)
834
835 def _escape_attrib(text, encoding=None, replace=str.replace):
836     # escape attribute value
837     try:
838         text = replace(text, "&", "&amp;")
839         text = replace(text, "'", "&apos;") # FIXME: overkill
840         text = replace(text, "\"", "&quot;")
841         text = replace(text, "<", "&lt;")
842         text = replace(text, ">", "&gt;")
843         if encoding:
844             try:
845                 text = _encode(text, encoding)
846             except UnicodeError:
847                 return _encode_entity(text)
848         return text
849     except (TypeError, AttributeError):
850         _raise_serialization_error(text)
851
852 def fixtag(tag, namespaces):
853     # given a decorated tag (of the form {uri}tag), return prefixed
854     # tag and namespace declaration, if any
855     if isinstance(tag, QName):
856         tag = tag.text
857     namespace_uri, tag = string.split(tag[1:], "}", 1)
858     prefix = namespaces.get(namespace_uri)
859     if prefix is None:
860         prefix = _namespace_map.get(namespace_uri)
861         if prefix is None:
862             prefix = "ns%d" % len(namespaces)
863         namespaces[namespace_uri] = prefix
864         if prefix == "xml":
865             xmlns = None
866         else:
867             xmlns = ("xmlns:%s" % prefix, namespace_uri)
868     else:
869         xmlns = None
870     return "%s:%s" % (prefix, tag), xmlns
871
872 ##
873 # Parses an XML document into an element tree.
874 #
875 # @param source A filename or file object containing XML data.
876 # @param parser An optional parser instance.  If not given, the
877 #     standard {@link XMLTreeBuilder} parser is used.
878 # @return An ElementTree instance
879
880 def parse(source, parser=None):
881     tree = ElementTree()
882     tree.parse(source, parser)
883     return tree
884
885 ##
886 # Parses an XML document into an element tree incrementally, and reports
887 # what's going on to the user.
888 #
889 # @param source A filename or file object containing XML data.
890 # @param events A list of events to report back.  If omitted, only "end"
891 #     events are reported.
892 # @return A (event, elem) iterator.
893
894 class iterparse:
895
896     def __init__(self, source, events=None):
897         if not hasattr(source, "read"):
898             source = open(source, "rb")
899         self._file = source
900         self._events = []
901         self._index = 0
902         self.root = self._root = None
903         self._parser = XMLTreeBuilder()
904         # wire up the parser for event reporting
905         parser = self._parser._parser
906         append = self._events.append
907         if events is None:
908             events = ["end"]
909         for event in events:
910             if event == "start":
911                 try:
912                     parser.ordered_attributes = 1
913                     parser.specified_attributes = 1
914                     def handler(tag, attrib_in, event=event, append=append,
915                                 start=self._parser._start_list):
916                         append((event, start(tag, attrib_in)))
917                     parser.StartElementHandler = handler
918                 except AttributeError:
919                     def handler(tag, attrib_in, event=event, append=append,
920                                 start=self._parser._start):
921                         append((event, start(tag, attrib_in)))
922                     parser.StartElementHandler = handler
923             elif event == "end":
924                 def handler(tag, event=event, append=append,
925                             end=self._parser._end):
926                     append((event, end(tag)))
927                 parser.EndElementHandler = handler
928             elif event == "start-ns":
929                 def handler(prefix, uri, event=event, append=append):
930                     try:
931                         uri = _encode(uri, "ascii")
932                     except UnicodeError:
933                         pass
934                     append((event, (prefix or "", uri)))
935                 parser.StartNamespaceDeclHandler = handler
936             elif event == "end-ns":
937                 def handler(prefix, event=event, append=append):
938                     append((event, None))
939                 parser.EndNamespaceDeclHandler = handler
940
941     def next(self):
942         while 1:
943             try:
944                 item = self._events[self._index]
945             except IndexError:
946                 if self._parser is None:
947                     self.root = self._root
948                     try:
949                         raise StopIteration
950                     except NameError:
951                         raise IndexError
952                 # load event buffer
953                 del self._events[:]
954                 self._index = 0
955                 data = self._file.read(16384)
956                 if data:
957                     self._parser.feed(data)
958                 else:
959                     self._root = self._parser.close()
960                     self._parser = None
961             else:
962                 self._index = self._index + 1
963                 return item
964
965     try:
966         iter
967         def __iter__(self):
968             return self
969     except NameError:
970         def __getitem__(self, index):
971             return self.next()
972
973 ##
974 # Parses an XML document from a string constant.  This function can
975 # be used to embed "XML literals" in Python code.
976 #
977 # @param source A string containing XML data.
978 # @return An Element instance.
979 # @defreturn Element
980
981 def XML(text):
982     parser = XMLTreeBuilder()
983     parser.feed(text)
984     return parser.close()
985
986 ##
987 # Parses an XML document from a string constant, and also returns
988 # a dictionary which maps from element id:s to elements.
989 #
990 # @param source A string containing XML data.
991 # @return A tuple containing an Element instance and a dictionary.
992 # @defreturn (Element, dictionary)
993
994 def XMLID(text):
995     parser = XMLTreeBuilder()
996     parser.feed(text)
997     tree = parser.close()
998     ids = {}
999     for elem in tree.getiterator():
1000         id = elem.get("id")
1001         if id:
1002             ids[id] = elem
1003     return tree, ids
1004
1005 ##
1006 # Parses an XML document from a string constant.  Same as {@link #XML}.
1007 #
1008 # @def fromstring(text)
1009 # @param source A string containing XML data.
1010 # @return An Element instance.
1011 # @defreturn Element
1012
1013 fromstring = XML
1014
1015 ##
1016 # Generates a string representation of an XML element, including all
1017 # subelements.
1018 #
1019 # @param element An Element instance.
1020 # @return An encoded string containing the XML data.
1021 # @defreturn string
1022
1023 def tostring(element, encoding=None):
1024     class dummy:
1025         pass
1026     data = []
1027     file = dummy()
1028     file.write = data.append
1029     ElementTree(element).write(file, encoding)
1030     data2 = []
1031     for item in data:
1032         if isinstance(item, bytes):
1033             item = item.decode()
1034         data2.append(item)
1035     return "".join(data2)
1036
1037 ##
1038 # Generic element structure builder.  This builder converts a sequence
1039 # of {@link #TreeBuilder.start}, {@link #TreeBuilder.data}, and {@link
1040 # #TreeBuilder.end} method calls to a well-formed element structure.
1041 # <p>
1042 # You can use this class to build an element structure using a custom XML
1043 # parser, or a parser for some other XML-like format.
1044 #
1045 # @param element_factory Optional element factory.  This factory
1046 #    is called to create new Element instances, as necessary.
1047
1048 class TreeBuilder:
1049
1050     def __init__(self, element_factory=None):
1051         self._data = [] # data collector
1052         self._elem = [] # element stack
1053         self._last = None # last element
1054         self._tail = None # true if we're after an end tag
1055         if element_factory is None:
1056             element_factory = _ElementInterface
1057         self._factory = element_factory
1058
1059     ##
1060     # Flushes the parser buffers, and returns the toplevel documen
1061     # element.
1062     #
1063     # @return An Element instance.
1064     # @defreturn Element
1065
1066     def close(self):
1067         assert len(self._elem) == 0, "missing end tags"
1068         assert self._last != None, "missing toplevel element"
1069         return self._last
1070
1071     def _flush(self):
1072         if self._data:
1073             if self._last is not None:
1074                 text = ""
1075                 for item in self._data:
1076                     try:
1077                         text += item
1078                     except:
1079                         text += item.decode()
1080                 if self._tail:
1081                     assert self._last.tail is None, "internal error (tail)"
1082                     self._last.tail = text
1083                 else:
1084                     assert self._last.text is None, "internal error (text)"
1085                     self._last.text = text
1086             self._data = []
1087
1088     ##
1089     # Adds text to the current element.
1090     #
1091     # @param data A string.  This should be either an 8-bit string
1092     #    containing ASCII text, or a Unicode string.
1093
1094     def data(self, data):
1095         self._data.append(data)
1096
1097     ##
1098     # Opens a new element.
1099     #
1100     # @param tag The element name.
1101     # @param attrib A dictionary containing element attributes.
1102     # @return The opened element.
1103     # @defreturn Element
1104
1105     def start(self, tag, attrs):
1106         self._flush()
1107         self._last = elem = self._factory(tag, attrs)
1108         if self._elem:
1109             self._elem[-1].append(elem)
1110         self._elem.append(elem)
1111         self._tail = 0
1112         return elem
1113
1114     ##
1115     # Closes the current element.
1116     #
1117     # @param tag The element name.
1118     # @return The closed element.
1119     # @defreturn Element
1120
1121     def end(self, tag):
1122         self._flush()
1123         self._last = self._elem.pop()
1124         assert self._last.tag == tag,\
1125                "end tag mismatch (expected %s, got %s)" % (
1126                    self._last.tag, tag)
1127         self._tail = 1
1128         return self._last
1129
1130 ##
1131 # Element structure builder for XML source data, based on the
1132 # <b>expat</b> parser.
1133 #
1134 # @keyparam target Target object.  If omitted, the builder uses an
1135 #     instance of the standard {@link #TreeBuilder} class.
1136 # @keyparam html Predefine HTML entities.  This flag is not supported
1137 #     by the current implementation.
1138 # @see #ElementTree
1139 # @see #TreeBuilder
1140
1141 class XMLTreeBuilder:
1142
1143     def __init__(self, html=0, target=None):
1144         try:
1145             from xml.parsers import expat
1146         except ImportError:
1147             raise ImportError(
1148                 "No module named expat; use SimpleXMLTreeBuilder instead"
1149                 )
1150         self._parser = parser = expat.ParserCreate(None, "}")
1151         if target is None:
1152             target = TreeBuilder()
1153         self._target = target
1154         self._names = {} # name memo cache
1155         # callbacks
1156         parser.DefaultHandlerExpand = self._default
1157         parser.StartElementHandler = self._start
1158         parser.EndElementHandler = self._end
1159         parser.CharacterDataHandler = self._data
1160         # let expat do the buffering, if supported
1161         try:
1162             self._parser.buffer_text = 1
1163         except AttributeError:
1164             pass
1165         # use new-style attribute handling, if supported
1166         try:
1167             self._parser.ordered_attributes = 1
1168             self._parser.specified_attributes = 1
1169             parser.StartElementHandler = self._start_list
1170         except AttributeError:
1171             pass
1172         #encoding = None
1173         #if not parser.returns_unicode:
1174         #    encoding = "utf-8"
1175         # target.xml(encoding, None)
1176         self._doctype = None
1177         self.entity = {}
1178
1179     def _fixtext(self, text):
1180         # convert text string to ascii, if possible
1181         try:
1182             return _encode(text, "ascii")
1183         except UnicodeError:
1184             return text
1185
1186     def _fixname(self, key):
1187         # expand qname, and convert name string to ascii, if possible
1188         try:
1189             name = self._names[key]
1190         except KeyError:
1191             name = key
1192             if "}" in name:
1193                 name = "{" + name
1194             self._names[key] = name = self._fixtext(name)
1195         return name
1196
1197     def _start(self, tag, attrib_in):
1198         fixname = self._fixname
1199         tag = fixname(tag)
1200         attrib = {}
1201         for key, value in attrib_in.items():
1202             attrib[fixname(key)] = self._fixtext(value)
1203         return self._target.start(tag, attrib)
1204
1205     def _start_list(self, tag, attrib_in):
1206         fixname = self._fixname
1207         tag = fixname(tag)
1208         attrib = {}
1209         if attrib_in:
1210             for i in range(0, len(attrib_in), 2):
1211                 attrib[fixname(attrib_in[i])] = self._fixtext(attrib_in[i+1])
1212         return self._target.start(tag, attrib)
1213
1214     def _data(self, text):
1215         return self._target.data(self._fixtext(text))
1216
1217     def _end(self, tag):
1218         return self._target.end(self._fixname(tag))
1219
1220     def _default(self, text):
1221         prefix = text[:1]
1222         if prefix == "&":
1223             # deal with undefined entities
1224             try:
1225                 self._target.data(self.entity[text[1:-1]])
1226             except KeyError:
1227                 from xml.parsers import expat
1228                 raise expat.error(
1229                     "undefined entity %s: line %d, column %d" %
1230                     (text, self._parser.ErrorLineNumber,
1231                     self._parser.ErrorColumnNumber)
1232                     )
1233         elif prefix == "<" and text[:9] == "<!DOCTYPE":
1234             self._doctype = [] # inside a doctype declaration
1235         elif self._doctype is not None:
1236             # parse doctype contents
1237             if prefix == ">":
1238                 self._doctype = None
1239                 return
1240             text = string.strip(text)
1241             if not text:
1242                 return
1243             self._doctype.append(text)
1244             n = len(self._doctype)
1245             if n > 2:
1246                 type = self._doctype[1]
1247                 if type == "PUBLIC" and n == 4:
1248                     name, type, pubid, system = self._doctype
1249                 elif type == "SYSTEM" and n == 3:
1250                     name, type, system = self._doctype
1251                     pubid = None
1252                 else:
1253                     return
1254                 if pubid:
1255                     pubid = pubid[1:-1]
1256                 self.doctype(name, pubid, system[1:-1])
1257                 self._doctype = None
1258
1259     ##
1260     # Handles a doctype declaration.
1261     #
1262     # @param name Doctype name.
1263     # @param pubid Public identifier.
1264     # @param system System identifier.
1265
1266     def doctype(self, name, pubid, system):
1267         pass
1268
1269     ##
1270     # Feeds data to the parser.
1271     #
1272     # @param data Encoded data.
1273
1274     def feed(self, data):
1275         self._parser.Parse(data, 0)
1276
1277     ##
1278     # Finishes feeding data to the parser.
1279     #
1280     # @return An element structure.
1281     # @defreturn Element
1282
1283     def close(self):
1284         self._parser.Parse("", 1) # end of data
1285         tree = self._target.close()
1286         del self._target, self._parser # get rid of circular references
1287         return tree