Salome HOME
#12754 fixed i18n accent francais
[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             # OP TEST
899             print("iterparse.__init__ source = %s" % source)
900             source = open(source, "rb")
901         self._file = source
902         self._events = []
903         self._index = 0
904         self.root = self._root = None
905         self._parser = XMLTreeBuilder()
906         # wire up the parser for event reporting
907         parser = self._parser._parser
908         append = self._events.append
909         if events is None:
910             events = ["end"]
911         for event in events:
912             if event == "start":
913                 try:
914                     parser.ordered_attributes = 1
915                     parser.specified_attributes = 1
916                     def handler(tag, attrib_in, event=event, append=append,
917                                 start=self._parser._start_list):
918                         append((event, start(tag, attrib_in)))
919                     parser.StartElementHandler = handler
920                 except AttributeError:
921                     def handler(tag, attrib_in, event=event, append=append,
922                                 start=self._parser._start):
923                         append((event, start(tag, attrib_in)))
924                     parser.StartElementHandler = handler
925             elif event == "end":
926                 def handler(tag, event=event, append=append,
927                             end=self._parser._end):
928                     append((event, end(tag)))
929                 parser.EndElementHandler = handler
930             elif event == "start-ns":
931                 def handler(prefix, uri, event=event, append=append):
932                     try:
933                         uri = _encode(uri, "ascii")
934                     except UnicodeError:
935                         pass
936                     append((event, (prefix or "", uri)))
937                 parser.StartNamespaceDeclHandler = handler
938             elif event == "end-ns":
939                 def handler(prefix, event=event, append=append):
940                     append((event, None))
941                 parser.EndNamespaceDeclHandler = handler
942
943     def next(self):
944         while 1:
945             try:
946                 item = self._events[self._index]
947             except IndexError:
948                 if self._parser is None:
949                     self.root = self._root
950                     try:
951                         raise StopIteration
952                     except NameError:
953                         raise IndexError
954                 # load event buffer
955                 del self._events[:]
956                 self._index = 0
957                 data = self._file.read(16384)
958                 if data:
959                     self._parser.feed(data)
960                 else:
961                     self._root = self._parser.close()
962                     self._parser = None
963             else:
964                 self._index = self._index + 1
965                 return item
966
967     try:
968         iter
969         def __iter__(self):
970             return self
971     except NameError:
972         def __getitem__(self, index):
973             return self.next()
974
975 ##
976 # Parses an XML document from a string constant.  This function can
977 # be used to embed "XML literals" in Python code.
978 #
979 # @param source A string containing XML data.
980 # @return An Element instance.
981 # @defreturn Element
982
983 def XML(text):
984     parser = XMLTreeBuilder()
985     parser.feed(text)
986     return parser.close()
987
988 ##
989 # Parses an XML document from a string constant, and also returns
990 # a dictionary which maps from element id:s to elements.
991 #
992 # @param source A string containing XML data.
993 # @return A tuple containing an Element instance and a dictionary.
994 # @defreturn (Element, dictionary)
995
996 def XMLID(text):
997     parser = XMLTreeBuilder()
998     parser.feed(text)
999     tree = parser.close()
1000     ids = {}
1001     for elem in tree.getiterator():
1002         id = elem.get("id")
1003         if id:
1004             ids[id] = elem
1005     return tree, ids
1006
1007 ##
1008 # Parses an XML document from a string constant.  Same as {@link #XML}.
1009 #
1010 # @def fromstring(text)
1011 # @param source A string containing XML data.
1012 # @return An Element instance.
1013 # @defreturn Element
1014
1015 fromstring = XML
1016
1017 ##
1018 # Generates a string representation of an XML element, including all
1019 # subelements.
1020 #
1021 # @param element An Element instance.
1022 # @return An encoded string containing the XML data.
1023 # @defreturn string
1024
1025 def tostring(element, encoding=None):
1026     class dummy:
1027         pass
1028     data = []
1029     file = dummy()
1030     file.write = data.append
1031     ElementTree(element).write(file, encoding)
1032     data2 = []
1033     for item in data:
1034         if isinstance(item, bytes):
1035             item = item.decode()
1036         data2.append(item)
1037     return "".join(data2)
1038
1039 ##
1040 # Generic element structure builder.  This builder converts a sequence
1041 # of {@link #TreeBuilder.start}, {@link #TreeBuilder.data}, and {@link
1042 # #TreeBuilder.end} method calls to a well-formed element structure.
1043 # <p>
1044 # You can use this class to build an element structure using a custom XML
1045 # parser, or a parser for some other XML-like format.
1046 #
1047 # @param element_factory Optional element factory.  This factory
1048 #    is called to create new Element instances, as necessary.
1049
1050 class TreeBuilder:
1051
1052     def __init__(self, element_factory=None):
1053         self._data = [] # data collector
1054         self._elem = [] # element stack
1055         self._last = None # last element
1056         self._tail = None # true if we're after an end tag
1057         if element_factory is None:
1058             element_factory = _ElementInterface
1059         self._factory = element_factory
1060
1061     ##
1062     # Flushes the parser buffers, and returns the toplevel documen
1063     # element.
1064     #
1065     # @return An Element instance.
1066     # @defreturn Element
1067
1068     def close(self):
1069         assert len(self._elem) == 0, "missing end tags"
1070         assert self._last != None, "missing toplevel element"
1071         return self._last
1072
1073     def _flush(self):
1074         if self._data:
1075             if self._last is not None:
1076                 text = ""
1077                 for item in self._data:
1078                     try:
1079                         text += item
1080                     except:
1081                         text += item.decode()
1082                 if self._tail:
1083                     assert self._last.tail is None, "internal error (tail)"
1084                     self._last.tail = text
1085                 else:
1086                     assert self._last.text is None, "internal error (text)"
1087                     self._last.text = text
1088             self._data = []
1089
1090     ##
1091     # Adds text to the current element.
1092     #
1093     # @param data A string.  This should be either an 8-bit string
1094     #    containing ASCII text, or a Unicode string.
1095
1096     def data(self, data):
1097         self._data.append(data)
1098
1099     ##
1100     # Opens a new element.
1101     #
1102     # @param tag The element name.
1103     # @param attrib A dictionary containing element attributes.
1104     # @return The opened element.
1105     # @defreturn Element
1106
1107     def start(self, tag, attrs):
1108         self._flush()
1109         self._last = elem = self._factory(tag, attrs)
1110         if self._elem:
1111             self._elem[-1].append(elem)
1112         self._elem.append(elem)
1113         self._tail = 0
1114         return elem
1115
1116     ##
1117     # Closes the current element.
1118     #
1119     # @param tag The element name.
1120     # @return The closed element.
1121     # @defreturn Element
1122
1123     def end(self, tag):
1124         self._flush()
1125         self._last = self._elem.pop()
1126         assert self._last.tag == tag,\
1127                "end tag mismatch (expected %s, got %s)" % (
1128                    self._last.tag, tag)
1129         self._tail = 1
1130         return self._last
1131
1132 ##
1133 # Element structure builder for XML source data, based on the
1134 # <b>expat</b> parser.
1135 #
1136 # @keyparam target Target object.  If omitted, the builder uses an
1137 #     instance of the standard {@link #TreeBuilder} class.
1138 # @keyparam html Predefine HTML entities.  This flag is not supported
1139 #     by the current implementation.
1140 # @see #ElementTree
1141 # @see #TreeBuilder
1142
1143 class XMLTreeBuilder:
1144
1145     def __init__(self, html=0, target=None):
1146         try:
1147             from xml.parsers import expat
1148         except ImportError:
1149             raise ImportError(
1150                 "No module named expat; use SimpleXMLTreeBuilder instead"
1151                 )
1152         self._parser = parser = expat.ParserCreate(None, "}")
1153         if target is None:
1154             target = TreeBuilder()
1155         self._target = target
1156         self._names = {} # name memo cache
1157         # callbacks
1158         parser.DefaultHandlerExpand = self._default
1159         parser.StartElementHandler = self._start
1160         parser.EndElementHandler = self._end
1161         parser.CharacterDataHandler = self._data
1162         # let expat do the buffering, if supported
1163         try:
1164             self._parser.buffer_text = 1
1165         except AttributeError:
1166             pass
1167         # use new-style attribute handling, if supported
1168         try:
1169             self._parser.ordered_attributes = 1
1170             self._parser.specified_attributes = 1
1171             parser.StartElementHandler = self._start_list
1172         except AttributeError:
1173             pass
1174         #encoding = None
1175         #if not parser.returns_unicode:
1176         #    encoding = "utf-8"
1177         # target.xml(encoding, None)
1178         self._doctype = None
1179         self.entity = {}
1180
1181     def _fixtext(self, text):
1182         # convert text string to ascii, if possible
1183         try:
1184             return _encode(text, "ascii")
1185         except UnicodeError:
1186             return text
1187
1188     def _fixname(self, key):
1189         # expand qname, and convert name string to ascii, if possible
1190         try:
1191             name = self._names[key]
1192         except KeyError:
1193             name = key
1194             if "}" in name:
1195                 name = "{" + name
1196             self._names[key] = name = self._fixtext(name)
1197         return name
1198
1199     def _start(self, tag, attrib_in):
1200         fixname = self._fixname
1201         tag = fixname(tag)
1202         attrib = {}
1203         for key, value in attrib_in.items():
1204             attrib[fixname(key)] = self._fixtext(value)
1205         return self._target.start(tag, attrib)
1206
1207     def _start_list(self, tag, attrib_in):
1208         fixname = self._fixname
1209         tag = fixname(tag)
1210         attrib = {}
1211         if attrib_in:
1212             for i in range(0, len(attrib_in), 2):
1213                 attrib[fixname(attrib_in[i])] = self._fixtext(attrib_in[i+1])
1214         return self._target.start(tag, attrib)
1215
1216     def _data(self, text):
1217         return self._target.data(self._fixtext(text))
1218
1219     def _end(self, tag):
1220         return self._target.end(self._fixname(tag))
1221
1222     def _default(self, text):
1223         prefix = text[:1]
1224         if prefix == "&":
1225             # deal with undefined entities
1226             try:
1227                 self._target.data(self.entity[text[1:-1]])
1228             except KeyError:
1229                 from xml.parsers import expat
1230                 raise expat.error(
1231                     "undefined entity %s: line %d, column %d" %
1232                     (text, self._parser.ErrorLineNumber,
1233                     self._parser.ErrorColumnNumber)
1234                     )
1235         elif prefix == "<" and text[:9] == "<!DOCTYPE":
1236             self._doctype = [] # inside a doctype declaration
1237         elif self._doctype is not None:
1238             # parse doctype contents
1239             if prefix == ">":
1240                 self._doctype = None
1241                 return
1242             text = string.strip(text)
1243             if not text:
1244                 return
1245             self._doctype.append(text)
1246             n = len(self._doctype)
1247             if n > 2:
1248                 type = self._doctype[1]
1249                 if type == "PUBLIC" and n == 4:
1250                     name, type, pubid, system = self._doctype
1251                 elif type == "SYSTEM" and n == 3:
1252                     name, type, system = self._doctype
1253                     pubid = None
1254                 else:
1255                     return
1256                 if pubid:
1257                     pubid = pubid[1:-1]
1258                 self.doctype(name, pubid, system[1:-1])
1259                 self._doctype = None
1260
1261     ##
1262     # Handles a doctype declaration.
1263     #
1264     # @param name Doctype name.
1265     # @param pubid Public identifier.
1266     # @param system System identifier.
1267
1268     def doctype(self, name, pubid, system):
1269         pass
1270
1271     ##
1272     # Feeds data to the parser.
1273     #
1274     # @param data Encoded data.
1275
1276     def feed(self, data):
1277         # OP 14/11/2017 Ajout de traces pour essayer de decouvrir le pb
1278         #               de remontee de log des tests
1279         #print "TRACES OP - ElementTree.py/XMLTreeBuilder.feed() data = '#%s#'" %data
1280         self._parser.Parse(data, 0)
1281
1282     ##
1283     # Finishes feeding data to the parser.
1284     #
1285     # @return An element structure.
1286     # @defreturn Element
1287
1288     def close(self):
1289         self._parser.Parse("", 1) # end of data
1290         tree = self._target.close()
1291         del self._target, self._parser # get rid of circular references
1292         return tree