xml.etree.ElementTree

Package xml :: Package etree :: Module ElementTree |
[hide private]
[frames] | no frames]

# Source Code for Module xml.etree.ElementTree

       1  #
       2  # ElementTree
       3  # $Id: ElementTree.py 3440 2008-07-18 14:45:01Z fredrik $
       4  #
       5  # light-weight XML support for Python 2.3 and later.
       6  #
       7  # history (since 1.2.6):
       8  # 2005-11-12 fl   added tostringlist/fromstringlist helpers
       9  # 2006-07-05 fl   merged in selected changes from the 1.3 sandbox
      10  # 2006-07-05 fl   removed support for 2.1 and earlier
      11  # 2007-06-21 fl   added deprecation/future warnings
      12  # 2007-08-25 fl   added doctype hook, added parser version attribute etc
      13  # 2007-08-26 fl   added new serializer code (better namespace handling, etc)
      14  # 2007-08-27 fl   warn for broken /tag searches on tree level
      15  # 2007-09-02 fl   added html/text methods to serializer (experimental)
      16  # 2007-09-05 fl   added method argument to tostring/tostringlist
      17  # 2007-09-06 fl   improved error handling
      18  # 2007-09-13 fl   added itertext, iterfind; assorted cleanups
      19  # 2007-12-15 fl   added C14N hooks, copy method (experimental)
      20  #
      21  # Copyright (c) 1999-2008 by Fredrik Lundh.  All rights reserved.
      22  #
      23  # fredrik@pythonware.com
      24  # http://www.pythonware.com
      25  #
      26  # --------------------------------------------------------------------
      27  # The ElementTree toolkit is
      28  #
      29  # Copyright (c) 1999-2008 by Fredrik Lundh
      30  #
      31  # By obtaining, using, and/or copying this software and/or its
      32  # associated documentation, you agree that you have read, understood,
      33  # and will comply with the following terms and conditions:
      34  #
      35  # Permission to use, copy, modify, and distribute this software and
      36  # its associated documentation for any purpose and without fee is
      37  # hereby granted, provided that the above copyright notice appears in
      38  # all copies, and that both that copyright notice and this permission
      39  # notice appear in supporting documentation, and that the name of
      40  # Secret Labs AB or the author not be used in advertising or publicity
      41  # pertaining to distribution of the software without specific, written
      42  # prior permission.
      43  #
      44  # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
      45  # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
      46  # ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
      47  # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
      48  # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
      49  # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
      50  # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
      51  # OF THIS SOFTWARE.
      52  # --------------------------------------------------------------------
      53
      54  # Licensed to PSF under a Contributor Agreement.
      55  # See http://www.python.org/psf/license for licensing details.
      56
      57  __all__ = [
      58      # public symbols
      59      "Comment",
      60      "dump",
      61      "Element", "ElementTree",
      62      "fromstring", "fromstringlist",
      63      "iselement", "iterparse",
      64      "parse", "ParseError",
      65      "PI", "ProcessingInstruction",
      66      "QName",
      67      "SubElement",
      68      "tostring", "tostringlist",
      69      "TreeBuilder",
      70      "VERSION",
      71      "XML",
      72      "XMLParser", "XMLTreeBuilder",
      73      ]
      74
      75  VERSION = "1.3.0"
      76
      77  ##
      78  # The <b>Element</b> type is a flexible container object, designed to
      79  # store hierarchical data structures in memory. The type can be
      80  # described as a cross between a list and a dictionary.
      81  # <p>
      82  # Each element has a number of properties associated with it:
      83  # <ul>
      84  # <li>a <i>tag</i>. This is a string identifying what kind of data
      85  # this element represents (the element type, in other words).</li>
      86  # <li>a number of <i>attributes</i>, stored in a Python dictionary.</li>
      87  # <li>a <i>text</i> string.</li>
      88  # <li>an optional <i>tail</i> string.</li>
      89  # <li>a number of <i>child elements</i>, stored in a Python sequence</li>
      90  # </ul>
      91  #
      92  # To create an element instance, use the {@link #Element} constructor
      93  # or the {@link #SubElement} factory function.
      94  # <p>
      95  # The {@link #ElementTree} class can be used to wrap an element
      96  # structure, and convert it from and to XML.
      97  ##
      98
      99  import sys
     100  import re
     101  import warnings
     102
     103
     104 -class _SimpleElementPath(object):
     105      # emulate pre-1.2 find/findtext/findall behaviour
     106 -    def find(self, element, tag, namespaces=None):
     107          for elem in element:
     108              if elem.tag == tag:
     109                  return elem
     110          return None
     111 -    def findtext(self, element, tag, default=None, namespaces=None):
     112          elem = self.find(element, tag)
     113          if elem is None:
     114              return default
     115          return elem.text or ""
     116 -    def iterfind(self, element, tag, namespaces=None):
     117          if tag[:3] == ".//":
     118              for elem in element.iter(tag[3:]):
     119                  yield elem
     120          for elem in element:
     121              if elem.tag == tag:
     122                  yield elem
     123 -    def findall(self, element, tag, namespaces=None):
     124          return list(self.iterfind(element, tag, namespaces))
     125
     126  try:
     127      from . import ElementPath
     128  except ImportError:
     129      ElementPath = _SimpleElementPath()
     130
     131  ##
     132  # Parser error.  This is a subclass of <b>SyntaxError</b>.
     133  # <p>
     134  # In addition to the exception value, an exception instance contains a
     135  # specific exception code in the <b>code</b> attribute, and the line and
     136  # column of the error in the <b>position</b> attribute.
     137
     138 -class ParseError(SyntaxError):
     139      pass
     140
     141  # --------------------------------------------------------------------
     142
     143  ##
     144  # Checks if an object appears to be a valid element object.
     145  #
     146  # @param An element instance.
     147  # @return A true value if this is an element object.
     148  # @defreturn flag
     149
     150 -def iselement(element):
     151      # FIXME: not sure about this; might be a better idea to look
     152      # for tag/attrib/text attributes
     153      return isinstance(element, Element) or hasattr(element, "tag")
     154
     155  ##
     156  # Element class.  This class defines the Element interface, and
     157  # provides a reference implementation of this interface.
     158  # <p>
     159  # The element name, attribute names, and attribute values can be
     160  # either ASCII strings (ordinary Python strings containing only 7-bit
     161  # ASCII characters) or Unicode strings.
     162  #
     163  # @param tag The element name.
     164  # @param attrib An optional dictionary, containing element attributes.
     165  # @param **extra Additional attributes, given as keyword arguments.
     166  # @see Element
     167  # @see SubElement
     168  # @see Comment
     169  # @see ProcessingInstruction
     170
     171 -class Element(object):
     172      # <tag attrib>text<child/>...</tag>tail
     173
     174      ##
     175      # (Attribute) Element tag.
     176
     177      tag = None
     178
     179      ##
     180      # (Attribute) Element attribute dictionary.  Where possible, use
     181      # {@link #Element.get},
     182      # {@link #Element.set},
     183      # {@link #Element.keys}, and
     184      # {@link #Element.items} to access
     185      # element attributes.
     186
     187      attrib = None
     188
     189      ##
     190      # (Attribute) Text before first subelement.  This is either a
     191      # string or the value None.  Note that if there was no text, this
     192      # attribute may be either None or an empty string, depending on
     193      # the parser.
     194
     195      text = None
     196
     197      ##
     198      # (Attribute) Text after this element's end tag, but before the
     199      # next sibling element's start tag.  This is either a string or
     200      # the value None.  Note that if there was no text, this attribute
     201      # may be either None or an empty string, depending on the parser.
     202
     203      tail = None # text after end tag, if any
     204
     205      # constructor
     206
     207 -    def __init__(self, tag, attrib={}, **extra):
     208          attrib = attrib.copy()
     209          attrib.update(extra)
     210          self.tag = tag
     211          self.attrib = attrib
     212          self._children = []
     213
     214 -    def __repr__(self):
     215          return "<Element %s at 0x%x>" % (repr(self.tag), id(self))
     216
     217      ##
     218      # Creates a new element object of the same type as this element.
     219      #
     220      # @param tag Element tag.
     221      # @param attrib Element attributes, given as a dictionary.
     222      # @return A new element instance.
     223
     224 -    def makeelement(self, tag, attrib):
     225          return self.__class__(tag, attrib)
     226
     227      ##
     228      # (Experimental) Copies the current element.  This creates a
     229      # shallow copy; subelements will be shared with the original tree.
     230      #
     231      # @return A new element instance.
     232
     233 -    def copy(self):
     234          elem = self.makeelement(self.tag, self.attrib)
     235          elem.text = self.text
     236          elem.tail = self.tail
     237          elem[:] = self
     238          return elem
     239
     240      ##
     241      # Returns the number of subelements.  Note that this only counts
     242      # full elements; to check if there's any content in an element, you
     243      # have to check both the length and the <b>text</b> attribute.
     244      #
     245      # @return The number of subelements.
     246
     247 -    def __len__(self):
     248          return len(self._children)
     249
     250 -    def __nonzero__(self):
     251          warnings.warn(
     252              "The behavior of this method will change in future versions.  "
     253              "Use specific 'len(elem)' or 'elem is not None' test instead.",
     254              FutureWarning, stacklevel=2
     255              )
     256          return len(self._children) != 0 # emulate old behaviour, for now
     257
     258      ##
     259      # Returns the given subelement, by index.
     260      #
     261      # @param index What subelement to return.
     262      # @return The given subelement.
     263      # @exception IndexError If the given element does not exist.
     264
     265 -    def __getitem__(self, index):
     266          return self._children[index]
     267
     268      ##
     269      # Replaces the given subelement, by index.
     270      #
     271      # @param index What subelement to replace.
     272      # @param element The new element value.
     273      # @exception IndexError If the given element does not exist.
     274
     275 -    def __setitem__(self, index, element):
     276          # if isinstance(index, slice):
     277          #     for elt in element:
     278          #         assert iselement(elt)
     279          # else:
     280          #     assert iselement(element)
     281          self._children[index] = element
     282
     283      ##
     284      # Deletes the given subelement, by index.
     285      #
     286      # @param index What subelement to delete.
     287      # @exception IndexError If the given element does not exist.
     288
     289 -    def __delitem__(self, index):
     290          del self._children[index]
     291
     292      ##
     293      # Adds a subelement to the end of this element.  In document order,
     294      # the new element will appear after the last existing subelement (or
     295      # directly after the text, if it's the first subelement), but before
     296      # the end tag for this element.
     297      #
     298      # @param element The element to add.
     299
     300 -    def append(self, element):
     301          # assert iselement(element)
     302          self._children.append(element)
     303
     304      ##
     305      # Appends subelements from a sequence.
     306      #
     307      # @param elements A sequence object with zero or more elements.
     308      # @since 1.3
     309
     310 -    def extend(self, elements):
     311          # for element in elements:
     312          #     assert iselement(element)
     313          self._children.extend(elements)
     314
     315      ##
     316      # Inserts a subelement at the given position in this element.
     317      #
     318      # @param index Where to insert the new subelement.
     319
     320 -    def insert(self, index, element):
     321          # assert iselement(element)
     322          self._children.insert(index, element)
     323
     324      ##
     325      # Removes a matching subelement.  Unlike the <b>find</b> methods,
     326      # this method compares elements based on identity, not on tag
     327      # value or contents.  To remove subelements by other means, the
     328      # easiest way is often to use a list comprehension to select what
     329      # elements to keep, and use slice assignment to update the parent
     330      # element.
     331      #
     332      # @param element What element to remove.
     333      # @exception ValueError If a matching element could not be found.
     334
     335 -    def remove(self, element):
     336          # assert iselement(element)
     337          self._children.remove(element)
     338
     339      ##
     340      # (Deprecated) Returns all subelements.  The elements are returned
     341      # in document order.
     342      #
     343      # @return A list of subelements.
     344      # @defreturn list of Element instances
     345
     346 -    def getchildren(self):
     347          warnings.warn(
     348              "This method will be removed in future versions.  "
     349              "Use 'list(elem)' or iteration over elem instead.",
     350              DeprecationWarning, stacklevel=2
     351              )
     352          return self._children
     353
     354      ##
     355      # Finds the first matching subelement, by tag name or path.
     356      #
     357      # @param path What element to look for.
     358      # @keyparam namespaces Optional namespace prefix map.
     359      # @return The first matching element, or None if no element was found.
     360      # @defreturn Element or None
     361
     362 -    def find(self, path, namespaces=None):
     363          return ElementPath.find(self, path, namespaces)
     364
     365      ##
     366      # Finds text for the first matching subelement, by tag name or path.
     367      #
     368      # @param path What element to look for.
     369      # @param default What to return if the element was not found.
     370      # @keyparam namespaces Optional namespace prefix map.
     371      # @return The text content of the first matching element, or the
     372      #     default value no element was found.  Note that if the element
     373      #     is found, but has no text content, this method returns an
     374      #     empty string.
     375      # @defreturn string
     376
     377 -    def findtext(self, path, default=None, namespaces=None):
     378          return ElementPath.findtext(self, path, default, namespaces)
     379
     380      ##
     381      # Finds all matching subelements, by tag name or path.
     382      #
     383      # @param path What element to look for.
     384      # @keyparam namespaces Optional namespace prefix map.
     385      # @return A list or other sequence containing all matching elements,
     386      #    in document order.
     387      # @defreturn list of Element instances
     388
     389 -    def findall(self, path, namespaces=None):
     390          return ElementPath.findall(self, path, namespaces)
     391
     392      ##
     393      # Finds all matching subelements, by tag name or path.
     394      #
     395      # @param path What element to look for.
     396      # @keyparam namespaces Optional namespace prefix map.
     397      # @return An iterator or sequence containing all matching elements,
     398      #    in document order.
     399      # @defreturn a generated sequence of Element instances
     400
     401 -    def iterfind(self, path, namespaces=None):
     402          return ElementPath.iterfind(self, path, namespaces)
     403
     404      ##
     405      # Resets an element.  This function removes all subelements, clears
     406      # all attributes, and sets the <b>text</b> and <b>tail</b> attributes
     407      # to None.
     408
     409 -    def clear(self):
     410          self.attrib.clear()
     411          self._children = []
     412          self.text = self.tail = None
     413
     414      ##
     415      # Gets an element attribute.  Equivalent to <b>attrib.get</b>, but
     416      # some implementations may handle this a bit more efficiently.
     417      #
     418      # @param key What attribute to look for.
     419      # @param default What to return if the attribute was not found.
     420      # @return The attribute value, or the default value, if the
     421      #     attribute was not found.
     422      # @defreturn string or None
     423
     424 -    def get(self, key, default=None):
     425          return self.attrib.get(key, default)
     426
     427      ##
     428      # Sets an element attribute.  Equivalent to <b>attrib[key] = value</b>,
     429      # but some implementations may handle this a bit more efficiently.
     430      #
     431      # @param key What attribute to set.
     432      # @param value The attribute value.
     433
     434 -    def set(self, key, value):
     435          self.attrib[key] = value
     436
     437      ##
     438      # Gets a list of attribute names.  The names are returned in an
     439      # arbitrary order (just like for an ordinary Python dictionary).
     440      # Equivalent to <b>attrib.keys()</b>.
     441      #
     442      # @return A list of element attribute names.
     443      # @defreturn list of strings
     444
     445 -    def keys(self):
     446          return self.attrib.keys()
     447
     448      ##
     449      # Gets element attributes, as a sequence.  The attributes are
     450      # returned in an arbitrary order.  Equivalent to <b>attrib.items()</b>.
     451      #
     452      # @return A list of (name, value) tuples for all attributes.
     453      # @defreturn list of (string, string) tuples
     454
     455 -    def items(self):
     456          return self.attrib.items()
     457
     458      ##
     459      # Creates a tree iterator.  The iterator loops over this element
     460      # and all subelements, in document order, and returns all elements
     461      # with a matching tag.
     462      # <p>
     463      # If the tree structure is modified during iteration, new or removed
     464      # elements may or may not be included.  To get a stable set, use the
     465      # list() function on the iterator, and loop over the resulting list.
     466      #
     467      # @param tag What tags to look for (default is to return all elements).
     468      # @return An iterator containing all the matching elements.
     469      # @defreturn iterator
     470
     471 -    def iter(self, tag=None):
     472          if tag == "*":
     473              tag = None
     474          if tag is None or self.tag == tag:
     475              yield self
     476          for e in self._children:
     477              for e in e.iter(tag):
     478                  yield e
     479
     480      # compatibility
     481 -    def getiterator(self, tag=None):
     482          # Change for a DeprecationWarning in 1.4
     483          warnings.warn(
     484              "This method will be removed in future versions.  "
     485              "Use 'elem.iter()' or 'list(elem.iter())' instead.",
     486              PendingDeprecationWarning, stacklevel=2
     487          )
     488          return list(self.iter(tag))
     489
     490      ##
     491      # Creates a text iterator.  The iterator loops over this element
     492      # and all subelements, in document order, and returns all inner
     493      # text.
     494      #
     495      # @return An iterator containing all inner text.
     496      # @defreturn iterator
     497
     498 -    def itertext(self):
     499          tag = self.tag
     500          if not isinstance(tag, basestring) and tag is not None:
     501              return
     502          if self.text:
     503              yield self.text
     504          for e in self:
     505              for s in e.itertext():
     506                  yield s
     507              if e.tail:
     508                  yield e.tail
     509
     510  # compatibility
     511  _Element = _ElementInterface = Element
     512
     513  ##
     514  # Subelement factory.  This function creates an element instance, and
     515  # appends it to an existing element.
     516  # <p>
     517  # The element name, attribute names, and attribute values can be
     518  # either 8-bit ASCII strings or Unicode strings.
     519  #
     520  # @param parent The parent element.
     521  # @param tag The subelement name.
     522  # @param attrib An optional dictionary, containing element attributes.
     523  # @param **extra Additional attributes, given as keyword arguments.
     524  # @return An element instance.
     525  # @defreturn Element
     526
     527 -def SubElement(parent, tag, attrib={}, **extra):
     528      attrib = attrib.copy()
     529      attrib.update(extra)
     530      element = parent.makeelement(tag, attrib)
     531      parent.append(element)
     532      return element
     533
     534  ##
     535  # Comment element factory.  This factory function creates a special
     536  # element that will be serialized as an XML comment by the standard
     537  # serializer.
     538  # <p>
     539  # The comment string can be either an 8-bit ASCII string or a Unicode
     540  # string.
     541  #
     542  # @param text A string containing the comment string.
     543  # @return An element instance, representing a comment.
     544  # @defreturn Element
     545
     546 -def Comment(text=None):
     547      element = Element(Comment)
     548      element.text = text
     549      return element
     550
     551  ##
     552  # PI element factory.  This factory function creates a special element
     553  # that will be serialized as an XML processing instruction by the standard
     554  # serializer.
     555  #
     556  # @param target A string containing the PI target.
     557  # @param text A string containing the PI contents, if any.
     558  # @return An element instance, representing a PI.
     559  # @defreturn Element
     560
     561 -def ProcessingInstruction(target, text=None):
     562      element = Element(ProcessingInstruction)
     563      element.text = target
     564      if text:
     565          element.text = element.text + " " + text
     566      return element
     567
     568  PI = ProcessingInstruction
     569
     570  ##
     571  # QName wrapper.  This can be used to wrap a QName attribute value, in
     572  # order to get proper namespace handling on output.
     573  #
     574  # @param text A string containing the QName value, in the form {uri}local,
     575  #     or, if the tag argument is given, the URI part of a QName.
     576  # @param tag Optional tag.  If given, the first argument is interpreted as
     577  #     a URI, and this argument is interpreted as a local name.
     578  # @return An opaque object, representing the QName.
     579
     580 -class QName(object):
     581 -    def __init__(self, text_or_uri, tag=None):
     582          if tag:
     583              text_or_uri = "{%s}%s" % (text_or_uri, tag)
     584          self.text = text_or_uri
     585 -    def __str__(self):
     586          return self.text
     587 -    def __hash__(self):
     588          return hash(self.text)
     589 -    def __cmp__(self, other):
     590          if isinstance(other, QName):
     591              return cmp(self.text, other.text)
     592          return cmp(self.text, other)
     593
     594  # --------------------------------------------------------------------
     595
     596  ##
     597  # ElementTree wrapper class.  This class represents an entire element
     598  # hierarchy, and adds some extra support for serialization to and from
     599  # standard XML.
     600  #
     601  # @param element Optional root element.
     602  # @keyparam file Optional file handle or file name.  If given, the
     603  #     tree is initialized with the contents of this XML file.
     604
     605 -class ElementTree(object):
     606
     607 -    def __init__(self, element=None, file=None):
     608          # assert element is None or iselement(element)
     609          self._root = element # first node
     610          if file:
     611              self.parse(file)
     612
     613      ##
     614      # Gets the root element for this tree.
     615      #
     616      # @return An element instance.
     617      # @defreturn Element
     618
     619 -    def getroot(self):
     620          return self._root
     621
     622      ##
     623      # Replaces the root element for this tree.  This discards the
     624      # current contents of the tree, and replaces it with the given
     625      # element.  Use with care.
     626      #
     627      # @param element An element instance.
     628
     629 -    def _setroot(self, element):
     630          # assert iselement(element)
     631          self._root = element
     632
     633      ##
     634      # Loads an external XML document into this element tree.
     635      #
     636      # @param source A file name or file object.  If a file object is
     637      #     given, it only has to implement a <b>read(n)</b> method.
     638      # @keyparam parser An optional parser instance.  If not given, the
     639      #     standard {@link XMLParser} parser is used.
     640      # @return The document root element.
     641      # @defreturn Element
     642      # @exception ParseError If the parser fails to parse the document.
     643
     644 -    def parse(self, source, parser=None):
     645          close_source = False
     646          if not hasattr(source, "read"):
     647              source = open(source, "rb")
     648              close_source = True
     649          try:
     650              if not parser:
     651                  parser = XMLParser(target=TreeBuilder())
     652              while 1:
     653                  data = source.read(65536)
     654                  if not data:
     655                      break
     656                  parser.feed(data)
     657              self._root = parser.close()
     658              return self._root
     659          finally:
     660              if close_source:
     661                  source.close()
     662
     663      ##
     664      # Creates a tree iterator for the root element.  The iterator loops
     665      # over all elements in this tree, in document order.
     666      #
     667      # @param tag What tags to look for (default is to return all elements)
     668      # @return An iterator.
     669      # @defreturn iterator
     670
     671 -    def iter(self, tag=None):
     672          # assert self._root is not None
     673          return self._root.iter(tag)
     674
     675      # compatibility
     676 -    def getiterator(self, tag=None):
     677          # Change for a DeprecationWarning in 1.4
     678          warnings.warn(
     679              "This method will be removed in future versions.  "
     680              "Use 'tree.iter()' or 'list(tree.iter())' instead.",
     681              PendingDeprecationWarning, stacklevel=2
     682          )
     683          return list(self.iter(tag))
     684
     685      ##
     686      # Same as getroot().find(path), starting at the root of the
     687      # tree.
     688      #
     689      # @param path What element to look for.
     690      # @keyparam namespaces Optional namespace prefix map.
     691      # @return The first matching element, or None if no element was found.
     692      # @defreturn Element or None
     693
     694 -    def find(self, path, namespaces=None):
     695          # assert self._root is not None
     696          if path[:1] == "/":
     697              path = "." + path
     698              warnings.warn(
     699                  "This search is broken in 1.3 and earlier, and will be "
     700                  "fixed in a future version.  If you rely on the current "
     701                  "behaviour, change it to %r" % path,
     702                  FutureWarning, stacklevel=2
     703                  )
     704          return self._root.find(path, namespaces)
     705
     706      ##
     707      # Same as getroot().findtext(path), starting at the root of the tree.
     708      #
     709      # @param path What element to look for.
     710      # @param default What to return if the element was not found.
     711      # @keyparam namespaces Optional namespace prefix map.
     712      # @return The text content of the first matching element, or the
     713      #     default value no element was found.  Note that if the element
     714      #     is found, but has no text content, this method returns an
     715      #     empty string.
     716      # @defreturn string
     717
     718 -    def findtext(self, path, default=None, namespaces=None):
     719          # assert self._root is not None
     720          if path[:1] == "/":
     721              path = "." + path
     722              warnings.warn(
     723                  "This search is broken in 1.3 and earlier, and will be "
     724                  "fixed in a future version.  If you rely on the current "
     725                  "behaviour, change it to %r" % path,
     726                  FutureWarning, stacklevel=2
     727                  )
     728          return self._root.findtext(path, default, namespaces)
     729
     730      ##
     731      # Same as getroot().findall(path), starting at the root of the tree.
     732      #
     733      # @param path What element to look for.
     734      # @keyparam namespaces Optional namespace prefix map.
     735      # @return A list or iterator containing all matching elements,
     736      #    in document order.
     737      # @defreturn list of Element instances
     738
     739 -    def findall(self, path, namespaces=None):
     740          # assert self._root is not None
     741          if path[:1] == "/":
     742              path = "." + path
     743              warnings.warn(
     744                  "This search is broken in 1.3 and earlier, and will be "
     745                  "fixed in a future version.  If you rely on the current "
     746                  "behaviour, change it to %r" % path,
     747                  FutureWarning, stacklevel=2
     748                  )
     749          return self._root.findall(path, namespaces)
     750
     751      ##
     752      # Finds all matching subelements, by tag name or path.
     753      # Same as getroot().iterfind(path).
     754      #
     755      # @param path What element to look for.
     756      # @keyparam namespaces Optional namespace prefix map.
     757      # @return An iterator or sequence containing all matching elements,
     758      #    in document order.
     759      # @defreturn a generated sequence of Element instances
     760
     761 -    def iterfind(self, path, namespaces=None):
     762          # assert self._root is not None
     763          if path[:1] == "/":
     764              path = "." + path
     765              warnings.warn(
     766                  "This search is broken in 1.3 and earlier, and will be "
     767                  "fixed in a future version.  If you rely on the current "
     768                  "behaviour, change it to %r" % path,
     769                  FutureWarning, stacklevel=2
     770                  )
     771          return self._root.iterfind(path, namespaces)
     772
     773      ##
     774      # Writes the element tree to a file, as XML.
     775      #
     776      # @def write(file, **options)
     777      # @param file A file name, or a file object opened for writing.
     778      # @param **options Options, given as keyword arguments.
     779      # @keyparam encoding Optional output encoding (default is US-ASCII).
     780      # @keyparam xml_declaration Controls if an XML declaration should
     781      #     be added to the file.  Use False for never, True for always,
     782      #     None for only if not US-ASCII or UTF-8.  None is default.
     783      # @keyparam default_namespace Sets the default XML namespace (for "xmlns").
     784      # @keyparam method Optional output method ("xml", "html", "text" or
     785      #     "c14n"; default is "xml").
     786
     787 -    def write(self, file_or_filename,
     788                # keyword arguments
     789                encoding=None,
     790                xml_declaration=None,
     791                default_namespace=None,
     792                method=None):
     793          # assert self._root is not None
     794          if not method:
     795              method = "xml"
     796          elif method not in _serialize:
     797              # FIXME: raise an ImportError for c14n if ElementC14N is missing?
     798              raise ValueError("unknown method %r" % method)
     799          if hasattr(file_or_filename, "write"):
     800              file = file_or_filename
     801          else:
     802              file = open(file_or_filename, "wb")
     803          write = file.write
     804          if not encoding:
     805              if method == "c14n":
     806                  encoding = "utf-8"
     807              else:
     808                  encoding = "us-ascii"
     809          elif xml_declaration or (xml_declaration is None and
     810                                   encoding not in ("utf-8", "us-ascii")):
     811              if method == "xml":
     812                  write("<?xml version='1.0' encoding='%s'?>\n" % encoding)
     813          if method == "text":
     814              _serialize_text(write, self._root, encoding)
     815          else:
     816              qnames, namespaces = _namespaces(
     817                  self._root, encoding, default_namespace
     818                  )
     819              serialize = _serialize[method]
     820              serialize(write, self._root, encoding, qnames, namespaces)
     821          if file_or_filename is not file:
     822              file.close()
     823
     824 -    def write_c14n(self, file):
     825          # lxml.etree compatibility.  use output method instead
     826          return self.write(file, method="c14n")
     827
     828  # --------------------------------------------------------------------
     829  # serialization support
     830
     831 -def _namespaces(elem, encoding, default_namespace=None):
     832      # identify namespaces used in this tree
     833
     834      # maps qnames to *encoded* prefix:local names
     835      qnames = {None: None}
     836
     837      # maps uri:s to prefixes
     838      namespaces = {}
     839      if default_namespace:
     840          namespaces[default_namespace] = ""
     841
     842      def encode(text):
     843          return text.encode(encoding)
     844
     845      def add_qname(qname):
     846          # calculate serialized qname representation
     847          try:
     848              if qname[:1] == "{":
     849                  uri, tag = qname[1:].rsplit("}", 1)
     850                  prefix = namespaces.get(uri)
     851                  if prefix is None:
     852                      prefix = _namespace_map.get(uri)
     853                      if prefix is None:
     854                          prefix = "ns%d" % len(namespaces)
     855                      if prefix != "xml":
     856                          namespaces[uri] = prefix
     857                  if prefix:
     858                      qnames[qname] = encode("%s:%s" % (prefix, tag))
     859                  else:
     860                      qnames[qname] = encode(tag) # default element
     861              else:
     862                  if default_namespace:
     863                      # FIXME: can this be handled in XML 1.0?
     864                      raise ValueError(
     865                          "cannot use non-qualified names with "
     866                          "default_namespace option"
     867                          )
     868                  qnames[qname] = encode(qname)
     869          except TypeError:
     870              _raise_serialization_error(qname)
     871
     872      # populate qname and namespaces table
     873      try:
     874          iterate = elem.iter
     875      except AttributeError:
     876          iterate = elem.getiterator # cET compatibility
     877      for elem in iterate():
     878          tag = elem.tag
     879          if isinstance(tag, QName):
     880              if tag.text not in qnames:
     881                  add_qname(tag.text)
     882          elif isinstance(tag, basestring):
     883              if tag not in qnames:
     884                  add_qname(tag)
     885          elif tag is not None and tag is not Comment and tag is not PI:
     886              _raise_serialization_error(tag)
     887          for key, value in elem.items():
     888              if isinstance(key, QName):
     889                  key = key.text
     890              if key not in qnames:
     891                  add_qname(key)
     892              if isinstance(value, QName) and value.text not in qnames:
     893                  add_qname(value.text)
     894          text = elem.text
     895          if isinstance(text, QName) and text.text not in qnames:
     896              add_qname(text.text)
     897      return qnames, namespaces
     898
     899 -def _serialize_xml(write, elem, encoding, qnames, namespaces):
     900      tag = elem.tag
     901      text = elem.text
     902      if tag is Comment:
     903          write("<!--%s-->" % _encode(text, encoding))
     904      elif tag is ProcessingInstruction:
     905          write("<?%s?>" % _encode(text, encoding))
     906      else:
     907          tag = qnames[tag]
     908          if tag is None:
     909              if text:
     910                  write(_escape_cdata(text, encoding))
     911              for e in elem:
     912                  _serialize_xml(write, e, encoding, qnames, None)
     913          else:
     914              write("<" + tag)
     915              items = elem.items()
     916              if items or namespaces:
     917                  if namespaces:
     918                      for v, k in sorted(namespaces.items(),
     919                                         key=lambda x: x[1]):  # sort on prefix
     920                          if k:
     921                              k = ":" + k
     922                          write(" xmlns%s=\"%s\"" % (
     923                              k.encode(encoding),
     924                              _escape_attrib(v, encoding)
     925                              ))
     926                  for k, v in sorted(items):  # lexical order
     927                      if isinstance(k, QName):
     928                          k = k.text
     929                      if isinstance(v, QName):
     930                          v = qnames[v.text]
     931                      else:
     932                          v = _escape_attrib(v, encoding)
     933                      write(" %s=\"%s\"" % (qnames[k], v))
     934              if text or len(elem):
     935                  write(">")
     936                  if text:
     937                      write(_escape_cdata(text, encoding))
     938                  for e in elem:
     939                      _serialize_xml(write, e, encoding, qnames, None)
     940                  write("</" + tag + ">")
     941              else:
     942                  write(" />")
     943      if elem.tail:
     944          write(_escape_cdata(elem.tail, encoding))
     945
     946  HTML_EMPTY = ("area", "base", "basefont", "br", "col", "frame", "hr",
     947                "img", "input", "isindex", "link", "meta", "param")
     948
     949  try:
     950      HTML_EMPTY = set(HTML_EMPTY)
     951  except NameError:
     952      pass
     953
     954 -def _serialize_html(write, elem, encoding, qnames, namespaces):
     955      tag = elem.tag
     956      text = elem.text
     957      if tag is Comment:
     958          write("<!--%s-->" % _escape_cdata(text, encoding))
     959      elif tag is ProcessingInstruction:
     960          write("<?%s?>" % _escape_cdata(text, encoding))
     961      else:
     962          tag = qnames[tag]
     963          if tag is None:
     964              if text:
     965                  write(_escape_cdata(text, encoding))
     966              for e in elem:
     967                  _serialize_html(write, e, encoding, qnames, None)
     968          else:
     969              write("<" + tag)
     970              items = elem.items()
     971              if items or namespaces:
     972                  if namespaces:
     973                      for v, k in sorted(namespaces.items(),
     974                                         key=lambda x: x[1]):  # sort on prefix
     975                          if k:
     976                              k = ":" + k
     977                          write(" xmlns%s=\"%s\"" % (
     978                              k.encode(encoding),
     979                              _escape_attrib(v, encoding)
     980                              ))
     981                  for k, v in sorted(items):  # lexical order
     982                      if isinstance(k, QName):
     983                          k = k.text
     984                      if isinstance(v, QName):
     985                          v = qnames[v.text]
     986                      else:
     987                          v = _escape_attrib_html(v, encoding)
     988                      # FIXME: handle boolean attributes
     989                      write(" %s=\"%s\"" % (qnames[k], v))
     990              write(">")
     991              ltag = tag.lower()
     992              if text:
     993                  if ltag == "script" or ltag == "style":
     994                      write(_encode(text, encoding))
     995                  else:
     996                      write(_escape_cdata(text, encoding))
     997              for e in elem:
     998                  _serialize_html(write, e, encoding, qnames, None)
     999              if ltag not in HTML_EMPTY:
    1000                  write("</" + tag + ">")
    1001      if elem.tail:
    1002          write(_escape_cdata(elem.tail, encoding))
    1003
    1004 -def _serialize_text(write, elem, encoding):
    1005      for part in elem.itertext():
    1006          write(part.encode(encoding))
    1007      if elem.tail:
    1008          write(elem.tail.encode(encoding))
    1009
    1010  _serialize = {
    1011      "xml": _serialize_xml,
    1012      "html": _serialize_html,
    1013      "text": _serialize_text,
    1014  # this optional method is imported at the end of the module
    1015  #   "c14n": _serialize_c14n,
    1016  }
    1017
    1018  ##
    1019  # Registers a namespace prefix.  The registry is global, and any
    1020  # existing mapping for either the given prefix or the namespace URI
    1021  # will be removed.
    1022  #
    1023  # @param prefix Namespace prefix.
    1024  # @param uri Namespace uri.  Tags and attributes in this namespace
    1025  #     will be serialized with the given prefix, if at all possible.
    1026  # @exception ValueError If the prefix is reserved, or is otherwise
    1027  #     invalid.
    1028
    1029 -def register_namespace(prefix, uri):
    1030      if re.match("ns\d+$", prefix):
    1031          raise ValueError("Prefix format reserved for internal use")
    1032      for k, v in _namespace_map.items():
    1033          if k == uri or v == prefix:
    1034              del _namespace_map[k]
    1035      _namespace_map[uri] = prefix
    1036
    1037  _namespace_map = {
    1038      # "well-known" namespace prefixes
    1039      "http://www.w3.org/XML/1998/namespace": "xml",
    1040      "http://www.w3.org/1999/xhtml": "html",
    1041      "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf",
    1042      "http://schemas.xmlsoap.org/wsdl/": "wsdl",
    1043      # xml schema
    1044      "http://www.w3.org/2001/XMLSchema": "xs",
    1045      "http://www.w3.org/2001/XMLSchema-instance": "xsi",
    1046      # dublin core
    1047      "http://purl.org/dc/elements/1.1/": "dc",
    1048  }
    1049
    1050 -def _raise_serialization_error(text):
    1051      raise TypeError(
    1052          "cannot serialize %r (type %s)" % (text, type(text).__name__)
    1053          )
    1054
    1055 -def _encode(text, encoding):
    1056      try:
    1057          return text.encode(encoding, "xmlcharrefreplace")
    1058      except (TypeError, AttributeError):
    1059          _raise_serialization_error(text)
    1060
    1061 -def _escape_cdata(text, encoding):
    1062      # escape character data
    1063      try:
    1064          # it's worth avoiding do-nothing calls for strings that are
    1065          # shorter than 500 character, or so.  assume that's, by far,
    1066          # the most common case in most applications.
    1067          if "&" in text:
    1068              text = text.replace("&", "&amp;")
    1069          if "<" in text:
    1070              text = text.replace("<", "&lt;")
    1071          if ">" in text:
    1072              text = text.replace(">", "&gt;")
    1073          return text.encode(encoding, "xmlcharrefreplace")
    1074      except (TypeError, AttributeError):
    1075          _raise_serialization_error(text)
    1076
    1077 -def _escape_attrib(text, encoding):
    1078      # escape attribute value
    1079      try:
    1080          if "&" in text:
    1081              text = text.replace("&", "&amp;")
    1082          if "<" in text:
    1083              text = text.replace("<", "&lt;")
    1084          if ">" in text:
    1085              text = text.replace(">", "&gt;")
    1086          if "\"" in text:
    1087              text = text.replace("\"", "&quot;")
    1088          if "\n" in text:
    1089              text = text.replace("\n", "&#10;")
    1090          return text.encode(encoding, "xmlcharrefreplace")
    1091      except (TypeError, AttributeError):
    1092          _raise_serialization_error(text)
    1093
    1094 -def _escape_attrib_html(text, encoding):
    1095      # escape attribute value
    1096      try:
    1097          if "&" in text:
    1098              text = text.replace("&", "&amp;")
    1099          if ">" in text:
    1100              text = text.replace(">", "&gt;")
    1101          if "\"" in text:
    1102              text = text.replace("\"", "&quot;")
    1103          return text.encode(encoding, "xmlcharrefreplace")
    1104      except (TypeError, AttributeError):
    1105          _raise_serialization_error(text)
    1106
    1107  # --------------------------------------------------------------------
    1108
    1109  ##
    1110  # Generates a string representation of an XML element, including all
    1111  # subelements.
    1112  #
    1113  # @param element An Element instance.
    1114  # @keyparam encoding Optional output encoding (default is US-ASCII).
    1115  # @keyparam method Optional output method ("xml", "html", "text" or
    1116  #     "c14n"; default is "xml").
    1117  # @return An encoded string containing the XML data.
    1118  # @defreturn string
    1119
    1120 -def tostring(element, encoding=None, method=None):
    1121      class dummy:
    1122          pass
    1123      data = []
    1124      file = dummy()
    1125      file.write = data.append
    1126      ElementTree(element).write(file, encoding, method=method)
    1127      return "".join(data)
    1128
    1129  ##
    1130  # Generates a string representation of an XML element, including all
    1131  # subelements.  The string is returned as a sequence of string fragments.
    1132  #
    1133  # @param element An Element instance.
    1134  # @keyparam encoding Optional output encoding (default is US-ASCII).
    1135  # @keyparam method Optional output method ("xml", "html", "text" or
    1136  #     "c14n"; default is "xml").
    1137  # @return A sequence object containing the XML data.
    1138  # @defreturn sequence
    1139  # @since 1.3
    1140
    1141 -def tostringlist(element, encoding=None, method=None):
    1142      class dummy:
    1143          pass
    1144      data = []
    1145      file = dummy()
    1146      file.write = data.append
    1147      ElementTree(element).write(file, encoding, method=method)
    1148      # FIXME: merge small fragments into larger parts
    1149      return data
    1150
    1151  ##
    1152  # Writes an element tree or element structure to sys.stdout.  This
    1153  # function should be used for debugging only.
    1154  # <p>
    1155  # The exact output format is implementation dependent.  In this
    1156  # version, it's written as an ordinary XML file.
    1157  #
    1158  # @param elem An element tree or an individual element.
    1159
    1160 -def dump(elem):
    1161      # debugging
    1162      if not isinstance(elem, ElementTree):
    1163          elem = ElementTree(elem)
    1164      elem.write(sys.stdout)
    1165      tail = elem.getroot().tail
    1166      if not tail or tail[-1] != "\n":
    1167          sys.stdout.write("\n")
    1168
    1169  # --------------------------------------------------------------------
    1170  # parsing
    1171
    1172  ##
    1173  # Parses an XML document into an element tree.
    1174  #
    1175  # @param source A filename or file object containing XML data.
    1176  # @param parser An optional parser instance.  If not given, the
    1177  #     standard {@link XMLParser} parser is used.
    1178  # @return An ElementTree instance
    1179
    1180 -def parse(source, parser=None):
    1181      tree = ElementTree()
    1182      tree.parse(source, parser)
    1183      return tree
    1184
    1185  ##
    1186  # Parses an XML document into an element tree incrementally, and reports
    1187  # what's going on to the user.
    1188  #
    1189  # @param source A filename or file object containing XML data.
    1190  # @param events A list of events to report back.  If omitted, only "end"
    1191  #     events are reported.
    1192  # @param parser An optional parser instance.  If not given, the
    1193  #     standard {@link XMLParser} parser is used.
    1194  # @return A (event, elem) iterator.
    1195
    1196 -def iterparse(source, events=None, parser=None):
    1197      close_source = False
    1198      if not hasattr(source, "read"):
    1199          source = open(source, "rb")
    1200          close_source = True
    1201      try:
    1202          if not parser:
    1203              parser = XMLParser(target=TreeBuilder())
    1204          return _IterParseIterator(source, events, parser, close_source)
    1205      except:
    1206          if close_source:
    1207              source.close()
    1208          raise
    1209
    1210 -class _IterParseIterator(object):
    1211
    1212 -    def __init__(self, source, events, parser, close_source=False):
    1213          self._file = source
    1214          self._close_file = close_source
    1215          self._events = []
    1216          self._index = 0
    1217          self._error = None
    1218          self.root = self._root = None
    1219          self._parser = parser
    1220          # wire up the parser for event reporting
    1221          parser = self._parser._parser
    1222          append = self._events.append
    1223          if events is None:
    1224              events = ["end"]
    1225          for event in events:
    1226              if event == "start":
    1227                  try:
    1228                      parser.ordered_attributes = 1
    1229                      parser.specified_attributes = 1
    1230                      def handler(tag, attrib_in, event=event, append=append,
    1231                                  start=self._parser._start_list):
    1232                          append((event, start(tag, attrib_in)))
    1233                      parser.StartElementHandler = handler
    1234                  except AttributeError:
    1235                      def handler(tag, attrib_in, event=event, append=append,
    1236                                  start=self._parser._start):
    1237                          append((event, start(tag, attrib_in)))
    1238                      parser.StartElementHandler = handler
    1239              elif event == "end":
    1240                  def handler(tag, event=event, append=append,
    1241                              end=self._parser._end):
    1242                      append((event, end(tag)))
    1243                  parser.EndElementHandler = handler
    1244              elif event == "start-ns":
    1245                  def handler(prefix, uri, event=event, append=append):
    1246                      try:
    1247                          uri = (uri or "").encode("ascii")
    1248                      except UnicodeError:
    1249                          pass
    1250                      append((event, (prefix or "", uri or "")))
    1251                  parser.StartNamespaceDeclHandler = handler
    1252              elif event == "end-ns":
    1253                  def handler(prefix, event=event, append=append):
    1254                      append((event, None))
    1255                  parser.EndNamespaceDeclHandler = handler
    1256              else:
    1257                  raise ValueError("unknown event %r" % event)
    1258
    1259 -    def next(self):
    1260          try:
    1261              while 1:
    1262                  try:
    1263                      item = self._events[self._index]
    1264                      self._index += 1
    1265                      return item
    1266                  except IndexError:
    1267                      pass
    1268                  if self._error:
    1269                      e = self._error
    1270                      self._error = None
    1271                      raise e
    1272                  if self._parser is None:
    1273                      self.root = self._root
    1274                      break
    1275                  # load event buffer
    1276                  del self._events[:]
    1277                  self._index = 0
    1278                  data = self._file.read(16384)
    1279                  if data:
    1280                      try:
    1281                          self._parser.feed(data)
    1282                      except SyntaxError as exc:
    1283                          self._error = exc
    1284                  else:
    1285                      self._root = self._parser.close()
    1286                      self._parser = None
    1287          except:
    1288              if self._close_file:
    1289                  self._file.close()
    1290              raise
    1291          if self._close_file:
    1292              self._file.close()
    1293          raise StopIteration
    1294
    1295 -    def __iter__(self):
    1296          return self
    1297
    1298  ##
    1299  # Parses an XML document from a string constant.  This function can
    1300  # be used to embed "XML literals" in Python code.
    1301  #
    1302  # @param source A string containing XML data.
    1303  # @param parser An optional parser instance.  If not given, the
    1304  #     standard {@link XMLParser} parser is used.
    1305  # @return An Element instance.
    1306  # @defreturn Element
    1307
    1308 -def XML(text, parser=None):
    1309      if not parser:
    1310          parser = XMLParser(target=TreeBuilder())
    1311      parser.feed(text)
    1312      return parser.close()
    1313
    1314  ##
    1315  # Parses an XML document from a string constant, and also returns
    1316  # a dictionary which maps from element id:s to elements.
    1317  #
    1318  # @param source A string containing XML data.
    1319  # @param parser An optional parser instance.  If not given, the
    1320  #     standard {@link XMLParser} parser is used.
    1321  # @return A tuple containing an Element instance and a dictionary.
    1322  # @defreturn (Element, dictionary)
    1323
    1324 -def XMLID(text, parser=None):
    1325      if not parser:
    1326          parser = XMLParser(target=TreeBuilder())
    1327      parser.feed(text)
    1328      tree = parser.close()
    1329      ids = {}
    1330      for elem in tree.iter():
    1331          id = elem.get("id")
    1332          if id:
    1333              ids[id] = elem
    1334      return tree, ids
    1335
    1336  ##
    1337  # Parses an XML document from a string constant.  Same as {@link #XML}.
    1338  #
    1339  # @def fromstring(text)
    1340  # @param source A string containing XML data.
    1341  # @return An Element instance.
    1342  # @defreturn Element
    1343
    1344  fromstring = XML
    1345
    1346  ##
    1347  # Parses an XML document from a sequence of string fragments.
    1348  #
    1349  # @param sequence A list or other sequence containing XML data fragments.
    1350  # @param parser An optional parser instance.  If not given, the
    1351  #     standard {@link XMLParser} parser is used.
    1352  # @return An Element instance.
    1353  # @defreturn Element
    1354  # @since 1.3
    1355
    1356 -def fromstringlist(sequence, parser=None):
    1357      if not parser:
    1358          parser = XMLParser(target=TreeBuilder())
    1359      for text in sequence:
    1360          parser.feed(text)
    1361      return parser.close()
    1362
    1363  # --------------------------------------------------------------------
    1364
    1365  ##
    1366  # Generic element structure builder.  This builder converts a sequence
    1367  # of {@link #TreeBuilder.start}, {@link #TreeBuilder.data}, and {@link
    1368  # #TreeBuilder.end} method calls to a well-formed element structure.
    1369  # <p>
    1370  # You can use this class to build an element structure using a custom XML
    1371  # parser, or a parser for some other XML-like format.
    1372  #
    1373  # @param element_factory Optional element factory.  This factory
    1374  #    is called to create new Element instances, as necessary.
    1375
    1376 -class TreeBuilder(object):
    1377
    1378 -    def __init__(self, element_factory=None):
    1379          self._data = [] # data collector
    1380          self._elem = [] # element stack
    1381          self._last = None # last element
    1382          self._tail = None # true if we're after an end tag
    1383          if element_factory is None:
    1384              element_factory = Element
    1385          self._factory = element_factory
    1386
    1387      ##
    1388      # Flushes the builder buffers, and returns the toplevel document
    1389      # element.
    1390      #
    1391      # @return An Element instance.
    1392      # @defreturn Element
    1393
    1394 -    def close(self):
    1395          assert len(self._elem) == 0, "missing end tags"
    1396          assert self._last is not None, "missing toplevel element"
    1397          return self._last
    1398
    1399 -    def _flush(self):
    1400          if self._data:
    1401              if self._last is not None:
    1402                  text = "".join(self._data)
    1403                  if self._tail:
    1404                      assert self._last.tail is None, "internal error (tail)"
    1405                      self._last.tail = text
    1406                  else:
    1407                      assert self._last.text is None, "internal error (text)"
    1408                      self._last.text = text
    1409              self._data = []
    1410
    1411      ##
    1412      # Adds text to the current element.
    1413      #
    1414      # @param data A string.  This should be either an 8-bit string
    1415      #    containing ASCII text, or a Unicode string.
    1416
    1417 -    def data(self, data):
    1418          self._data.append(data)
    1419
    1420      ##
    1421      # Opens a new element.
    1422      #
    1423      # @param tag The element name.
    1424      # @param attrib A dictionary containing element attributes.
    1425      # @return The opened element.
    1426      # @defreturn Element
    1427
    1428 -    def start(self, tag, attrs):
    1429          self._flush()
    1430          self._last = elem = self._factory(tag, attrs)
    1431          if self._elem:
    1432              self._elem[-1].append(elem)
    1433          self._elem.append(elem)
    1434          self._tail = 0
    1435          return elem
    1436
    1437      ##
    1438      # Closes the current element.
    1439      #
    1440      # @param tag The element name.
    1441      # @return The closed element.
    1442      # @defreturn Element
    1443
    1444 -    def end(self, tag):
    1445          self._flush()
    1446          self._last = self._elem.pop()
    1447          assert self._last.tag == tag,\
    1448                 "end tag mismatch (expected %s, got %s)" % (
    1449                     self._last.tag, tag)
    1450          self._tail = 1
    1451          return self._last
    1452
    1453  ##
    1454  # Element structure builder for XML source data, based on the
    1455  # <b>expat</b> parser.
    1456  #
    1457  # @keyparam target Target object.  If omitted, the builder uses an
    1458  #     instance of the standard {@link #TreeBuilder} class.
    1459  # @keyparam html Predefine HTML entities.  This flag is not supported
    1460  #     by the current implementation.
    1461  # @keyparam encoding Optional encoding.  If given, the value overrides
    1462  #     the encoding specified in the XML file.
    1463  # @see #ElementTree
    1464  # @see #TreeBuilder
    1465
    1466 -class XMLParser(object):
    1467
    1468 -    def __init__(self, html=0, target=None, encoding=None):
    1469          try:
    1470              from xml.parsers import expat
    1471          except ImportError:
    1472              try:
    1473                  import pyexpat as expat
    1474              except ImportError:
    1475                  raise ImportError(
    1476                      "No module named expat; use SimpleXMLTreeBuilder instead"
    1477                      )
    1478          parser = expat.ParserCreate(encoding, "}")
    1479          if target is None:
    1480              target = TreeBuilder()
    1481          # underscored names are provided for compatibility only
    1482          self.parser = self._parser = parser
    1483          self.target = self._target = target
    1484          self._error = expat.error
    1485          self._names = {} # name memo cache
    1486          # callbacks
    1487          parser.DefaultHandlerExpand = self._default
    1488          parser.StartElementHandler = self._start
    1489          parser.EndElementHandler = self._end
    1490          parser.CharacterDataHandler = self._data
    1491          # optional callbacks
    1492          parser.CommentHandler = self._comment
    1493          parser.ProcessingInstructionHandler = self._pi
    1494          # let expat do the buffering, if supported
    1495          try:
    1496              self._parser.buffer_text = 1
    1497          except AttributeError:
    1498              pass
    1499          # use new-style attribute handling, if supported
    1500          try:
    1501              self._parser.ordered_attributes = 1
    1502              self._parser.specified_attributes = 1
    1503              parser.StartElementHandler = self._start_list
    1504          except AttributeError:
    1505              pass
    1506          self._doctype = None
    1507          self.entity = {}
    1508          try:
    1509              self.version = "Expat %d.%d.%d" % expat.version_info
    1510          except AttributeError:
    1511              pass # unknown
    1512
    1513 -    def _raiseerror(self, value):
    1514          err = ParseError(value)
    1515          err.code = value.code
    1516          err.position = value.lineno, value.offset
    1517          raise err
    1518
    1519 -    def _fixtext(self, text):
    1520          # convert text string to ascii, if possible
    1521          try:
    1522              return text.encode("ascii")
    1523          except UnicodeError:
    1524              return text
    1525
    1526 -    def _fixname(self, key):
    1527          # expand qname, and convert name string to ascii, if possible
    1528          try:
    1529              name = self._names[key]
    1530          except KeyError:
    1531              name = key
    1532              if "}" in name:
    1533                  name = "{" + name
    1534              self._names[key] = name = self._fixtext(name)
    1535          return name
    1536
    1537 -    def _start(self, tag, attrib_in):
    1538          fixname = self._fixname
    1539          fixtext = self._fixtext
    1540          tag = fixname(tag)
    1541          attrib = {}
    1542          for key, value in attrib_in.items():
    1543              attrib[fixname(key)] = fixtext(value)
    1544          return self.target.start(tag, attrib)
    1545
    1546 -    def _start_list(self, tag, attrib_in):
    1547          fixname = self._fixname
    1548          fixtext = self._fixtext
    1549          tag = fixname(tag)
    1550          attrib = {}
    1551          if attrib_in:
    1552              for i in range(0, len(attrib_in), 2):
    1553                  attrib[fixname(attrib_in[i])] = fixtext(attrib_in[i+1])
    1554          return self.target.start(tag, attrib)
    1555
    1556 -    def _data(self, text):
    1557          return self.target.data(self._fixtext(text))
    1558
    1559 -    def _end(self, tag):
    1560          return self.target.end(self._fixname(tag))
    1561
    1562 -    def _comment(self, data):
    1563          try:
    1564              comment = self.target.comment
    1565          except AttributeError:
    1566              pass
    1567          else:
    1568              return comment(self._fixtext(data))
    1569
    1570 -    def _pi(self, target, data):
    1571          try:
    1572              pi = self.target.pi
    1573          except AttributeError:
    1574              pass
    1575          else:
    1576              return pi(self._fixtext(target), self._fixtext(data))
    1577
    1578 -    def _default(self, text):
    1579          prefix = text[:1]
    1580          if prefix == "&":
    1581              # deal with undefined entities
    1582              try:
    1583                  self.target.data(self.entity[text[1:-1]])
    1584              except KeyError:
    1585                  from xml.parsers import expat
    1586                  err = expat.error(
    1587                      "undefined entity %s: line %d, column %d" %
    1588                      (text, self._parser.ErrorLineNumber,
    1589                      self._parser.ErrorColumnNumber)
    1590                      )
    1591                  err.code = 11 # XML_ERROR_UNDEFINED_ENTITY
    1592                  err.lineno = self._parser.ErrorLineNumber
    1593                  err.offset = self._parser.ErrorColumnNumber
    1594                  raise err
    1595          elif prefix == "<" and text[:9] == "<!DOCTYPE":
    1596              self._doctype = [] # inside a doctype declaration
    1597          elif self._doctype is not None:
    1598              # parse doctype contents
    1599              if prefix == ">":
    1600                  self._doctype = None
    1601                  return
    1602              text = text.strip()
    1603              if not text:
    1604                  return
    1605              self._doctype.append(text)
    1606              n = len(self._doctype)
    1607              if n > 2:
    1608                  type = self._doctype[1]
    1609                  if type == "PUBLIC" and n == 4:
    1610                      name, type, pubid, system = self._doctype
    1611                  elif type == "SYSTEM" and n == 3:
    1612                      name, type, system = self._doctype
    1613                      pubid = None
    1614                  else:
    1615                      return
    1616                  if pubid:
    1617                      pubid = pubid[1:-1]
    1618                  if hasattr(self.target, "doctype"):
    1619                      self.target.doctype(name, pubid, system[1:-1])
    1620                  elif self.doctype is not self._XMLParser__doctype:
    1621                      # warn about deprecated call
    1622                      self._XMLParser__doctype(name, pubid, system[1:-1])
    1623                      self.doctype(name, pubid, system[1:-1])
    1624                  self._doctype = None
    1625
    1626      ##
    1627      # (Deprecated) Handles a doctype declaration.
    1628      #
    1629      # @param name Doctype name.
    1630      # @param pubid Public identifier.
    1631      # @param system System identifier.
    1632
    1633 -    def doctype(self, name, pubid, system):
    1634          """This method of XMLParser is deprecated."""
    1635          warnings.warn(
    1636              "This method of XMLParser is deprecated.  Define doctype() "
    1637              "method on the TreeBuilder target.",
    1638              DeprecationWarning,
    1639              )
    1640
    1641      # sentinel, if doctype is redefined in a subclass
    1642      __doctype = doctype
    1643
    1644      ##
    1645      # Feeds data to the parser.
    1646      #
    1647      # @param data Encoded data.
    1648
    1649 -    def feed(self, data):
    1650          try:
    1651              self._parser.Parse(data, 0)
    1652          except self._error, v:
    1653              self._raiseerror(v)
    1654
    1655      ##
    1656      # Finishes feeding data to the parser.
    1657      #
    1658      # @return An element structure.
    1659      # @defreturn Element
    1660
    1661 -    def close(self):
    1662          try:
    1663              self._parser.Parse("", 1) # end of data
    1664          except self._error, v:
    1665              self._raiseerror(v)
    1666          tree = self.target.close()
    1667          del self.target, self._parser # get rid of circular references
    1668          return tree
    1669
    1670  # compatibility
    1671  XMLTreeBuilder = XMLParser
    1672
    1673  # workaround circular import.
    1674  try:
    1675      from ElementC14N import _serialize_c14n
    1676      _serialize["c14n"] = _serialize_c14n
    1677  except ImportError:
    1678      pass
    1679