| """Lightweight XML support for Python. | 
 |  | 
 |  XML is an inherently hierarchical data format, and the most natural way to | 
 |  represent it is with a tree.  This module has two classes for this purpose: | 
 |  | 
 |     1. ElementTree represents the whole XML document as a tree and | 
 |  | 
 |     2. Element represents a single node in this tree. | 
 |  | 
 |  Interactions with the whole document (reading and writing to/from files) are | 
 |  usually done on the ElementTree level.  Interactions with a single XML element | 
 |  and its sub-elements are done on the Element level. | 
 |  | 
 |  Element is a flexible container object designed to store hierarchical data | 
 |  structures in memory. It can be described as a cross between a list and a | 
 |  dictionary.  Each Element has a number of properties associated with it: | 
 |  | 
 |     'tag' - a string containing the element's name. | 
 |  | 
 |     'attributes' - a Python dictionary storing the element's attributes. | 
 |  | 
 |     'text' - a string containing the element's text content. | 
 |  | 
 |     'tail' - an optional string containing text after the element's end tag. | 
 |  | 
 |     And a number of child elements stored in a Python sequence. | 
 |  | 
 |  To create an element instance, use the Element constructor, | 
 |  or the SubElement factory function. | 
 |  | 
 |  You can also use the ElementTree class to wrap an element structure | 
 |  and convert it to and from XML. | 
 |  | 
 | """ | 
 |  | 
 | #--------------------------------------------------------------------- | 
 | # Licensed to PSF under a Contributor Agreement. | 
 | # See http://www.python.org/psf/license for licensing details. | 
 | # | 
 | # ElementTree | 
 | # Copyright (c) 1999-2008 by Fredrik Lundh.  All rights reserved. | 
 | # | 
 | # fredrik@pythonware.com | 
 | # http://www.pythonware.com | 
 | # -------------------------------------------------------------------- | 
 | # The ElementTree toolkit is | 
 | # | 
 | # Copyright (c) 1999-2008 by Fredrik Lundh | 
 | # | 
 | # By obtaining, using, and/or copying this software and/or its | 
 | # associated documentation, you agree that you have read, understood, | 
 | # and will comply with the following terms and conditions: | 
 | # | 
 | # Permission to use, copy, modify, and distribute this software and | 
 | # its associated documentation for any purpose and without fee is | 
 | # hereby granted, provided that the above copyright notice appears in | 
 | # all copies, and that both that copyright notice and this permission | 
 | # notice appear in supporting documentation, and that the name of | 
 | # Secret Labs AB or the author not be used in advertising or publicity | 
 | # pertaining to distribution of the software without specific, written | 
 | # prior permission. | 
 | # | 
 | # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | 
 | # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- | 
 | # ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR | 
 | # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY | 
 | # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | 
 | # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS | 
 | # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | 
 | # OF THIS SOFTWARE. | 
 | # -------------------------------------------------------------------- | 
 |  | 
 | __all__ = [ | 
 |     # public symbols | 
 |     "Comment", | 
 |     "dump", | 
 |     "Element", "ElementTree", | 
 |     "fromstring", "fromstringlist", | 
 |     "iselement", "iterparse", | 
 |     "parse", "ParseError", | 
 |     "PI", "ProcessingInstruction", | 
 |     "QName", | 
 |     "SubElement", | 
 |     "tostring", "tostringlist", | 
 |     "TreeBuilder", | 
 |     "VERSION", | 
 |     "XML", "XMLID", | 
 |     "XMLParser", | 
 |     "register_namespace", | 
 |     ] | 
 |  | 
 | VERSION = "1.3.0" | 
 |  | 
 | import sys | 
 | import re | 
 | import warnings | 
 | import io | 
 | import contextlib | 
 |  | 
 | from . import ElementPath | 
 |  | 
 |  | 
 | class ParseError(SyntaxError): | 
 |     """An error when parsing an XML document. | 
 |  | 
 |     In addition to its exception value, a ParseError contains | 
 |     two extra attributes: | 
 |         'code'     - the specific exception code | 
 |         'position' - the line and column of the error | 
 |  | 
 |     """ | 
 |     pass | 
 |  | 
 | # -------------------------------------------------------------------- | 
 |  | 
 |  | 
 | def iselement(element): | 
 |     """Return True if *element* appears to be an Element.""" | 
 |     return hasattr(element, 'tag') | 
 |  | 
 |  | 
 | class Element: | 
 |     """An XML element. | 
 |  | 
 |     This class is the reference implementation of the Element interface. | 
 |  | 
 |     An element's length is its number of subelements.  That means if you | 
 |     you want to check if an element is truly empty, you should check BOTH | 
 |     its length AND its text attribute. | 
 |  | 
 |     The element tag, attribute names, and attribute values can be either | 
 |     bytes or strings. | 
 |  | 
 |     *tag* is the element name.  *attrib* is an optional dictionary containing | 
 |     element attributes. *extra* are additional element attributes given as | 
 |     keyword arguments. | 
 |  | 
 |     Example form: | 
 |         <tag attrib>text<child/>...</tag>tail | 
 |  | 
 |     """ | 
 |  | 
 |     tag = None | 
 |     """The element's name.""" | 
 |  | 
 |     attrib = None | 
 |     """Dictionary of the element's attributes.""" | 
 |  | 
 |     text = None | 
 |     """ | 
 |     Text before first subelement. This is either a string or the value None. | 
 |     Note that if there is no text, this attribute may be either | 
 |     None or the empty string, depending on the parser. | 
 |  | 
 |     """ | 
 |  | 
 |     tail = None | 
 |     """ | 
 |     Text after this element's end tag, but before the next sibling element's | 
 |     start tag.  This is either a string or the value None.  Note that if there | 
 |     was no text, this attribute may be either None or an empty string, | 
 |     depending on the parser. | 
 |  | 
 |     """ | 
 |  | 
 |     def __init__(self, tag, attrib={}, **extra): | 
 |         if not isinstance(attrib, dict): | 
 |             raise TypeError("attrib must be dict, not %s" % ( | 
 |                 attrib.__class__.__name__,)) | 
 |         attrib = attrib.copy() | 
 |         attrib.update(extra) | 
 |         self.tag = tag | 
 |         self.attrib = attrib | 
 |         self._children = [] | 
 |  | 
 |     def __repr__(self): | 
 |         return "<Element %s at 0x%x>" % (repr(self.tag), id(self)) | 
 |  | 
 |     def makeelement(self, tag, attrib): | 
 |         """Create a new element with the same type. | 
 |  | 
 |         *tag* is a string containing the element name. | 
 |         *attrib* is a dictionary containing the element attributes. | 
 |  | 
 |         Do not call this method, use the SubElement factory function instead. | 
 |  | 
 |         """ | 
 |         return self.__class__(tag, attrib) | 
 |  | 
 |     def copy(self): | 
 |         """Return copy of current element. | 
 |  | 
 |         This creates a shallow copy. Subelements will be shared with the | 
 |         original tree. | 
 |  | 
 |         """ | 
 |         elem = self.makeelement(self.tag, self.attrib) | 
 |         elem.text = self.text | 
 |         elem.tail = self.tail | 
 |         elem[:] = self | 
 |         return elem | 
 |  | 
 |     def __len__(self): | 
 |         return len(self._children) | 
 |  | 
 |     def __bool__(self): | 
 |         warnings.warn( | 
 |             "The behavior of this method will change in future versions.  " | 
 |             "Use specific 'len(elem)' or 'elem is not None' test instead.", | 
 |             FutureWarning, stacklevel=2 | 
 |             ) | 
 |         return len(self._children) != 0 # emulate old behaviour, for now | 
 |  | 
 |     def __getitem__(self, index): | 
 |         return self._children[index] | 
 |  | 
 |     def __setitem__(self, index, element): | 
 |         # if isinstance(index, slice): | 
 |         #     for elt in element: | 
 |         #         assert iselement(elt) | 
 |         # else: | 
 |         #     assert iselement(element) | 
 |         self._children[index] = element | 
 |  | 
 |     def __delitem__(self, index): | 
 |         del self._children[index] | 
 |  | 
 |     def append(self, subelement): | 
 |         """Add *subelement* to the end of this element. | 
 |  | 
 |         The new element will appear in document order after the last existing | 
 |         subelement (or directly after the text, if it's the first subelement), | 
 |         but before the end tag for this element. | 
 |  | 
 |         """ | 
 |         self._assert_is_element(subelement) | 
 |         self._children.append(subelement) | 
 |  | 
 |     def extend(self, elements): | 
 |         """Append subelements from a sequence. | 
 |  | 
 |         *elements* is a sequence with zero or more elements. | 
 |  | 
 |         """ | 
 |         for element in elements: | 
 |             self._assert_is_element(element) | 
 |         self._children.extend(elements) | 
 |  | 
 |     def insert(self, index, subelement): | 
 |         """Insert *subelement* at position *index*.""" | 
 |         self._assert_is_element(subelement) | 
 |         self._children.insert(index, subelement) | 
 |  | 
 |     def _assert_is_element(self, e): | 
 |         # Need to refer to the actual Python implementation, not the | 
 |         # shadowing C implementation. | 
 |         if not isinstance(e, _Element_Py): | 
 |             raise TypeError('expected an Element, not %s' % type(e).__name__) | 
 |  | 
 |     def remove(self, subelement): | 
 |         """Remove matching subelement. | 
 |  | 
 |         Unlike the find methods, this method compares elements based on | 
 |         identity, NOT ON tag value or contents.  To remove subelements by | 
 |         other means, the easiest way is to use a list comprehension to | 
 |         select what elements to keep, and then use slice assignment to update | 
 |         the parent element. | 
 |  | 
 |         ValueError is raised if a matching element could not be found. | 
 |  | 
 |         """ | 
 |         # assert iselement(element) | 
 |         self._children.remove(subelement) | 
 |  | 
 |     def getchildren(self): | 
 |         """(Deprecated) Return all subelements. | 
 |  | 
 |         Elements are returned in document order. | 
 |  | 
 |         """ | 
 |         warnings.warn( | 
 |             "This method will be removed in future versions.  " | 
 |             "Use 'list(elem)' or iteration over elem instead.", | 
 |             DeprecationWarning, stacklevel=2 | 
 |             ) | 
 |         return self._children | 
 |  | 
 |     def find(self, path, namespaces=None): | 
 |         """Find first matching element by tag name or path. | 
 |  | 
 |         *path* is a string having either an element tag or an XPath, | 
 |         *namespaces* is an optional mapping from namespace prefix to full name. | 
 |  | 
 |         Return the first matching element, or None if no element was found. | 
 |  | 
 |         """ | 
 |         return ElementPath.find(self, path, namespaces) | 
 |  | 
 |     def findtext(self, path, default=None, namespaces=None): | 
 |         """Find text for first matching element by tag name or path. | 
 |  | 
 |         *path* is a string having either an element tag or an XPath, | 
 |         *default* is the value to return if the element was not found, | 
 |         *namespaces* is an optional mapping from namespace prefix to full name. | 
 |  | 
 |         Return text content of first matching element, or default value if | 
 |         none was found.  Note that if an element is found having no text | 
 |         content, the empty string is returned. | 
 |  | 
 |         """ | 
 |         return ElementPath.findtext(self, path, default, namespaces) | 
 |  | 
 |     def findall(self, path, namespaces=None): | 
 |         """Find all matching subelements by tag name or path. | 
 |  | 
 |         *path* is a string having either an element tag or an XPath, | 
 |         *namespaces* is an optional mapping from namespace prefix to full name. | 
 |  | 
 |         Returns list containing all matching elements in document order. | 
 |  | 
 |         """ | 
 |         return ElementPath.findall(self, path, namespaces) | 
 |  | 
 |     def iterfind(self, path, namespaces=None): | 
 |         """Find all matching subelements by tag name or path. | 
 |  | 
 |         *path* is a string having either an element tag or an XPath, | 
 |         *namespaces* is an optional mapping from namespace prefix to full name. | 
 |  | 
 |         Return an iterable yielding all matching elements in document order. | 
 |  | 
 |         """ | 
 |         return ElementPath.iterfind(self, path, namespaces) | 
 |  | 
 |     def clear(self): | 
 |         """Reset element. | 
 |  | 
 |         This function removes all subelements, clears all attributes, and sets | 
 |         the text and tail attributes to None. | 
 |  | 
 |         """ | 
 |         self.attrib.clear() | 
 |         self._children = [] | 
 |         self.text = self.tail = None | 
 |  | 
 |     def get(self, key, default=None): | 
 |         """Get element attribute. | 
 |  | 
 |         Equivalent to attrib.get, but some implementations may handle this a | 
 |         bit more efficiently.  *key* is what attribute to look for, and | 
 |         *default* is what to return if the attribute was not found. | 
 |  | 
 |         Returns a string containing the attribute value, or the default if | 
 |         attribute was not found. | 
 |  | 
 |         """ | 
 |         return self.attrib.get(key, default) | 
 |  | 
 |     def set(self, key, value): | 
 |         """Set element attribute. | 
 |  | 
 |         Equivalent to attrib[key] = value, but some implementations may handle | 
 |         this a bit more efficiently.  *key* is what attribute to set, and | 
 |         *value* is the attribute value to set it to. | 
 |  | 
 |         """ | 
 |         self.attrib[key] = value | 
 |  | 
 |     def keys(self): | 
 |         """Get list of attribute names. | 
 |  | 
 |         Names are returned in an arbitrary order, just like an ordinary | 
 |         Python dict.  Equivalent to attrib.keys() | 
 |  | 
 |         """ | 
 |         return self.attrib.keys() | 
 |  | 
 |     def items(self): | 
 |         """Get element attributes as a sequence. | 
 |  | 
 |         The attributes are returned in arbitrary order.  Equivalent to | 
 |         attrib.items(). | 
 |  | 
 |         Return a list of (name, value) tuples. | 
 |  | 
 |         """ | 
 |         return self.attrib.items() | 
 |  | 
 |     def iter(self, tag=None): | 
 |         """Create tree iterator. | 
 |  | 
 |         The iterator loops over the element and all subelements in document | 
 |         order, returning all elements with a matching tag. | 
 |  | 
 |         If the tree structure is modified during iteration, new or removed | 
 |         elements may or may not be included.  To get a stable set, use the | 
 |         list() function on the iterator, and loop over the resulting list. | 
 |  | 
 |         *tag* is what tags to look for (default is to return all elements) | 
 |  | 
 |         Return an iterator containing all the matching elements. | 
 |  | 
 |         """ | 
 |         if tag == "*": | 
 |             tag = None | 
 |         if tag is None or self.tag == tag: | 
 |             yield self | 
 |         for e in self._children: | 
 |             yield from e.iter(tag) | 
 |  | 
 |     # compatibility | 
 |     def getiterator(self, tag=None): | 
 |         # Change for a DeprecationWarning in 1.4 | 
 |         warnings.warn( | 
 |             "This method will be removed in future versions.  " | 
 |             "Use 'elem.iter()' or 'list(elem.iter())' instead.", | 
 |             PendingDeprecationWarning, stacklevel=2 | 
 |         ) | 
 |         return list(self.iter(tag)) | 
 |  | 
 |     def itertext(self): | 
 |         """Create text iterator. | 
 |  | 
 |         The iterator loops over the element and all subelements in document | 
 |         order, returning all inner text. | 
 |  | 
 |         """ | 
 |         tag = self.tag | 
 |         if not isinstance(tag, str) and tag is not None: | 
 |             return | 
 |         if self.text: | 
 |             yield self.text | 
 |         for e in self: | 
 |             yield from e.itertext() | 
 |             if e.tail: | 
 |                 yield e.tail | 
 |  | 
 |  | 
 | def SubElement(parent, tag, attrib={}, **extra): | 
 |     """Subelement factory which creates an element instance, and appends it | 
 |     to an existing parent. | 
 |  | 
 |     The element tag, attribute names, and attribute values can be either | 
 |     bytes or Unicode strings. | 
 |  | 
 |     *parent* is the parent element, *tag* is the subelements name, *attrib* is | 
 |     an optional directory containing element attributes, *extra* are | 
 |     additional attributes given as keyword arguments. | 
 |  | 
 |     """ | 
 |     attrib = attrib.copy() | 
 |     attrib.update(extra) | 
 |     element = parent.makeelement(tag, attrib) | 
 |     parent.append(element) | 
 |     return element | 
 |  | 
 |  | 
 | def Comment(text=None): | 
 |     """Comment element factory. | 
 |  | 
 |     This function creates a special element which the standard serializer | 
 |     serializes as an XML comment. | 
 |  | 
 |     *text* is a string containing the comment string. | 
 |  | 
 |     """ | 
 |     element = Element(Comment) | 
 |     element.text = text | 
 |     return element | 
 |  | 
 |  | 
 | def ProcessingInstruction(target, text=None): | 
 |     """Processing Instruction element factory. | 
 |  | 
 |     This function creates a special element which the standard serializer | 
 |     serializes as an XML comment. | 
 |  | 
 |     *target* is a string containing the processing instruction, *text* is a | 
 |     string containing the processing instruction contents, if any. | 
 |  | 
 |     """ | 
 |     element = Element(ProcessingInstruction) | 
 |     element.text = target | 
 |     if text: | 
 |         element.text = element.text + " " + text | 
 |     return element | 
 |  | 
 | PI = ProcessingInstruction | 
 |  | 
 |  | 
 | class QName: | 
 |     """Qualified name wrapper. | 
 |  | 
 |     This class can be used to wrap a QName attribute value in order to get | 
 |     proper namespace handing on output. | 
 |  | 
 |     *text_or_uri* is a string containing the QName value either in the form | 
 |     {uri}local, or if the tag argument is given, the URI part of a QName. | 
 |  | 
 |     *tag* is an optional argument which if given, will make the first | 
 |     argument (text_or_uri) be interpreted as a URI, and this argument (tag) | 
 |     be interpreted as a local name. | 
 |  | 
 |     """ | 
 |     def __init__(self, text_or_uri, tag=None): | 
 |         if tag: | 
 |             text_or_uri = "{%s}%s" % (text_or_uri, tag) | 
 |         self.text = text_or_uri | 
 |     def __str__(self): | 
 |         return self.text | 
 |     def __repr__(self): | 
 |         return '<QName %r>' % (self.text,) | 
 |     def __hash__(self): | 
 |         return hash(self.text) | 
 |     def __le__(self, other): | 
 |         if isinstance(other, QName): | 
 |             return self.text <= other.text | 
 |         return self.text <= other | 
 |     def __lt__(self, other): | 
 |         if isinstance(other, QName): | 
 |             return self.text < other.text | 
 |         return self.text < other | 
 |     def __ge__(self, other): | 
 |         if isinstance(other, QName): | 
 |             return self.text >= other.text | 
 |         return self.text >= other | 
 |     def __gt__(self, other): | 
 |         if isinstance(other, QName): | 
 |             return self.text > other.text | 
 |         return self.text > other | 
 |     def __eq__(self, other): | 
 |         if isinstance(other, QName): | 
 |             return self.text == other.text | 
 |         return self.text == other | 
 |     def __ne__(self, other): | 
 |         if isinstance(other, QName): | 
 |             return self.text != other.text | 
 |         return self.text != other | 
 |  | 
 | # -------------------------------------------------------------------- | 
 |  | 
 |  | 
 | class ElementTree: | 
 |     """An XML element hierarchy. | 
 |  | 
 |     This class also provides support for serialization to and from | 
 |     standard XML. | 
 |  | 
 |     *element* is an optional root element node, | 
 |     *file* is an optional file handle or file name of an XML file whose | 
 |     contents will be used to initialize the tree with. | 
 |  | 
 |     """ | 
 |     def __init__(self, element=None, file=None): | 
 |         # assert element is None or iselement(element) | 
 |         self._root = element # first node | 
 |         if file: | 
 |             self.parse(file) | 
 |  | 
 |     def getroot(self): | 
 |         """Return root element of this tree.""" | 
 |         return self._root | 
 |  | 
 |     def _setroot(self, element): | 
 |         """Replace root element of this tree. | 
 |  | 
 |         This will discard the current contents of the tree and replace it | 
 |         with the given element.  Use with care! | 
 |  | 
 |         """ | 
 |         # assert iselement(element) | 
 |         self._root = element | 
 |  | 
 |     def parse(self, source, parser=None): | 
 |         """Load external XML document into element tree. | 
 |  | 
 |         *source* is a file name or file object, *parser* is an optional parser | 
 |         instance that defaults to XMLParser. | 
 |  | 
 |         ParseError is raised if the parser fails to parse the document. | 
 |  | 
 |         Returns the root element of the given source document. | 
 |  | 
 |         """ | 
 |         close_source = False | 
 |         if not hasattr(source, "read"): | 
 |             source = open(source, "rb") | 
 |             close_source = True | 
 |         try: | 
 |             if parser is None: | 
 |                 # If no parser was specified, create a default XMLParser | 
 |                 parser = XMLParser() | 
 |                 if hasattr(parser, '_parse_whole'): | 
 |                     # The default XMLParser, when it comes from an accelerator, | 
 |                     # can define an internal _parse_whole API for efficiency. | 
 |                     # It can be used to parse the whole source without feeding | 
 |                     # it with chunks. | 
 |                     self._root = parser._parse_whole(source) | 
 |                     return self._root | 
 |             while True: | 
 |                 data = source.read(65536) | 
 |                 if not data: | 
 |                     break | 
 |                 parser.feed(data) | 
 |             self._root = parser.close() | 
 |             return self._root | 
 |         finally: | 
 |             if close_source: | 
 |                 source.close() | 
 |  | 
 |     def iter(self, tag=None): | 
 |         """Create and return tree iterator for the root element. | 
 |  | 
 |         The iterator loops over all elements in this tree, in document order. | 
 |  | 
 |         *tag* is a string with the tag name to iterate over | 
 |         (default is to return all elements). | 
 |  | 
 |         """ | 
 |         # assert self._root is not None | 
 |         return self._root.iter(tag) | 
 |  | 
 |     # compatibility | 
 |     def getiterator(self, tag=None): | 
 |         # Change for a DeprecationWarning in 1.4 | 
 |         warnings.warn( | 
 |             "This method will be removed in future versions.  " | 
 |             "Use 'tree.iter()' or 'list(tree.iter())' instead.", | 
 |             PendingDeprecationWarning, stacklevel=2 | 
 |         ) | 
 |         return list(self.iter(tag)) | 
 |  | 
 |     def find(self, path, namespaces=None): | 
 |         """Find first matching element by tag name or path. | 
 |  | 
 |         Same as getroot().find(path), which is Element.find() | 
 |  | 
 |         *path* is a string having either an element tag or an XPath, | 
 |         *namespaces* is an optional mapping from namespace prefix to full name. | 
 |  | 
 |         Return the first matching element, or None if no element was found. | 
 |  | 
 |         """ | 
 |         # assert self._root is not None | 
 |         if path[:1] == "/": | 
 |             path = "." + path | 
 |             warnings.warn( | 
 |                 "This search is broken in 1.3 and earlier, and will be " | 
 |                 "fixed in a future version.  If you rely on the current " | 
 |                 "behaviour, change it to %r" % path, | 
 |                 FutureWarning, stacklevel=2 | 
 |                 ) | 
 |         return self._root.find(path, namespaces) | 
 |  | 
 |     def findtext(self, path, default=None, namespaces=None): | 
 |         """Find first matching element by tag name or path. | 
 |  | 
 |         Same as getroot().findtext(path),  which is Element.findtext() | 
 |  | 
 |         *path* is a string having either an element tag or an XPath, | 
 |         *namespaces* is an optional mapping from namespace prefix to full name. | 
 |  | 
 |         Return the first matching element, or None if no element was found. | 
 |  | 
 |         """ | 
 |         # assert self._root is not None | 
 |         if path[:1] == "/": | 
 |             path = "." + path | 
 |             warnings.warn( | 
 |                 "This search is broken in 1.3 and earlier, and will be " | 
 |                 "fixed in a future version.  If you rely on the current " | 
 |                 "behaviour, change it to %r" % path, | 
 |                 FutureWarning, stacklevel=2 | 
 |                 ) | 
 |         return self._root.findtext(path, default, namespaces) | 
 |  | 
 |     def findall(self, path, namespaces=None): | 
 |         """Find all matching subelements by tag name or path. | 
 |  | 
 |         Same as getroot().findall(path), which is Element.findall(). | 
 |  | 
 |         *path* is a string having either an element tag or an XPath, | 
 |         *namespaces* is an optional mapping from namespace prefix to full name. | 
 |  | 
 |         Return list containing all matching elements in document order. | 
 |  | 
 |         """ | 
 |         # assert self._root is not None | 
 |         if path[:1] == "/": | 
 |             path = "." + path | 
 |             warnings.warn( | 
 |                 "This search is broken in 1.3 and earlier, and will be " | 
 |                 "fixed in a future version.  If you rely on the current " | 
 |                 "behaviour, change it to %r" % path, | 
 |                 FutureWarning, stacklevel=2 | 
 |                 ) | 
 |         return self._root.findall(path, namespaces) | 
 |  | 
 |     def iterfind(self, path, namespaces=None): | 
 |         """Find all matching subelements by tag name or path. | 
 |  | 
 |         Same as getroot().iterfind(path), which is element.iterfind() | 
 |  | 
 |         *path* is a string having either an element tag or an XPath, | 
 |         *namespaces* is an optional mapping from namespace prefix to full name. | 
 |  | 
 |         Return an iterable yielding all matching elements in document order. | 
 |  | 
 |         """ | 
 |         # assert self._root is not None | 
 |         if path[:1] == "/": | 
 |             path = "." + path | 
 |             warnings.warn( | 
 |                 "This search is broken in 1.3 and earlier, and will be " | 
 |                 "fixed in a future version.  If you rely on the current " | 
 |                 "behaviour, change it to %r" % path, | 
 |                 FutureWarning, stacklevel=2 | 
 |                 ) | 
 |         return self._root.iterfind(path, namespaces) | 
 |  | 
 |     def write(self, file_or_filename, | 
 |               encoding=None, | 
 |               xml_declaration=None, | 
 |               default_namespace=None, | 
 |               method=None, *, | 
 |               short_empty_elements=True): | 
 |         """Write element tree to a file as XML. | 
 |  | 
 |         Arguments: | 
 |           *file_or_filename* -- file name or a file object opened for writing | 
 |  | 
 |           *encoding* -- the output encoding (default: US-ASCII) | 
 |  | 
 |           *xml_declaration* -- bool indicating if an XML declaration should be | 
 |                                added to the output. If None, an XML declaration | 
 |                                is added if encoding IS NOT either of: | 
 |                                US-ASCII, UTF-8, or Unicode | 
 |  | 
 |           *default_namespace* -- sets the default XML namespace (for "xmlns") | 
 |  | 
 |           *method* -- either "xml" (default), "html, "text", or "c14n" | 
 |  | 
 |           *short_empty_elements* -- controls the formatting of elements | 
 |                                     that contain no content. If True (default) | 
 |                                     they are emitted as a single self-closed | 
 |                                     tag, otherwise they are emitted as a pair | 
 |                                     of start/end tags | 
 |  | 
 |         """ | 
 |         if not method: | 
 |             method = "xml" | 
 |         elif method not in _serialize: | 
 |             raise ValueError("unknown method %r" % method) | 
 |         if not encoding: | 
 |             if method == "c14n": | 
 |                 encoding = "utf-8" | 
 |             else: | 
 |                 encoding = "us-ascii" | 
 |         else: | 
 |             encoding = encoding.lower() | 
 |         with _get_writer(file_or_filename, encoding) as write: | 
 |             if method == "xml" and (xml_declaration or | 
 |                     (xml_declaration is None and | 
 |                      encoding not in ("utf-8", "us-ascii", "unicode"))): | 
 |                 declared_encoding = encoding | 
 |                 if encoding == "unicode": | 
 |                     # Retrieve the default encoding for the xml declaration | 
 |                     import locale | 
 |                     declared_encoding = locale.getpreferredencoding() | 
 |                 write("<?xml version='1.0' encoding='%s'?>\n" % ( | 
 |                     declared_encoding,)) | 
 |             if method == "text": | 
 |                 _serialize_text(write, self._root) | 
 |             else: | 
 |                 qnames, namespaces = _namespaces(self._root, default_namespace) | 
 |                 serialize = _serialize[method] | 
 |                 serialize(write, self._root, qnames, namespaces, | 
 |                           short_empty_elements=short_empty_elements) | 
 |  | 
 |     def write_c14n(self, file): | 
 |         # lxml.etree compatibility.  use output method instead | 
 |         return self.write(file, method="c14n") | 
 |  | 
 | # -------------------------------------------------------------------- | 
 | # serialization support | 
 |  | 
 | @contextlib.contextmanager | 
 | def _get_writer(file_or_filename, encoding): | 
 |     # returns text write method and release all resources after using | 
 |     try: | 
 |         write = file_or_filename.write | 
 |     except AttributeError: | 
 |         # file_or_filename is a file name | 
 |         if encoding == "unicode": | 
 |             file = open(file_or_filename, "w") | 
 |         else: | 
 |             file = open(file_or_filename, "w", encoding=encoding, | 
 |                         errors="xmlcharrefreplace") | 
 |         with file: | 
 |             yield file.write | 
 |     else: | 
 |         # file_or_filename is a file-like object | 
 |         # encoding determines if it is a text or binary writer | 
 |         if encoding == "unicode": | 
 |             # use a text writer as is | 
 |             yield write | 
 |         else: | 
 |             # wrap a binary writer with TextIOWrapper | 
 |             with contextlib.ExitStack() as stack: | 
 |                 if isinstance(file_or_filename, io.BufferedIOBase): | 
 |                     file = file_or_filename | 
 |                 elif isinstance(file_or_filename, io.RawIOBase): | 
 |                     file = io.BufferedWriter(file_or_filename) | 
 |                     # Keep the original file open when the BufferedWriter is | 
 |                     # destroyed | 
 |                     stack.callback(file.detach) | 
 |                 else: | 
 |                     # This is to handle passed objects that aren't in the | 
 |                     # IOBase hierarchy, but just have a write method | 
 |                     file = io.BufferedIOBase() | 
 |                     file.writable = lambda: True | 
 |                     file.write = write | 
 |                     try: | 
 |                         # TextIOWrapper uses this methods to determine | 
 |                         # if BOM (for UTF-16, etc) should be added | 
 |                         file.seekable = file_or_filename.seekable | 
 |                         file.tell = file_or_filename.tell | 
 |                     except AttributeError: | 
 |                         pass | 
 |                 file = io.TextIOWrapper(file, | 
 |                                         encoding=encoding, | 
 |                                         errors="xmlcharrefreplace", | 
 |                                         newline="\n") | 
 |                 # Keep the original file open when the TextIOWrapper is | 
 |                 # destroyed | 
 |                 stack.callback(file.detach) | 
 |                 yield file.write | 
 |  | 
 | def _namespaces(elem, default_namespace=None): | 
 |     # identify namespaces used in this tree | 
 |  | 
 |     # maps qnames to *encoded* prefix:local names | 
 |     qnames = {None: None} | 
 |  | 
 |     # maps uri:s to prefixes | 
 |     namespaces = {} | 
 |     if default_namespace: | 
 |         namespaces[default_namespace] = "" | 
 |  | 
 |     def add_qname(qname): | 
 |         # calculate serialized qname representation | 
 |         try: | 
 |             if qname[:1] == "{": | 
 |                 uri, tag = qname[1:].rsplit("}", 1) | 
 |                 prefix = namespaces.get(uri) | 
 |                 if prefix is None: | 
 |                     prefix = _namespace_map.get(uri) | 
 |                     if prefix is None: | 
 |                         prefix = "ns%d" % len(namespaces) | 
 |                     if prefix != "xml": | 
 |                         namespaces[uri] = prefix | 
 |                 if prefix: | 
 |                     qnames[qname] = "%s:%s" % (prefix, tag) | 
 |                 else: | 
 |                     qnames[qname] = tag # default element | 
 |             else: | 
 |                 if default_namespace: | 
 |                     # FIXME: can this be handled in XML 1.0? | 
 |                     raise ValueError( | 
 |                         "cannot use non-qualified names with " | 
 |                         "default_namespace option" | 
 |                         ) | 
 |                 qnames[qname] = qname | 
 |         except TypeError: | 
 |             _raise_serialization_error(qname) | 
 |  | 
 |     # populate qname and namespaces table | 
 |     for elem in elem.iter(): | 
 |         tag = elem.tag | 
 |         if isinstance(tag, QName): | 
 |             if tag.text not in qnames: | 
 |                 add_qname(tag.text) | 
 |         elif isinstance(tag, str): | 
 |             if tag not in qnames: | 
 |                 add_qname(tag) | 
 |         elif tag is not None and tag is not Comment and tag is not PI: | 
 |             _raise_serialization_error(tag) | 
 |         for key, value in elem.items(): | 
 |             if isinstance(key, QName): | 
 |                 key = key.text | 
 |             if key not in qnames: | 
 |                 add_qname(key) | 
 |             if isinstance(value, QName) and value.text not in qnames: | 
 |                 add_qname(value.text) | 
 |         text = elem.text | 
 |         if isinstance(text, QName) and text.text not in qnames: | 
 |             add_qname(text.text) | 
 |     return qnames, namespaces | 
 |  | 
 | def _serialize_xml(write, elem, qnames, namespaces, | 
 |                    short_empty_elements, **kwargs): | 
 |     tag = elem.tag | 
 |     text = elem.text | 
 |     if tag is Comment: | 
 |         write("<!--%s-->" % text) | 
 |     elif tag is ProcessingInstruction: | 
 |         write("<?%s?>" % text) | 
 |     else: | 
 |         tag = qnames[tag] | 
 |         if tag is None: | 
 |             if text: | 
 |                 write(_escape_cdata(text)) | 
 |             for e in elem: | 
 |                 _serialize_xml(write, e, qnames, None, | 
 |                                short_empty_elements=short_empty_elements) | 
 |         else: | 
 |             write("<" + tag) | 
 |             items = list(elem.items()) | 
 |             if items or namespaces: | 
 |                 if namespaces: | 
 |                     for v, k in sorted(namespaces.items(), | 
 |                                        key=lambda x: x[1]):  # sort on prefix | 
 |                         if k: | 
 |                             k = ":" + k | 
 |                         write(" xmlns%s=\"%s\"" % ( | 
 |                             k, | 
 |                             _escape_attrib(v) | 
 |                             )) | 
 |                 for k, v in sorted(items):  # lexical order | 
 |                     if isinstance(k, QName): | 
 |                         k = k.text | 
 |                     if isinstance(v, QName): | 
 |                         v = qnames[v.text] | 
 |                     else: | 
 |                         v = _escape_attrib(v) | 
 |                     write(" %s=\"%s\"" % (qnames[k], v)) | 
 |             if text or len(elem) or not short_empty_elements: | 
 |                 write(">") | 
 |                 if text: | 
 |                     write(_escape_cdata(text)) | 
 |                 for e in elem: | 
 |                     _serialize_xml(write, e, qnames, None, | 
 |                                    short_empty_elements=short_empty_elements) | 
 |                 write("</" + tag + ">") | 
 |             else: | 
 |                 write(" />") | 
 |     if elem.tail: | 
 |         write(_escape_cdata(elem.tail)) | 
 |  | 
 | HTML_EMPTY = ("area", "base", "basefont", "br", "col", "frame", "hr", | 
 |               "img", "input", "isindex", "link", "meta", "param") | 
 |  | 
 | try: | 
 |     HTML_EMPTY = set(HTML_EMPTY) | 
 | except NameError: | 
 |     pass | 
 |  | 
 | def _serialize_html(write, elem, qnames, namespaces, **kwargs): | 
 |     tag = elem.tag | 
 |     text = elem.text | 
 |     if tag is Comment: | 
 |         write("<!--%s-->" % _escape_cdata(text)) | 
 |     elif tag is ProcessingInstruction: | 
 |         write("<?%s?>" % _escape_cdata(text)) | 
 |     else: | 
 |         tag = qnames[tag] | 
 |         if tag is None: | 
 |             if text: | 
 |                 write(_escape_cdata(text)) | 
 |             for e in elem: | 
 |                 _serialize_html(write, e, qnames, None) | 
 |         else: | 
 |             write("<" + tag) | 
 |             items = list(elem.items()) | 
 |             if items or namespaces: | 
 |                 if namespaces: | 
 |                     for v, k in sorted(namespaces.items(), | 
 |                                        key=lambda x: x[1]):  # sort on prefix | 
 |                         if k: | 
 |                             k = ":" + k | 
 |                         write(" xmlns%s=\"%s\"" % ( | 
 |                             k, | 
 |                             _escape_attrib(v) | 
 |                             )) | 
 |                 for k, v in sorted(items):  # lexical order | 
 |                     if isinstance(k, QName): | 
 |                         k = k.text | 
 |                     if isinstance(v, QName): | 
 |                         v = qnames[v.text] | 
 |                     else: | 
 |                         v = _escape_attrib_html(v) | 
 |                     # FIXME: handle boolean attributes | 
 |                     write(" %s=\"%s\"" % (qnames[k], v)) | 
 |             write(">") | 
 |             ltag = tag.lower() | 
 |             if text: | 
 |                 if ltag == "script" or ltag == "style": | 
 |                     write(text) | 
 |                 else: | 
 |                     write(_escape_cdata(text)) | 
 |             for e in elem: | 
 |                 _serialize_html(write, e, qnames, None) | 
 |             if ltag not in HTML_EMPTY: | 
 |                 write("</" + tag + ">") | 
 |     if elem.tail: | 
 |         write(_escape_cdata(elem.tail)) | 
 |  | 
 | def _serialize_text(write, elem): | 
 |     for part in elem.itertext(): | 
 |         write(part) | 
 |     if elem.tail: | 
 |         write(elem.tail) | 
 |  | 
 | _serialize = { | 
 |     "xml": _serialize_xml, | 
 |     "html": _serialize_html, | 
 |     "text": _serialize_text, | 
 | # this optional method is imported at the end of the module | 
 | #   "c14n": _serialize_c14n, | 
 | } | 
 |  | 
 |  | 
 | def register_namespace(prefix, uri): | 
 |     """Register a namespace prefix. | 
 |  | 
 |     The registry is global, and any existing mapping for either the | 
 |     given prefix or the namespace URI will be removed. | 
 |  | 
 |     *prefix* is the namespace prefix, *uri* is a namespace uri. Tags and | 
 |     attributes in this namespace will be serialized with prefix if possible. | 
 |  | 
 |     ValueError is raised if prefix is reserved or is invalid. | 
 |  | 
 |     """ | 
 |     if re.match("ns\d+$", prefix): | 
 |         raise ValueError("Prefix format reserved for internal use") | 
 |     for k, v in list(_namespace_map.items()): | 
 |         if k == uri or v == prefix: | 
 |             del _namespace_map[k] | 
 |     _namespace_map[uri] = prefix | 
 |  | 
 | _namespace_map = { | 
 |     # "well-known" namespace prefixes | 
 |     "http://www.w3.org/XML/1998/namespace": "xml", | 
 |     "http://www.w3.org/1999/xhtml": "html", | 
 |     "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf", | 
 |     "http://schemas.xmlsoap.org/wsdl/": "wsdl", | 
 |     # xml schema | 
 |     "http://www.w3.org/2001/XMLSchema": "xs", | 
 |     "http://www.w3.org/2001/XMLSchema-instance": "xsi", | 
 |     # dublin core | 
 |     "http://purl.org/dc/elements/1.1/": "dc", | 
 | } | 
 | # For tests and troubleshooting | 
 | register_namespace._namespace_map = _namespace_map | 
 |  | 
 | def _raise_serialization_error(text): | 
 |     raise TypeError( | 
 |         "cannot serialize %r (type %s)" % (text, type(text).__name__) | 
 |         ) | 
 |  | 
 | def _escape_cdata(text): | 
 |     # escape character data | 
 |     try: | 
 |         # it's worth avoiding do-nothing calls for strings that are | 
 |         # shorter than 500 character, or so.  assume that's, by far, | 
 |         # the most common case in most applications. | 
 |         if "&" in text: | 
 |             text = text.replace("&", "&") | 
 |         if "<" in text: | 
 |             text = text.replace("<", "<") | 
 |         if ">" in text: | 
 |             text = text.replace(">", ">") | 
 |         return text | 
 |     except (TypeError, AttributeError): | 
 |         _raise_serialization_error(text) | 
 |  | 
 | def _escape_attrib(text): | 
 |     # escape attribute value | 
 |     try: | 
 |         if "&" in text: | 
 |             text = text.replace("&", "&") | 
 |         if "<" in text: | 
 |             text = text.replace("<", "<") | 
 |         if ">" in text: | 
 |             text = text.replace(">", ">") | 
 |         if "\"" in text: | 
 |             text = text.replace("\"", """) | 
 |         if "\n" in text: | 
 |             text = text.replace("\n", "
") | 
 |         return text | 
 |     except (TypeError, AttributeError): | 
 |         _raise_serialization_error(text) | 
 |  | 
 | def _escape_attrib_html(text): | 
 |     # escape attribute value | 
 |     try: | 
 |         if "&" in text: | 
 |             text = text.replace("&", "&") | 
 |         if ">" in text: | 
 |             text = text.replace(">", ">") | 
 |         if "\"" in text: | 
 |             text = text.replace("\"", """) | 
 |         return text | 
 |     except (TypeError, AttributeError): | 
 |         _raise_serialization_error(text) | 
 |  | 
 | # -------------------------------------------------------------------- | 
 |  | 
 | def tostring(element, encoding=None, method=None, *, | 
 |              short_empty_elements=True): | 
 |     """Generate string representation of XML element. | 
 |  | 
 |     All subelements are included.  If encoding is "unicode", a string | 
 |     is returned. Otherwise a bytestring is returned. | 
 |  | 
 |     *element* is an Element instance, *encoding* is an optional output | 
 |     encoding defaulting to US-ASCII, *method* is an optional output which can | 
 |     be one of "xml" (default), "html", "text" or "c14n". | 
 |  | 
 |     Returns an (optionally) encoded string containing the XML data. | 
 |  | 
 |     """ | 
 |     stream = io.StringIO() if encoding == 'unicode' else io.BytesIO() | 
 |     ElementTree(element).write(stream, encoding, method=method, | 
 |                                short_empty_elements=short_empty_elements) | 
 |     return stream.getvalue() | 
 |  | 
 | class _ListDataStream(io.BufferedIOBase): | 
 |     """An auxiliary stream accumulating into a list reference.""" | 
 |     def __init__(self, lst): | 
 |         self.lst = lst | 
 |  | 
 |     def writable(self): | 
 |         return True | 
 |  | 
 |     def seekable(self): | 
 |         return True | 
 |  | 
 |     def write(self, b): | 
 |         self.lst.append(b) | 
 |  | 
 |     def tell(self): | 
 |         return len(self.lst) | 
 |  | 
 | def tostringlist(element, encoding=None, method=None, *, | 
 |                  short_empty_elements=True): | 
 |     lst = [] | 
 |     stream = _ListDataStream(lst) | 
 |     ElementTree(element).write(stream, encoding, method=method, | 
 |                                short_empty_elements=short_empty_elements) | 
 |     return lst | 
 |  | 
 |  | 
 | def dump(elem): | 
 |     """Write element tree or element structure to sys.stdout. | 
 |  | 
 |     This function should be used for debugging only. | 
 |  | 
 |     *elem* is either an ElementTree, or a single Element.  The exact output | 
 |     format is implementation dependent.  In this version, it's written as an | 
 |     ordinary XML file. | 
 |  | 
 |     """ | 
 |     # debugging | 
 |     if not isinstance(elem, ElementTree): | 
 |         elem = ElementTree(elem) | 
 |     elem.write(sys.stdout, encoding="unicode") | 
 |     tail = elem.getroot().tail | 
 |     if not tail or tail[-1] != "\n": | 
 |         sys.stdout.write("\n") | 
 |  | 
 | # -------------------------------------------------------------------- | 
 | # parsing | 
 |  | 
 |  | 
 | def parse(source, parser=None): | 
 |     """Parse XML document into element tree. | 
 |  | 
 |     *source* is a filename or file object containing XML data, | 
 |     *parser* is an optional parser instance defaulting to XMLParser. | 
 |  | 
 |     Return an ElementTree instance. | 
 |  | 
 |     """ | 
 |     tree = ElementTree() | 
 |     tree.parse(source, parser) | 
 |     return tree | 
 |  | 
 |  | 
 | def iterparse(source, events=None, parser=None): | 
 |     """Incrementally parse XML document into ElementTree. | 
 |  | 
 |     This class also reports what's going on to the user based on the | 
 |     *events* it is initialized with.  The supported events are the strings | 
 |     "start", "end", "start-ns" and "end-ns" (the "ns" events are used to get | 
 |     detailed namespace information).  If *events* is omitted, only | 
 |     "end" events are reported. | 
 |  | 
 |     *source* is a filename or file object containing XML data, *events* is | 
 |     a list of events to report back, *parser* is an optional parser instance. | 
 |  | 
 |     Returns an iterator providing (event, elem) pairs. | 
 |  | 
 |     """ | 
 |     close_source = False | 
 |     if not hasattr(source, "read"): | 
 |         source = open(source, "rb") | 
 |         close_source = True | 
 |     return _IterParseIterator(source, events, parser, close_source) | 
 |  | 
 |  | 
 | class XMLPullParser: | 
 |  | 
 |     def __init__(self, events=None, *, _parser=None): | 
 |         # The _parser argument is for internal use only and must not be relied | 
 |         # upon in user code. It will be removed in a future release. | 
 |         # See http://bugs.python.org/issue17741 for more details. | 
 |  | 
 |         # _elementtree.c expects a list, not a deque | 
 |         self._events_queue = [] | 
 |         self._index = 0 | 
 |         self._parser = _parser or XMLParser(target=TreeBuilder()) | 
 |         # wire up the parser for event reporting | 
 |         if events is None: | 
 |             events = ("end",) | 
 |         self._parser._setevents(self._events_queue, events) | 
 |  | 
 |     def feed(self, data): | 
 |         """Feed encoded data to parser.""" | 
 |         if self._parser is None: | 
 |             raise ValueError("feed() called after end of stream") | 
 |         if data: | 
 |             try: | 
 |                 self._parser.feed(data) | 
 |             except SyntaxError as exc: | 
 |                 self._events_queue.append(exc) | 
 |  | 
 |     def _close_and_return_root(self): | 
 |         # iterparse needs this to set its root attribute properly :( | 
 |         root = self._parser.close() | 
 |         self._parser = None | 
 |         return root | 
 |  | 
 |     def close(self): | 
 |         """Finish feeding data to parser. | 
 |  | 
 |         Unlike XMLParser, does not return the root element. Use | 
 |         read_events() to consume elements from XMLPullParser. | 
 |         """ | 
 |         self._close_and_return_root() | 
 |  | 
 |     def read_events(self): | 
 |         """Return an iterator over currently available (event, elem) pairs. | 
 |  | 
 |         Events are consumed from the internal event queue as they are | 
 |         retrieved from the iterator. | 
 |         """ | 
 |         events = self._events_queue | 
 |         while True: | 
 |             index = self._index | 
 |             try: | 
 |                 event = events[self._index] | 
 |                 # Avoid retaining references to past events | 
 |                 events[self._index] = None | 
 |             except IndexError: | 
 |                 break | 
 |             index += 1 | 
 |             # Compact the list in a O(1) amortized fashion | 
 |             # As noted above, _elementree.c needs a list, not a deque | 
 |             if index * 2 >= len(events): | 
 |                 events[:index] = [] | 
 |                 self._index = 0 | 
 |             else: | 
 |                 self._index = index | 
 |             if isinstance(event, Exception): | 
 |                 raise event | 
 |             else: | 
 |                 yield event | 
 |  | 
 |  | 
 | class _IterParseIterator: | 
 |  | 
 |     def __init__(self, source, events, parser, close_source=False): | 
 |         # Use the internal, undocumented _parser argument for now; When the | 
 |         # parser argument of iterparse is removed, this can be killed. | 
 |         self._parser = XMLPullParser(events=events, _parser=parser) | 
 |         self._file = source | 
 |         self._close_file = close_source | 
 |         self.root = self._root = None | 
 |  | 
 |     def __next__(self): | 
 |         while 1: | 
 |             for event in self._parser.read_events(): | 
 |                 return event | 
 |             if self._parser._parser is None: | 
 |                 self.root = self._root | 
 |                 if self._close_file: | 
 |                     self._file.close() | 
 |                 raise StopIteration | 
 |             # load event buffer | 
 |             data = self._file.read(16 * 1024) | 
 |             if data: | 
 |                 self._parser.feed(data) | 
 |             else: | 
 |                 self._root = self._parser._close_and_return_root() | 
 |  | 
 |     def __iter__(self): | 
 |         return self | 
 |  | 
 |  | 
 | def XML(text, parser=None): | 
 |     """Parse XML document from string constant. | 
 |  | 
 |     This function can be used to embed "XML Literals" in Python code. | 
 |  | 
 |     *text* is a string containing XML data, *parser* is an | 
 |     optional parser instance, defaulting to the standard XMLParser. | 
 |  | 
 |     Returns an Element instance. | 
 |  | 
 |     """ | 
 |     if not parser: | 
 |         parser = XMLParser(target=TreeBuilder()) | 
 |     parser.feed(text) | 
 |     return parser.close() | 
 |  | 
 |  | 
 | def XMLID(text, parser=None): | 
 |     """Parse XML document from string constant for its IDs. | 
 |  | 
 |     *text* is a string containing XML data, *parser* is an | 
 |     optional parser instance, defaulting to the standard XMLParser. | 
 |  | 
 |     Returns an (Element, dict) tuple, in which the | 
 |     dict maps element id:s to elements. | 
 |  | 
 |     """ | 
 |     if not parser: | 
 |         parser = XMLParser(target=TreeBuilder()) | 
 |     parser.feed(text) | 
 |     tree = parser.close() | 
 |     ids = {} | 
 |     for elem in tree.iter(): | 
 |         id = elem.get("id") | 
 |         if id: | 
 |             ids[id] = elem | 
 |     return tree, ids | 
 |  | 
 | # Parse XML document from string constant.  Alias for XML(). | 
 | fromstring = XML | 
 |  | 
 | def fromstringlist(sequence, parser=None): | 
 |     """Parse XML document from sequence of string fragments. | 
 |  | 
 |     *sequence* is a list of other sequence, *parser* is an optional parser | 
 |     instance, defaulting to the standard XMLParser. | 
 |  | 
 |     Returns an Element instance. | 
 |  | 
 |     """ | 
 |     if not parser: | 
 |         parser = XMLParser(target=TreeBuilder()) | 
 |     for text in sequence: | 
 |         parser.feed(text) | 
 |     return parser.close() | 
 |  | 
 | # -------------------------------------------------------------------- | 
 |  | 
 |  | 
 | class TreeBuilder: | 
 |     """Generic element structure builder. | 
 |  | 
 |     This builder converts a sequence of start, data, and end method | 
 |     calls to a well-formed element structure. | 
 |  | 
 |     You can use this class to build an element structure using a custom XML | 
 |     parser, or a parser for some other XML-like format. | 
 |  | 
 |     *element_factory* is an optional element factory which is called | 
 |     to create new Element instances, as necessary. | 
 |  | 
 |     """ | 
 |     def __init__(self, element_factory=None): | 
 |         self._data = [] # data collector | 
 |         self._elem = [] # element stack | 
 |         self._last = None # last element | 
 |         self._tail = None # true if we're after an end tag | 
 |         if element_factory is None: | 
 |             element_factory = Element | 
 |         self._factory = element_factory | 
 |  | 
 |     def close(self): | 
 |         """Flush builder buffers and return toplevel document Element.""" | 
 |         assert len(self._elem) == 0, "missing end tags" | 
 |         assert self._last is not None, "missing toplevel element" | 
 |         return self._last | 
 |  | 
 |     def _flush(self): | 
 |         if self._data: | 
 |             if self._last is not None: | 
 |                 text = "".join(self._data) | 
 |                 if self._tail: | 
 |                     assert self._last.tail is None, "internal error (tail)" | 
 |                     self._last.tail = text | 
 |                 else: | 
 |                     assert self._last.text is None, "internal error (text)" | 
 |                     self._last.text = text | 
 |             self._data = [] | 
 |  | 
 |     def data(self, data): | 
 |         """Add text to current element.""" | 
 |         self._data.append(data) | 
 |  | 
 |     def start(self, tag, attrs): | 
 |         """Open new element and return it. | 
 |  | 
 |         *tag* is the element name, *attrs* is a dict containing element | 
 |         attributes. | 
 |  | 
 |         """ | 
 |         self._flush() | 
 |         self._last = elem = self._factory(tag, attrs) | 
 |         if self._elem: | 
 |             self._elem[-1].append(elem) | 
 |         self._elem.append(elem) | 
 |         self._tail = 0 | 
 |         return elem | 
 |  | 
 |     def end(self, tag): | 
 |         """Close and return current Element. | 
 |  | 
 |         *tag* is the element name. | 
 |  | 
 |         """ | 
 |         self._flush() | 
 |         self._last = self._elem.pop() | 
 |         assert self._last.tag == tag,\ | 
 |                "end tag mismatch (expected %s, got %s)" % ( | 
 |                    self._last.tag, tag) | 
 |         self._tail = 1 | 
 |         return self._last | 
 |  | 
 |  | 
 | # also see ElementTree and TreeBuilder | 
 | class XMLParser: | 
 |     """Element structure builder for XML source data based on the expat parser. | 
 |  | 
 |     *html* are predefined HTML entities (not supported currently), | 
 |     *target* is an optional target object which defaults to an instance of the | 
 |     standard TreeBuilder class, *encoding* is an optional encoding string | 
 |     which if given, overrides the encoding specified in the XML file: | 
 |     http://www.iana.org/assignments/character-sets | 
 |  | 
 |     """ | 
 |  | 
 |     def __init__(self, html=0, target=None, encoding=None): | 
 |         try: | 
 |             from xml.parsers import expat | 
 |         except ImportError: | 
 |             try: | 
 |                 import pyexpat as expat | 
 |             except ImportError: | 
 |                 raise ImportError( | 
 |                     "No module named expat; use SimpleXMLTreeBuilder instead" | 
 |                     ) | 
 |         parser = expat.ParserCreate(encoding, "}") | 
 |         if target is None: | 
 |             target = TreeBuilder() | 
 |         # underscored names are provided for compatibility only | 
 |         self.parser = self._parser = parser | 
 |         self.target = self._target = target | 
 |         self._error = expat.error | 
 |         self._names = {} # name memo cache | 
 |         # main callbacks | 
 |         parser.DefaultHandlerExpand = self._default | 
 |         if hasattr(target, 'start'): | 
 |             parser.StartElementHandler = self._start | 
 |         if hasattr(target, 'end'): | 
 |             parser.EndElementHandler = self._end | 
 |         if hasattr(target, 'data'): | 
 |             parser.CharacterDataHandler = target.data | 
 |         # miscellaneous callbacks | 
 |         if hasattr(target, 'comment'): | 
 |             parser.CommentHandler = target.comment | 
 |         if hasattr(target, 'pi'): | 
 |             parser.ProcessingInstructionHandler = target.pi | 
 |         # Configure pyexpat: buffering, new-style attribute handling. | 
 |         parser.buffer_text = 1 | 
 |         parser.ordered_attributes = 1 | 
 |         parser.specified_attributes = 1 | 
 |         self._doctype = None | 
 |         self.entity = {} | 
 |         try: | 
 |             self.version = "Expat %d.%d.%d" % expat.version_info | 
 |         except AttributeError: | 
 |             pass # unknown | 
 |  | 
 |     def _setevents(self, events_queue, events_to_report): | 
 |         # Internal API for XMLPullParser | 
 |         # events_to_report: a list of events to report during parsing (same as | 
 |         # the *events* of XMLPullParser's constructor. | 
 |         # events_queue: a list of actual parsing events that will be populated | 
 |         # by the underlying parser. | 
 |         # | 
 |         parser = self._parser | 
 |         append = events_queue.append | 
 |         for event_name in events_to_report: | 
 |             if event_name == "start": | 
 |                 parser.ordered_attributes = 1 | 
 |                 parser.specified_attributes = 1 | 
 |                 def handler(tag, attrib_in, event=event_name, append=append, | 
 |                             start=self._start): | 
 |                     append((event, start(tag, attrib_in))) | 
 |                 parser.StartElementHandler = handler | 
 |             elif event_name == "end": | 
 |                 def handler(tag, event=event_name, append=append, | 
 |                             end=self._end): | 
 |                     append((event, end(tag))) | 
 |                 parser.EndElementHandler = handler | 
 |             elif event_name == "start-ns": | 
 |                 def handler(prefix, uri, event=event_name, append=append): | 
 |                     append((event, (prefix or "", uri or ""))) | 
 |                 parser.StartNamespaceDeclHandler = handler | 
 |             elif event_name == "end-ns": | 
 |                 def handler(prefix, event=event_name, append=append): | 
 |                     append((event, None)) | 
 |                 parser.EndNamespaceDeclHandler = handler | 
 |             else: | 
 |                 raise ValueError("unknown event %r" % event_name) | 
 |  | 
 |     def _raiseerror(self, value): | 
 |         err = ParseError(value) | 
 |         err.code = value.code | 
 |         err.position = value.lineno, value.offset | 
 |         raise err | 
 |  | 
 |     def _fixname(self, key): | 
 |         # expand qname, and convert name string to ascii, if possible | 
 |         try: | 
 |             name = self._names[key] | 
 |         except KeyError: | 
 |             name = key | 
 |             if "}" in name: | 
 |                 name = "{" + name | 
 |             self._names[key] = name | 
 |         return name | 
 |  | 
 |     def _start(self, tag, attr_list): | 
 |         # Handler for expat's StartElementHandler. Since ordered_attributes | 
 |         # is set, the attributes are reported as a list of alternating | 
 |         # attribute name,value. | 
 |         fixname = self._fixname | 
 |         tag = fixname(tag) | 
 |         attrib = {} | 
 |         if attr_list: | 
 |             for i in range(0, len(attr_list), 2): | 
 |                 attrib[fixname(attr_list[i])] = attr_list[i+1] | 
 |         return self.target.start(tag, attrib) | 
 |  | 
 |     def _end(self, tag): | 
 |         return self.target.end(self._fixname(tag)) | 
 |  | 
 |     def _default(self, text): | 
 |         prefix = text[:1] | 
 |         if prefix == "&": | 
 |             # deal with undefined entities | 
 |             try: | 
 |                 data_handler = self.target.data | 
 |             except AttributeError: | 
 |                 return | 
 |             try: | 
 |                 data_handler(self.entity[text[1:-1]]) | 
 |             except KeyError: | 
 |                 from xml.parsers import expat | 
 |                 err = expat.error( | 
 |                     "undefined entity %s: line %d, column %d" % | 
 |                     (text, self.parser.ErrorLineNumber, | 
 |                     self.parser.ErrorColumnNumber) | 
 |                     ) | 
 |                 err.code = 11 # XML_ERROR_UNDEFINED_ENTITY | 
 |                 err.lineno = self.parser.ErrorLineNumber | 
 |                 err.offset = self.parser.ErrorColumnNumber | 
 |                 raise err | 
 |         elif prefix == "<" and text[:9] == "<!DOCTYPE": | 
 |             self._doctype = [] # inside a doctype declaration | 
 |         elif self._doctype is not None: | 
 |             # parse doctype contents | 
 |             if prefix == ">": | 
 |                 self._doctype = None | 
 |                 return | 
 |             text = text.strip() | 
 |             if not text: | 
 |                 return | 
 |             self._doctype.append(text) | 
 |             n = len(self._doctype) | 
 |             if n > 2: | 
 |                 type = self._doctype[1] | 
 |                 if type == "PUBLIC" and n == 4: | 
 |                     name, type, pubid, system = self._doctype | 
 |                     if pubid: | 
 |                         pubid = pubid[1:-1] | 
 |                 elif type == "SYSTEM" and n == 3: | 
 |                     name, type, system = self._doctype | 
 |                     pubid = None | 
 |                 else: | 
 |                     return | 
 |                 if hasattr(self.target, "doctype"): | 
 |                     self.target.doctype(name, pubid, system[1:-1]) | 
 |                 elif self.doctype != self._XMLParser__doctype: | 
 |                     # warn about deprecated call | 
 |                     self._XMLParser__doctype(name, pubid, system[1:-1]) | 
 |                     self.doctype(name, pubid, system[1:-1]) | 
 |                 self._doctype = None | 
 |  | 
 |     def doctype(self, name, pubid, system): | 
 |         """(Deprecated)  Handle doctype declaration | 
 |  | 
 |         *name* is the Doctype name, *pubid* is the public identifier, | 
 |         and *system* is the system identifier. | 
 |  | 
 |         """ | 
 |         warnings.warn( | 
 |             "This method of XMLParser is deprecated.  Define doctype() " | 
 |             "method on the TreeBuilder target.", | 
 |             DeprecationWarning, | 
 |             ) | 
 |  | 
 |     # sentinel, if doctype is redefined in a subclass | 
 |     __doctype = doctype | 
 |  | 
 |     def feed(self, data): | 
 |         """Feed encoded data to parser.""" | 
 |         try: | 
 |             self.parser.Parse(data, 0) | 
 |         except self._error as v: | 
 |             self._raiseerror(v) | 
 |  | 
 |     def close(self): | 
 |         """Finish feeding data to parser and return element structure.""" | 
 |         try: | 
 |             self.parser.Parse("", 1) # end of data | 
 |         except self._error as v: | 
 |             self._raiseerror(v) | 
 |         try: | 
 |             close_handler = self.target.close | 
 |         except AttributeError: | 
 |             pass | 
 |         else: | 
 |             return close_handler() | 
 |         finally: | 
 |             # get rid of circular references | 
 |             del self.parser, self._parser | 
 |             del self.target, self._target | 
 |  | 
 |  | 
 | # Import the C accelerators | 
 | try: | 
 |     # Element is going to be shadowed by the C implementation. We need to keep | 
 |     # the Python version of it accessible for some "creative" by external code | 
 |     # (see tests) | 
 |     _Element_Py = Element | 
 |  | 
 |     # Element, SubElement, ParseError, TreeBuilder, XMLParser | 
 |     from _elementtree import * | 
 | except ImportError: | 
 |     pass |