elementtree.ElementTree

Package elementtree :: Module ElementTree |
[frames] | no frames]

# Source Code for Module elementtree.ElementTree

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