Mercurial > EmeraldTree
changeset 0:5169fce2d144 upstream
Import ElementTree (1.3a3-20070912-preview).
author | Bastian Blank <bblank@thinkmo.de> |
---|---|
date | Fri, 30 May 2008 18:02:30 +0200 |
parents | |
children | 34be1290ac0b |
files | PKG-INFO README elementtree/ElementInclude.py elementtree/ElementPath.py elementtree/ElementTree.py elementtree/HTMLTreeBuilder.py elementtree/SimpleXMLWriter.py elementtree/TidyHTMLTreeBuilder.py elementtree/TidyTools.py elementtree/__init__.py setup.py |
diffstat | 11 files changed, 2760 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PKG-INFO Fri May 30 18:02:30 2008 +0200 @@ -0,0 +1,17 @@ +Metadata-Version: 1.0 +Name: elementtree +Version: 1.3a3-20070912-preview +Summary: ElementTree - a light-weight XML object model for Python. +Home-page: http://effbot.org/zone/element-index.htm +Author: Fredrik Lundh +Author-email: fredrik@pythonware.com +License: Python (MIT style) +Download-URL: http://effbot.org/downloads#elementtree +Description: The Element type is a flexible container object, designed to store + hierarchical data structures in memory. Element structures can be + converted to and from XML. +Platform: Python 1.5.2 and later. +Classifier: Development Status :: 6 - Mature +Classifier: Operating System :: OS Independent +Classifier: Topic :: Text Processing :: Markup :: HTML +Classifier: Topic :: Text Processing :: Markup :: XML
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Fri May 30 18:02:30 2008 +0200 @@ -0,0 +1,118 @@ +$Id: README 3277 2007-09-12 07:14:03Z fredrik $ + +======================= +The elementtree library +======================= + +This kit contains the ElementTree library, a light-weight toolkit for +XML processing in Python. + +For more information on this library, see: + + docs/index.html + http://effbot.org/zone/element.htm + +The 1.3 release is designed to work with Python 2.2 and newer. If you +need support for earlier Python releases, use ElementTree 1.2.6. + +Enjoy /F + +fredrik@pythonware.com +http://www.pythonware.com + +-------------------------------------------------------------------- +release info +-------------------------------------------------------------------- + +This is release 1.3 alpha 3 of the ElementTree library. + +For a list of changes in this release, see the CHANGES document. + +The latest version of this library can be downloaded from: + + http://effbot.org/downloads + +Comments, bug reports, and patches are welcome. Send them to +fredrik@pythonware.com. + +Note that this is free software, with limited support. If you need +commercial support on this module, contact fredrik@pythonware.com. + +-------------------------------------------------------------------- +contents +-------------------------------------------------------------------- + +README This file + +CHANGES List of changes in this release. + +elementtree/ + + ElementTree.py Element tree implementation. For a minimal + install, this file is all you need. + + ElementPath.py Element path support module. Adds limited + XPath support to find/findtext/findall. + + ElementInclude.py Element include support module. Adds limited + XInclude support. + + HTMLTreeBuilder.py Element tree builder for HTML. This only + works for mostly well-formed HTML; if you + need something that can parse arbitrary + HTML at least as good as your browser, use + TidyHTMLTreeBuilder or TidyTools (see below). + + TidyHTMLTreeBuilder.py Element tree builder for HTML, based on the + tidylib library. This tree builder requires + the _elementtidy extension module (available + from http://effbot.org/downloads). + + SimpleXMLWriter.py Simple XML writer + + TidyTools.py Build element trees from HTML, using the + external 'tidy' utility. + +setup.py Build/installation script + +docs/index.html API reference pages. +docs/* + +demo*.py Sample scripts +samples/* Sample data + +selftest.py Selftest (requires Python 2.1 or later) +tidytest.py Selftest for TidyHTMLTreeBuilder components. + +benchmark.py Benchmark script (usage: benchmark.py file) + +-------------------------------------------------------------------- +license +-------------------------------------------------------------------- + +The ElementTree Library is + +Copyright (c) 1999-2007 by Secret Labs AB +Copyright (c) 1999-2007 by Fredrik Lundh + +By obtaining, using, and/or copying this software and/or its +associated documentation, you agree that you have read, understood, +and will comply with the following terms and conditions: + +Permission to use, copy, modify, and distribute this software and its +associated documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies, and that both that copyright notice and this permission notice +appear in supporting documentation, and that the name of Secret Labs +AB or the author not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR +ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +--------------------------------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/elementtree/ElementInclude.py Fri May 30 18:02:30 2008 +0200 @@ -0,0 +1,140 @@ +# +# ElementTree +# $Id: ElementInclude.py 3265 2007-09-06 20:42:00Z fredrik $ +# +# limited xinclude support for element trees +# +# history: +# 2003-08-15 fl created +# 2003-11-14 fl fixed default loader +# +# Copyright (c) 2003-2004 by Fredrik Lundh. All rights reserved. +# +# fredrik@pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2007 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +## +# Limited XInclude support for the ElementTree package. +## + +import copy +import ElementTree + +XINCLUDE = "{http://www.w3.org/2001/XInclude}" + +XINCLUDE_INCLUDE = XINCLUDE + "include" +XINCLUDE_FALLBACK = XINCLUDE + "fallback" + +## +# Fatal include error. + +class FatalIncludeError(SyntaxError): + pass + +## +# Default loader. This loader reads an included resource from disk. +# +# @param href Resource reference. +# @param parse Parse mode. Either "xml" or "text". +# @param encoding Optional text encoding. +# @return The expanded resource. If the parse mode is "xml", this +# is an ElementTree instance. If the parse mode is "text", this +# is a Unicode string. If the loader fails, it can return None +# or raise an IOError exception. +# @throws IOError If the loader fails to load the resource. + +def default_loader(href, parse, encoding=None): + file = open(href) + if parse == "xml": + data = ElementTree.parse(file).getroot() + else: + data = file.read() + if encoding: + data = data.decode(encoding) + file.close() + return data + +## +# Expand XInclude directives. +# +# @param elem Root element. +# @param loader Optional resource loader. If omitted, it defaults +# to {@link default_loader}. If given, it should be a callable +# that implements the same interface as <b>default_loader</b>. +# @throws FatalIncludeError If the function fails to include a given +# resource, or if the tree contains malformed XInclude elements. +# @throws IOError If the function fails to load a given resource. + +def include(elem, loader=None): + if loader is None: + loader = default_loader + # look for xinclude elements + i = 0 + while i < len(elem): + e = elem[i] + if e.tag == XINCLUDE_INCLUDE: + # process xinclude directive + href = e.get("href") + parse = e.get("parse", "xml") + if parse == "xml": + node = loader(href, parse) + if node is None: + raise FatalIncludeError( + "cannot load %r as %r" % (href, parse) + ) + node = copy.copy(node) + if e.tail: + node.tail = (node.tail or "") + e.tail + elem[i] = node + elif parse == "text": + text = loader(href, parse, e.get("encoding")) + if text is None: + raise FatalIncludeError( + "cannot load %r as %r" % (href, parse) + ) + if i: + node = elem[i-1] + node.tail = (node.tail or "") + text + else: + elem.text = (elem.text or "") + text + (e.tail or "") + del elem[i] + continue + else: + raise FatalIncludeError( + "unknown parse type in xi:include tag (%r)" % parse + ) + elif e.tag == XINCLUDE_FALLBACK: + raise FatalIncludeError( + "xi:fallback tag must be child of xi:include (%r)" % e.tag + ) + else: + include(e, loader) + i = i + 1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/elementtree/ElementPath.py Fri May 30 18:02:30 2008 +0200 @@ -0,0 +1,226 @@ +# +# ElementTree +# $Id: ElementPath.py 3276 2007-09-12 06:52:30Z fredrik $ +# +# limited xpath support for element trees +# +# history: +# 2003-05-23 fl created +# 2003-05-28 fl added support for // etc +# 2003-08-27 fl fixed parsing of periods in element names +# 2007-09-10 fl new selection engine +# +# Copyright (c) 2003-2007 by Fredrik Lundh. All rights reserved. +# +# fredrik@pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2007 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +## +# Implementation module for XPath support. There's usually no reason +# to import this module directly; the <b>ElementTree</b> does this for +# you, if needed. +## + +import re + +xpath_tokenizer = re.compile( + "(" + "'[^']*'|\"[^\"]*\"|" + "::|" + "//?|" + "\.\.|" + "\(\)|" + "[/.*:\[\]\(\)@=])|" + "((?:\{[^}]+\})?[^/:\[\]\(\)@=\s]+)|" + "\s+" + ).findall + +def prepare_tag(next, token): + tag = token[1] + def select(context, result): + for elem in result: + for e in elem: + if e.tag == tag: + yield e + return select + +def prepare_star(next, token): + def select(context, result): + for elem in result: + for e in elem: + yield e + return select + +def prepare_dot(next, token): + def select(context, result): + for elem in result: + yield elem + return select + +def prepare_iter(next, token): + token = next() + if token[0] == "*": + tag = "*" + elif not token[0]: + tag = token[1] + else: + raise SyntaxError + def select(context, result): + for elem in result: + for e in elem.iter(tag): + if e is not elem: + yield e + return select + +def prepare_dot_dot(next, token): + def select(context, result): + parent_map = context.parent_map + if parent_map is None: + context.parent_map = parent_map = {} + for p in context.root.iter(): + for e in p: + parent_map[e] = p + for elem in result: + if elem in parent_map: + yield parent_map[elem] + return select + +def prepare_predicate(next, token): + # this one should probably be refactored... + token = next() + if token[0] == "@": + # attribute + token = next() + if token[0]: + raise SyntaxError("invalid attribute predicate") + key = token[1] + token = next() + if token[0] == "]": + def select(context, result): + for elem in result: + if elem.get(key) is not None: + yield elem + elif token[0] == "=": + value = next()[0] + if value[:1] == "'" or value[:1] == '"': + value = value[1:-1] + else: + raise SyntaxError("invalid comparision target") + token = next() + def select(context, result): + for elem in result: + if elem.get(key) == value: + yield elem + if token[0] != "]": + raise SyntaxError("invalid attribute predicate") + elif not token[0]: + tag = token[1] + token = next() + if token[0] != "]": + raise SyntaxError("invalid node predicate") + def select(context, result): + for elem in result: + if elem.find(tag) is not None: + yield elem + else: + raise SyntaxError("invalid predicate") + return select + +ops = { + "": prepare_tag, + "*": prepare_star, + ".": prepare_dot, + "..": prepare_dot_dot, + "//": prepare_iter, + "[": prepare_predicate, + } + +_cache = {} + +class _SelectorContext: + parent_map = None + def __init__(self, root): + self.root = root + +# -------------------------------------------------------------------- + +## +# Find first matching object. + +def find(elem, path): + try: + return findall(elem, path).next() + except StopIteration: + return None + +## +# Find all matching objects. + +def findall(elem, path): + # compile selector pattern + try: + selector = _cache[path] + except KeyError: + if len(_cache) > 100: + _cache.clear() + if path[:1] == "/": + raise SyntaxError("cannot use absolute path on element") + stream = iter(xpath_tokenizer(path)) + next = stream.next; token = next() + selector = [] + while 1: + try: + selector.append(ops[token[0]](next, token)) + except StopIteration: + raise SyntaxError("invalid path") + try: + token = next() + if token[0] == "/": + token = next() + except StopIteration: + break + _cache[path] = selector + # execute selector pattern + result = [elem] + context = _SelectorContext(elem) + for select in selector: + result = select(context, result) + return result + +## +# Find text for first matching object. + +def findtext(elem, path, default=None): + try: + elem = findall(elem, path).next() + return elem.text + except StopIteration: + return default
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/elementtree/ElementTree.py Fri May 30 18:02:30 2008 +0200 @@ -0,0 +1,1542 @@ +# +# ElementTree +# $Id: ElementTree.py 3276 2007-09-12 06:52:30Z fredrik $ +# +# light-weight XML support for Python 2.2 and later. +# +# history: +# 2001-10-20 fl created (from various sources) +# 2001-11-01 fl return root from parse method +# 2002-02-16 fl sort attributes in lexical order +# 2002-04-06 fl TreeBuilder refactoring, added PythonDoc markup +# 2002-05-01 fl finished TreeBuilder refactoring +# 2002-07-14 fl added basic namespace support to ElementTree.write +# 2002-07-25 fl added QName attribute support +# 2002-10-20 fl fixed encoding in write +# 2002-11-24 fl changed default encoding to ascii; fixed attribute encoding +# 2002-11-27 fl accept file objects or file names for parse/write +# 2002-12-04 fl moved XMLTreeBuilder back to this module +# 2003-01-11 fl fixed entity encoding glitch for us-ascii +# 2003-02-13 fl added XML literal factory +# 2003-02-21 fl added ProcessingInstruction/PI factory +# 2003-05-11 fl added tostring/fromstring helpers +# 2003-05-26 fl added ElementPath support +# 2003-07-05 fl added makeelement factory method +# 2003-07-28 fl added more well-known namespace prefixes +# 2003-08-15 fl fixed typo in ElementTree.findtext (Thomas Dartsch) +# 2003-09-04 fl fall back on emulator if ElementPath is not installed +# 2003-10-31 fl markup updates +# 2003-11-15 fl fixed nested namespace bug +# 2004-03-28 fl added XMLID helper +# 2004-06-02 fl added default support to findtext +# 2004-06-08 fl fixed encoding of non-ascii element/attribute names +# 2004-08-23 fl take advantage of post-2.1 expat features +# 2004-09-03 fl made Element class visible; removed factory +# 2005-02-01 fl added iterparse implementation +# 2005-03-02 fl fixed iterparse support for pre-2.2 versions +# 2005-11-12 fl added tostringlist/fromstringlist helpers +# 2006-07-05 fl merged in selected changes from the 1.3 sandbox +# 2006-07-05 fl removed support for 2.1 and earlier +# 2007-06-21 fl added deprecation/future warnings +# 2007-08-25 fl added doctype hook, added parser version attribute etc +# 2007-08-26 fl added new serializer code (better namespace handling, etc) +# 2007-08-27 fl warn for broken /tag searches on tree level +# 2007-09-02 fl added html/text methods to serializer (experimental) +# 2007-09-05 fl added method argument to tostring/tostringlist +# 2007-09-06 fl improved error handling +# +# Copyright (c) 1999-2007 by Fredrik Lundh. All rights reserved. +# +# fredrik@pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2007 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +from __future__ import generators + +__all__ = [ + # public symbols + "Comment", + "dump", + "Element", "ElementTree", + "fromstring", "fromstringlist", + "iselement", "iterparse", + "parse", "ParseError", + "PI", "ProcessingInstruction", + "QName", + "SubElement", + "tostring", "tostringlist", + "TreeBuilder", + "VERSION", + "XML", + "XMLParser", "XMLTreeBuilder", + ] + +## +# The <b>Element</b> type is a flexible container object, designed to +# store hierarchical data structures in memory. The type can be +# described as a cross between a list and a dictionary. +# <p> +# Each element has a number of properties associated with it: +# <ul> +# <li>a <i>tag</i>. This is a string identifying what kind of data +# this element represents (the element type, in other words).</li> +# <li>a number of <i>attributes</i>, stored in a Python dictionary.</li> +# <li>a <i>text</i> string.</li> +# <li>an optional <i>tail</i> string.</li> +# <li>a number of <i>child elements</i>, stored in a Python sequence</li> +# </ul> +# +# To create an element instance, use the {@link #Element} constructor +# or the {@link #SubElement} factory function. +# <p> +# The {@link #ElementTree} class can be used to wrap an element +# structure, and convert it from and to XML. +## + +import sys, re + +class _SimpleElementPath(object): + # emulate pre-1.2 find/findtext/findall behaviour + def find(self, element, tag): + for elem in element: + if elem.tag == tag: + return elem + return None + def findtext(self, element, tag, default=None): + for elem in element: + if elem.tag == tag: + return elem.text or "" + return default + def findall(self, element, tag): + if tag[:3] == ".//": + return element.getiterator(tag[3:]) + result = [] + for elem in element: + if elem.tag == tag: + result.append(elem) + return result + +try: + import ElementPath +except ImportError: + # FIXME: issue warning in this case? + ElementPath = _SimpleElementPath() + +VERSION = "1.3a2" + +class ParseError(SyntaxError): + pass + +# -------------------------------------------------------------------- + +## +# Checks if an object appears to be a valid element object. +# +# @param An element instance. +# @return A true value if this is an element object. +# @defreturn flag + +def iselement(element): + # FIXME: not sure about this; might be a better idea to look + # for tag/attrib/text attributes + return isinstance(element, Element) or hasattr(element, "tag") + +## +# Element class. This class defines the Element interface, and +# provides a reference implementation of this interface. +# <p> +# The element name, attribute names, and attribute values can be +# either 8-bit ASCII strings or Unicode strings. +# +# @param tag The element name. +# @param attrib An optional dictionary, containing element attributes. +# @param **extra Additional attributes, given as keyword arguments. +# @see Element +# @see SubElement +# @see Comment +# @see ProcessingInstruction + +class Element(object): + # <tag attrib>text<child/>...</tag>tail + + ## + # (Attribute) Element tag. + + tag = None + + ## + # (Attribute) Element attribute dictionary. Where possible, use + # {@link #Element.get}, + # {@link #Element.set}, + # {@link #Element.keys}, and + # {@link #Element.items} to access + # element attributes. + + attrib = None + + ## + # (Attribute) Text before first subelement. This is either a + # string or the value None, if there was no text. + + text = None + + ## + # (Attribute) Text after this element's end tag, but before the + # next sibling element's start tag. This is either a string or + # the value None, if there was no text. + + tail = None # text after end tag, if any + + def __init__(self, tag, attrib={}, **extra): + attrib = attrib.copy() + attrib.update(extra) + self.tag = tag + self.attrib = attrib + self._children = [] + + def __repr__(self): + return "<Element %s at %x>" % (repr(self.tag), id(self)) + + ## + # Creates a new element object of the same type as this element. + # + # @param tag Element tag. + # @param attrib Element attributes, given as a dictionary. + # @return A new element instance. + + def makeelement(self, tag, attrib): + return Element(tag, attrib) + + ## + # Returns the number of subelements. + # + # @return The number of subelements. + + def __len__(self): + return len(self._children) + + def __nonzero__(self): + import warnings + warnings.warn( + "The behavior of this method will change in future versions. " + "Use specific 'len(elem)' or 'elem is not None' test instead.", + FutureWarning + ) + return len(self._children) != 0 # emulate old behaviour + + ## + # Returns the given subelement. + # + # @param index What subelement to return. + # @return The given subelement. + # @exception IndexError If the given element does not exist. + + def __getitem__(self, index): + return self._children[index] + + ## + # Replaces the given subelement. + # + # @param index What subelement to replace. + # @param element The new element value. + # @exception IndexError If the given element does not exist. + # @exception AssertionError If element is not a valid object. + + def __setitem__(self, index, element): + assert iselement(element) + self._children[index] = element + + ## + # Deletes the given subelement. + # + # @param index What subelement to delete. + # @exception IndexError If the given element does not exist. + + def __delitem__(self, index): + del self._children[index] + + ## + # Returns a list containing subelements in the given range. + # + # @param start The first subelement to return. + # @param stop The first subelement that shouldn't be returned. + # @return A sequence object containing subelements. + + def __getslice__(self, start, stop): + return self._children[start:stop] + + ## + # Replaces a number of subelements with elements from a sequence. + # + # @param start The first subelement to replace. + # @param stop The first subelement that shouldn't be replaced. + # @param elements A sequence object with zero or more elements. + # @exception AssertionError If a sequence member is not a valid object. + + def __setslice__(self, start, stop, elements): + for element in elements: + assert iselement(element) + self._children[start:stop] = list(elements) + + ## + # Deletes a number of subelements. + # + # @param start The first subelement to delete. + # @param stop The first subelement to leave in there. + + def __delslice__(self, start, stop): + del self._children[start:stop] + + ## + # Adds a subelement to the end of this element. + # + # @param element The element to add. + # @exception AssertionError If a sequence member is not a valid object. + + def append(self, element): + assert iselement(element) + self._children.append(element) + + ## + # Appends subelements from a sequence. + # + # @param elements A sequence object with zero or more elements. + # @exception AssertionError If a subelement is not a valid object. + # @since 1.3 + + def extend(self, elements): + for element in elements: + assert iselement(element) + self._children.extend(elements) + + ## + # Inserts a subelement at the given position in this element. + # + # @param index Where to insert the new subelement. + # @exception AssertionError If the element is not a valid object. + + def insert(self, index, element): + assert iselement(element) + self._children.insert(index, element) + + ## + # Removes a matching subelement. Unlike the <b>find</b> methods, + # this method compares elements based on identity, not on tag + # value or contents. + # + # @param element What element to remove. + # @exception ValueError If a matching element could not be found. + # @exception AssertionError If the element is not a valid object. + + def remove(self, element): + assert iselement(element) + self._children.remove(element) + + ## + # (Deprecated) Returns all subelements. The elements are returned + # in document order. + # + # @return A list of subelements. + # @defreturn list of Element instances + + def getchildren(self): + import warnings + warnings.warn( + "This method will be removed in future versions. " + "Use 'list(elem)' or iteration over elem instead.", + DeprecationWarning + ) + return self._children + + ## + # Finds the first matching subelement, by tag name or path. + # + # @param path What element to look for. + # @return The first matching element, or None if no element was found. + # @defreturn Element or None + + def find(self, path): + return ElementPath.find(self, path) + + ## + # Finds text for the first matching subelement, by tag name or path. + # + # @param path What element to look for. + # @param default What to return if the element was not found. + # @return The text content of the first matching element, or the + # default value no element was found. Note that if the element + # has is found, but has no text content, this method returns an + # empty string. + # @defreturn string + + def findtext(self, path, default=None): + return ElementPath.findtext(self, path, default) + + ## + # Finds all matching subelements, by tag name or path. + # + # @param path What element to look for. + # @return A list or iterator containing all matching elements, + # in document order. + # @defreturn list of Element instances + + def findall(self, path): + return ElementPath.findall(self, path) + + ## + # Resets an element. This function removes all subelements, clears + # all attributes, and sets the text and tail attributes to None. + + def clear(self): + self.attrib.clear() + self._children = [] + self.text = self.tail = None + + ## + # Gets an element attribute. + # + # @param key What attribute to look for. + # @param default What to return if the attribute was not found. + # @return The attribute value, or the default value, if the + # attribute was not found. + # @defreturn string or None + + def get(self, key, default=None): + return self.attrib.get(key, default) + + ## + # Sets an element attribute. + # + # @param key What attribute to set. + # @param value The attribute value. + + def set(self, key, value): + self.attrib[key] = value + + ## + # Gets a list of attribute names. The names are returned in an + # arbitrary order (just like for an ordinary Python dictionary). + # + # @return A list of element attribute names. + # @defreturn list of strings + + def keys(self): + return self.attrib.keys() + + ## + # Gets element attributes, as a sequence. The attributes are + # returned in an arbitrary order. + # + # @return A list of (name, value) tuples for all attributes. + # @defreturn list of (string, string) tuples + + def items(self): + return self.attrib.items() + + ## + # Creates a tree iterator. The iterator loops over this element + # and all subelements, in document order, and returns all elements + # with a matching tag. + # <p> + # If the tree structure is modified during iteration, new or removed + # elements may or may not be included. To get a stable set, use the + # list() function on the iterator, and loop over the resulting list. + # + # @param tag What tags to look for (default is to return all elements). + # @return An iterator containing all the matching elements. + # @defreturn iterator + + def iter(self, tag=None): + if tag == "*": + tag = None + if tag is None or self.tag == tag: + yield self + for e in self._children: + for e in e.iter(tag): + yield e + + # compatibility (FIXME: preserve list behaviour too? see below) + getiterator = iter + + # def getiterator(self, tag=None): + # return list(tag) + + ## + # Creates a text iterator. The iterator loops over this element + # and all subelements, in document order, and returns all inner + # text. + # + # @return An iterator containing all inner text. + # @defreturn iterator + + def itertext(self): + if self.text: + yield self.text + for e in self: + for s in e.itertext(): + yield s + if e.tail: + yield e.tail + +# compatibility +_Element = _ElementInterface = Element + +## +# Subelement factory. This function creates an element instance, and +# appends it to an existing element. +# <p> +# The element name, attribute names, and attribute values can be +# either 8-bit ASCII strings or Unicode strings. +# +# @param parent The parent element. +# @param tag The subelement name. +# @param attrib An optional dictionary, containing element attributes. +# @param **extra Additional attributes, given as keyword arguments. +# @return An element instance. +# @defreturn Element + +def SubElement(parent, tag, attrib={}, **extra): + attrib = attrib.copy() + attrib.update(extra) + element = parent.makeelement(tag, attrib) + parent.append(element) + return element + +## +# Comment element factory. This factory function creates a special +# element that will be serialized as an XML comment by the standard +# serializer. +# <p> +# The comment string can be either an 8-bit ASCII string or a Unicode +# string. +# +# @param text A string containing the comment string. +# @return An element instance, representing a comment. +# @defreturn Element + +def Comment(text=None): + element = Element(Comment) + element.text = text + return element + +## +# PI element factory. This factory function creates a special element +# that will be serialized as an XML processing instruction by the standard +# serializer. +# +# @param target A string containing the PI target. +# @param text A string containing the PI contents, if any. +# @return An element instance, representing a PI. +# @defreturn Element + +def ProcessingInstruction(target, text=None): + element = Element(ProcessingInstruction) + element.text = target + if text: + element.text = element.text + " " + text + return element + +PI = ProcessingInstruction + +## +# QName wrapper. This can be used to wrap a QName attribute value, in +# order to get proper namespace handling on output. +# +# @param text A string containing the QName value, in the form {uri}local, +# or, if the tag argument is given, the URI part of a QName. +# @param tag Optional tag. If given, the first argument is interpreted as +# an URI, and this argument is interpreted as a local name. +# @return An opaque object, representing the QName. + +class QName(object): + def __init__(self, text_or_uri, tag=None): + if tag: + text_or_uri = "{%s}%s" % (text_or_uri, tag) + self.text = text_or_uri + def __str__(self): + return self.text + def __hash__(self): + return hash(self.text) + def __cmp__(self, other): + if isinstance(other, QName): + return cmp(self.text, other.text) + return cmp(self.text, other) + +# -------------------------------------------------------------------- + +## +# ElementTree wrapper class. This class represents an entire element +# hierarchy, and adds some extra support for serialization to and from +# standard XML. +# +# @param element Optional root element. +# @keyparam file Optional file handle or file name. If given, the +# tree is initialized with the contents of this XML file. + +class ElementTree(object): + + def __init__(self, element=None, file=None): + assert element is None or iselement(element) + self._root = element # first node + if file: + self.parse(file) + + ## + # Gets the root element for this tree. + # + # @return An element instance. + # @defreturn Element + + def getroot(self): + return self._root + + ## + # Replaces the root element for this tree. This discards the + # current contents of the tree, and replaces it with the given + # element. Use with care. + # + # @param element An element instance. + + def _setroot(self, element): + assert iselement(element) + self._root = element + + ## + # Loads an external XML document into this element tree. + # + # @param source A file name or file object. + # @keyparam parser An optional parser instance. If not given, the + # standard {@link XMLParser} parser is used. + # @return The document root element. + # @defreturn Element + + def parse(self, source, parser=None): + if not hasattr(source, "read"): + source = open(source, "rb") + if not parser: + parser = XMLParser(target=TreeBuilder()) + while 1: + data = source.read(32768) + if not data: + break + parser.feed(data) + self._root = parser.close() + return self._root + + ## + # Creates a tree iterator for the root element. The iterator loops + # over all elements in this tree, in document order. + # + # @param tag What tags to look for (default is to return all elements) + # @return An iterator. + # @defreturn iterator + + def iter(self, tag=None): + assert self._root is not None + return self._root.iter(tag) + + getiterator = iter + + ## + # Finds the first toplevel element with given tag. + # Same as getroot().find(path). + # + # @param path What element to look for. + # @return The first matching element, or None if no element was found. + # @defreturn Element or None + + def find(self, path): + assert self._root is not None + if path[:1] == "/": + path = "." + path + import warnings + warnings.warn( + "This search is broken in 1.3 and earlier; if you rely " + "on the current behaviour, change it to %r" % path, + FutureWarning + ) + return self._root.find(path) + + ## + # Finds the element text for the first toplevel element with given + # tag. Same as getroot().findtext(path). + # + # @param path What toplevel element to look for. + # @param default What to return if the element was not found. + # @return The text content of the first matching element, or the + # default value no element was found. Note that if the element + # has is found, but has no text content, this method returns an + # empty string. + # @defreturn string + + def findtext(self, path, default=None): + assert self._root is not None + if path[:1] == "/": + path = "." + path + import warnings + warnings.warn( + "This search is broken in 1.3 and earlier; if you rely " + "on the current behaviour, change it to %r" % path, + FutureWarning + ) + return self._root.findtext(path, default) + + ## + # Finds all toplevel elements with the given tag. + # Same as getroot().findall(path). + # + # @param path What element to look for. + # @return A list or iterator containing all matching elements, + # in document order. + # @defreturn list of Element instances + + def findall(self, path): + assert self._root is not None + if path[:1] == "/": + path = "." + path + import warnings + warnings.warn( + "This search is broken in 1.3 and earlier; if you rely " + "on the current behaviour, change it to %r" % path, + FutureWarning + ) + return self._root.findall(path) + + ## + # Writes the element tree to a file, as XML. + # + # @param file A file name, or a file object opened for writing. + # @keyparam encoding Optional output encoding (default is US-ASCII). + # @keyparam method Optional output method ("xml" or "html"; default + # is "xml". + # @keyparam xml_declaration Controls if an XML declaration should + # be added to the file. Use False for never, True for always, + # None for only if not US-ASCII or UTF-8. None is default. + + def write(self, file, + # keyword arguments + encoding="us-ascii", + xml_declaration=None, + default_namespace=None, + method=None): + assert self._root is not None + if not hasattr(file, "write"): + file = open(file, "wb") + write = file.write + if not method: + method = "xml" + if not encoding: + encoding = "us-ascii" + elif xml_declaration or (xml_declaration is None and + encoding not in ("utf-8", "us-ascii")): + write("<?xml version='1.0' encoding='%s'?>\n" % encoding) + if method == "text": + _serialize_text(write, self._root, encoding) + else: + qnames, namespaces = _namespaces( + self._root, encoding, default_namespace + ) + if method == "xml": + _serialize_xml( + write, self._root, encoding, qnames, namespaces + ) + elif method == "html": + _serialize_html( + write, self._root, encoding, qnames, namespaces + ) + else: + raise ValueError("unknown method %r" % method) + +# -------------------------------------------------------------------- +# serialization support + +def _namespaces(elem, encoding, default_namespace=None): + # identify namespaces used in this tree + + # maps qnames to *encoded* prefix:local names + qnames = {None: None} + + # maps uri:s to prefixes + namespaces = {} + if default_namespace: + namespaces[default_namespace] = "" + + def encode(text): + return text.encode(encoding) + + def add_qname(qname): + # calculate serialized qname representation + try: + if qname[:1] == "{": + uri, tag = qname[1:].split("}", 1) + prefix = namespaces.get(uri) + if prefix is None: + prefix = _namespace_map.get(uri) + if prefix is None: + prefix = "ns%d" % len(namespaces) + if prefix != "xml": + namespaces[uri] = prefix + if prefix: + qnames[qname] = encode("%s:%s" % (prefix, tag)) + else: + qnames[qname] = encode(tag) # default element + else: + if default_namespace: + # FIXME: can this be handled in XML 1.0? + raise ValueError( + "cannot use non-qualified names with " + "default_namespace option" + ) + qnames[qname] = encode(qname) + except TypeError: + _raise_serialization_error(qname) + + # populate qname and namespaces table + try: + iterate = elem.iter + except AttributeError: + iterate = elem.getiterator # cET compatibility + for elem in iterate(): + tag = elem.tag + if isinstance(tag, QName) and tag.text not in qnames: + add_qname(tag.text) + elif isinstance(tag, basestring): + if tag not in qnames: + add_qname(tag) + elif tag is not None and tag is not Comment and tag is not PI: + _raise_serialization_error(tag) + for key, value in elem.items(): + if isinstance(key, QName): + key = key.text + if key not in qnames: + add_qname(key) + if isinstance(value, QName) and value.text not in qnames: + add_qname(value.text) + text = elem.text + if isinstance(text, QName) and text.text not in qnames: + add_qname(text.text) + return qnames, namespaces + +def _serialize_xml(write, elem, encoding, qnames, namespaces): + tag = elem.tag + text = elem.text + if tag is Comment: + write("<!--%s-->" % _escape_cdata(text, encoding)) + elif tag is ProcessingInstruction: + write("<?%s?>" % _escape_cdata(text, encoding)) + else: + tag = qnames[tag] + if tag is None: + if text: + write(_escape_cdata(text, encoding)) + for e in elem: + _serialize_xml(write, e, encoding, qnames, None) + else: + write("<" + tag) + items = elem.items() + if items or namespaces: + items.sort() # lexical order + for k, v in items: + if isinstance(k, QName): + k = k.text + if isinstance(v, QName): + v = qnames[v.text] + else: + v = _escape_attrib(v, encoding) + write(" %s=\"%s\"" % (qnames[k], v)) + if namespaces: + items = namespaces.items() + items.sort(key=lambda x: x[1]) # sort on prefix + for v, k in items: + if k: + k = ":" + k + write(" xmlns%s=\"%s\"" % ( + k.encode(encoding), + _escape_attrib(v, encoding) + )) + if text or len(elem): + write(">") + if text: + write(_escape_cdata(text, encoding)) + for e in elem: + _serialize_xml(write, e, encoding, qnames, None) + write("</" + tag + ">") + else: + write(" />") + if elem.tail: + write(_escape_cdata(elem.tail, encoding)) + +HTML_EMPTY = ("area", "base", "basefont", "br", "col", "frame", "hr", + "img", "input", "isindex", "link", "meta" "param") + +try: + HTML_EMPTY = set(HTML_EMPTY) +except NameError: + pass + +def _serialize_html(write, elem, encoding, qnames, namespaces): + tag = elem.tag + text = elem.text + if tag is Comment: + write("<!--%s-->" % _escape_cdata(text, encoding)) + elif tag is ProcessingInstruction: + write("<?%s?>" % _escape_cdata(text, encoding)) + else: + tag = qnames[tag] + if tag is None: + if text: + write(_escape_cdata(text, encoding)) + for e in elem: + _serialize_html(write, e, encoding, qnames, None) + else: + write("<" + tag) + items = elem.items() + if items or namespaces: + items.sort() # lexical order + for k, v in items: + if isinstance(k, QName): + k = k.text + if isinstance(v, QName): + v = qnames[v.text] + else: + v = _escape_attrib_html(v, encoding) + # FIXME: handle boolean attributes + write(" %s=\"%s\"" % (qnames[k], v)) + if namespaces: + items = namespaces.items() + items.sort(key=lambda x: x[1]) # sort on prefix + for v, k in items: + if k: + k = ":" + k + write(" xmlns%s=\"%s\"" % ( + k.encode(encoding), + _escape_attrib(v, encoding) + )) + write(">") + tag = tag.lower() + if text: + if tag == "script" or tag == "style": + write(_encode(text, encoding)) + else: + write(_escape_cdata(text, encoding)) + for e in elem: + _serialize_html(write, e, encoding, qnames, None) + if tag not in HTML_EMPTY: + write("</" + tag + ">") + if elem.tail: + write(_escape_cdata(elem.tail, encoding)) + +def _serialize_text(write, elem, encoding): + for part in elem.itertext(): + write(part.encode(encoding)) + if elem.tail: + write(elem.tail.encode(encoding)) + +## +# Registers a namespace prefix. The registry is global, and any +# existing mapping for either the given prefix or the namespace URI +# will be removed. +# +# @param prefix Namespace prefix. +# @param uri Namespace uri. Tags and attributes in this namespace +# will be serialized with the given prefix, if at all possible. +# @raise ValueError If the prefix is reserved, or is otherwise +# invalid. + +def register_namespace(prefix, uri): + if re.match("ns\d+$", prefix): + raise ValueError("Prefix format reserved for internal use") + for k, v in _namespace_map.items(): + if k == uri or v == prefix: + del _namespace_map[k] + _namespace_map[uri] = prefix + +_namespace_map = { + # "well-known" namespace prefixes + "http://www.w3.org/XML/1998/namespace": "xml", + "http://www.w3.org/1999/xhtml": "html", + "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf", + "http://schemas.xmlsoap.org/wsdl/": "wsdl", + # xml schema + "http://www.w3.org/2001/XMLSchema": "xs", + "http://www.w3.org/2001/XMLSchema-instance": "xsi", + # dublic core + "http://purl.org/dc/elements/1.1/": "dc", +} + +def _raise_serialization_error(text): + raise TypeError( + "cannot serialize %r (type %s)" % (text, type(text).__name__) + ) + +def _encode(text, encoding): + try: + return text.encode(encoding, "xmlcharrefreplace") + except (TypeError, AttributeError): + _raise_serialization_error(text) + +def _escape_cdata(text, encoding): + # escape character data + try: + # it's worth avoiding do-nothing calls for strings that are + # shorter than 500 character, or so. assume that's, by far, + # the most common case in most applications. + if "&" in text: + text = text.replace("&", "&") + if "<" in text: + text = text.replace("<", "<") + if ">" in text: + text = text.replace(">", ">") + return text.encode(encoding, "xmlcharrefreplace") + except (TypeError, AttributeError): + _raise_serialization_error(text) + +def _escape_attrib(text, encoding): + # escape attribute value + try: + if "&" in text: + text = text.replace("&", "&") + if "<" in text: + text = text.replace("<", "<") + if ">" in text: + text = text.replace(">", ">") + if "\"" in text: + text = text.replace("\"", """) + if "\n" in text: + text = text.replace("\n", " ") + return text.encode(encoding, "xmlcharrefreplace") + except (TypeError, AttributeError): + _raise_serialization_error(text) + +def _escape_attrib_html(text, encoding): + # escape attribute value + try: + if "&" in text: + text = text.replace("&", "&") + if ">" in text: + text = text.replace(">", ">") + if "\"" in text: + text = text.replace("\"", """) + return text.encode(encoding, "xmlcharrefreplace") + except (TypeError, AttributeError): + _raise_serialization_error(text) + +# -------------------------------------------------------------------- + +## +# Generates a string representation of an XML element, including all +# subelements. +# +# @param element An Element instance. +# @return An encoded string containing the XML data. +# @defreturn string + +def tostring(element, encoding=None, method=None): + class dummy: + pass + data = [] + file = dummy() + file.write = data.append + ElementTree(element).write(file, encoding, method=method) + return "".join(data) + +## +# Generates a string representation of an XML element, including all +# subelements. The string is returned as a sequence of string fragments. +# +# @param element An Element instance. +# @return A sequence object containing the XML data. +# @defreturn sequence +# @since 1.3 + +def tostringlist(element, encoding=None): + class dummy: + pass + data = [] + file = dummy() + file.write = data.append + ElementTree(element).write(file, encoding) + # FIXME: merge small fragments into larger parts + return data + +## +# Writes an element tree or element structure to sys.stdout. This +# function should be used for debugging only. +# <p> +# The exact output format is implementation dependent. In this +# version, it's written as an ordinary XML file. +# +# @param elem An element tree or an individual element. + +def dump(elem): + # debugging + if not isinstance(elem, ElementTree): + elem = ElementTree(elem) + elem.write(sys.stdout) + tail = elem.getroot().tail + if not tail or tail[-1] != "\n": + sys.stdout.write("\n") + +# -------------------------------------------------------------------- +# parsing + +## +# Parses an XML document into an element tree. +# +# @param source A filename or file object containing XML data. +# @param parser An optional parser instance. If not given, the +# standard {@link XMLParser} parser is used. +# @return An ElementTree instance + +def parse(source, parser=None): + tree = ElementTree() + tree.parse(source, parser) + return tree + +## +# Parses an XML document into an element tree incrementally, and reports +# what's going on to the user. +# +# @param source A filename or file object containing XML data. +# @param events A list of events to report back. If omitted, only "end" +# events are reported. +# @param parser An optional parser instance. If not given, the +# standard {@link XMLParser} parser is used. +# @return A (event, elem) iterator. + +def iterparse(source, events=None, parser=None): + if not hasattr(source, "read"): + source = open(source, "rb") + if not parser: + parser = XMLParser(target=TreeBuilder()) + return _IterParseIterator(source, events, parser) + +class _IterParseIterator(object): + + def __init__(self, source, events, parser): + self._file = source + self._events = [] + self._index = 0 + self.root = self._root = None + self._parser = parser + # wire up the parser for event reporting + parser = self._parser._parser + append = self._events.append + if events is None: + events = ["end"] + for event in events: + if event == "start": + try: + parser.ordered_attributes = 1 + parser.specified_attributes = 1 + def handler(tag, attrib_in, event=event, append=append, + start=self._parser._start_list): + append((event, start(tag, attrib_in))) + parser.StartElementHandler = handler + except AttributeError: + def handler(tag, attrib_in, event=event, append=append, + start=self._parser._start): + append((event, start(tag, attrib_in))) + parser.StartElementHandler = handler + elif event == "end": + def handler(tag, event=event, append=append, + end=self._parser._end): + append((event, end(tag))) + parser.EndElementHandler = handler + elif event == "start-ns": + def handler(prefix, uri, event=event, append=append): + try: + uri = uri.encode("ascii") + except UnicodeError: + pass + append((event, (prefix or "", uri))) + parser.StartNamespaceDeclHandler = handler + elif event == "end-ns": + def handler(prefix, event=event, append=append): + append((event, None)) + parser.EndNamespaceDeclHandler = handler + + def next(self): + while 1: + try: + item = self._events[self._index] + except IndexError: + if self._parser is None: + self.root = self._root + raise StopIteration + # load event buffer + del self._events[:] + self._index = 0 + data = self._file.read(16384) + if data: + self._parser.feed(data) + else: + self._root = self._parser.close() + self._parser = None + else: + self._index = self._index + 1 + return item + + def __iter__(self): + return self + +## +# Parses an XML document from a string constant. This function can +# be used to embed "XML literals" in Python code. +# +# @param source A string containing XML data. +# @param parser An optional parser instance. If not given, the +# standard {@link XMLParser} parser is used. +# @return An Element instance. +# @defreturn Element + +def XML(text, parser=None): + if not parser: + parser = XMLParser(target=TreeBuilder()) + parser.feed(text) + return parser.close() + +## +# Parses an XML document from a string constant, and also returns +# a dictionary which maps from element id:s to elements. +# +# @param source A string containing XML data. +# @param parser An optional parser instance. If not given, the +# standard {@link XMLParser} parser is used. +# @return A tuple containing an Element instance and a dictionary. +# @defreturn (Element, dictionary) + +def XMLID(text, parser=None): + if not parser: + parser = XMLParser(target=TreeBuilder()) + parser.feed(text) + tree = parser.close() + ids = {} + for elem in tree.getiterator(): + id = elem.get("id") + if id: + ids[id] = elem + return tree, ids + +## +# Parses an XML document from a string constant. Same as {@link #XML}. +# +# @def fromstring(text) +# @param source A string containing XML data. +# @return An Element instance. +# @defreturn Element + +fromstring = XML + +## +# Parses an XML document from a sequence of string fragments. +# +# @param sequence A list or other sequence containing XML data fragments. +# @param parser An optional parser instance. If not given, the +# standard {@link XMLParser} parser is used. +# @return An Element instance. +# @defreturn Element +# @since 1.3 + +def fromstringlist(sequence, parser=None): + if not parser: + parser = XMLParser(target=TreeBuilder()) + for text in sequence: + parser.feed(text) + return parser.close() + +# -------------------------------------------------------------------- + +## +# Generic element structure builder. This builder converts a sequence +# of {@link #TreeBuilder.start}, {@link #TreeBuilder.data}, and {@link +# #TreeBuilder.end} method calls to a well-formed element structure. +# <p> +# You can use this class to build an element structure using a custom XML +# parser, or a parser for some other XML-like format. +# +# @param element_factory Optional element factory. This factory +# is called to create new Element instances, as necessary. + +class TreeBuilder(object): + + def __init__(self, element_factory=None): + self._data = [] # data collector + self._elem = [] # element stack + self._last = None # last element + self._tail = None # true if we're after an end tag + if element_factory is None: + element_factory = Element + self._factory = element_factory + + ## + # Flushes the builder buffers, and returns the toplevel document + # element. + # + # @return An Element instance. + # @defreturn Element + + def close(self): + assert len(self._elem) == 0, "missing end tags" + assert self._last != None, "missing toplevel element" + return self._last + + def _flush(self): + if self._data: + if self._last is not None: + text = "".join(self._data) + if self._tail: + assert self._last.tail is None, "internal error (tail)" + self._last.tail = text + else: + assert self._last.text is None, "internal error (text)" + self._last.text = text + self._data = [] + + ## + # Adds text to the current element. + # + # @param data A string. This should be either an 8-bit string + # containing ASCII text, or a Unicode string. + + def data(self, data): + self._data.append(data) + + ## + # Opens a new element. + # + # @param tag The element name. + # @param attrib A dictionary containing element attributes. + # @return The opened element. + # @defreturn Element + + def start(self, tag, attrs): + self._flush() + self._last = elem = self._factory(tag, attrs) + if self._elem: + self._elem[-1].append(elem) + self._elem.append(elem) + self._tail = 0 + return elem + + ## + # Closes the current element. + # + # @param tag The element name. + # @return The closed element. + # @defreturn Element + + def end(self, tag): + self._flush() + self._last = self._elem.pop() + assert self._last.tag == tag,\ + "end tag mismatch (expected %s, got %s)" % ( + self._last.tag, tag) + self._tail = 1 + return self._last + +## +# Element structure builder for XML source data, based on the +# <b>expat</b> parser. +# +# @keyparam target Target object. If omitted, the builder uses an +# instance of the standard {@link #TreeBuilder} class. +# @keyparam html Predefine HTML entities. This flag is not supported +# by the current implementation. +# @keyparam encoding Optional encoding. If given, the value overrides +# the encoding specified in the XML file. +# @see #ElementTree +# @see #TreeBuilder + +class XMLParser(object): + + def __init__(self, html=0, target=None, encoding=None): + try: + from xml.parsers import expat + except ImportError: + try: + import pyexpat; expat = pyexpat + except ImportError: + raise ImportError( + "No module named expat; use SimpleXMLTreeBuilder instead" + ) + parser = expat.ParserCreate(encoding, "}") + if target is None: + target = TreeBuilder() + # underscored names are provided for compatibility only + self.parser = self._parser = parser + self.target = self._target = target + self._error = expat.error + self._names = {} # name memo cache + # callbacks + parser.DefaultHandlerExpand = self._default + parser.StartElementHandler = self._start + parser.EndElementHandler = self._end + parser.CharacterDataHandler = self._data + # let expat do the buffering, if supported + try: + self._parser.buffer_text = 1 + except AttributeError: + pass + # use new-style attribute handling, if supported + try: + self._parser.ordered_attributes = 1 + self._parser.specified_attributes = 1 + parser.StartElementHandler = self._start_list + except AttributeError: + pass + self._doctype = None + self.entity = {} + try: + self.version = "Expat %d.%d.%d" % expat.version_info + except AttributeError: + pass # unknown + + def _raiseerror(self, value): + err = ParseError(value) + err.code = value.code + err.position = value.lineno, value.offset + raise err + + def _fixtext(self, text): + # convert text string to ascii, if possible + try: + return text.encode("ascii") + except UnicodeError: + return text + + def _fixname(self, key): + # expand qname, and convert name string to ascii, if possible + try: + name = self._names[key] + except KeyError: + name = key + if "}" in name: + name = "{" + name + self._names[key] = name = self._fixtext(name) + return name + + def _start(self, tag, attrib_in): + fixname = self._fixname + fixtext = self._fixtext + tag = fixname(tag) + attrib = {} + for key, value in attrib_in.items(): + attrib[fixname(key)] = fixtext(value) + return self.target.start(tag, attrib) + + def _start_list(self, tag, attrib_in): + fixname = self._fixname + fixtext = self._fixtext + tag = fixname(tag) + attrib = {} + if attrib_in: + for i in range(0, len(attrib_in), 2): + attrib[fixname(attrib_in[i])] = fixtext(attrib_in[i+1]) + return self.target.start(tag, attrib) + + def _data(self, text): + return self.target.data(self._fixtext(text)) + + def _end(self, tag): + return self.target.end(self._fixname(tag)) + + def _default(self, text): + prefix = text[:1] + if prefix == "&": + # deal with undefined entities + try: + self.target.data(self.entity[text[1:-1]]) + except KeyError: + from xml.parsers import expat + err = expat.error( + "undefined entity %s: line %d, column %d" % + (text, self._parser.ErrorLineNumber, + self._parser.ErrorColumnNumber) + ) + err.code = 11 # XML_ERROR_UNDEFINED_ENTITY + err.lineno = self._parser.ErrorLineNumber + err.offset = self._parser.ErrorColumnNumber + raise err + elif prefix == "<" and text[:9] == "<!DOCTYPE": + self._doctype = [] # inside a doctype declaration + elif self._doctype is not None: + # parse doctype contents + if prefix == ">": + self._doctype = None + return + text = text.strip() + if not text: + return + self._doctype.append(text) + n = len(self._doctype) + if n > 2: + type = self._doctype[1] + if type == "PUBLIC" and n == 4: + name, type, pubid, system = self._doctype + elif type == "SYSTEM" and n == 3: + name, type, system = self._doctype + pubid = None + else: + return + if pubid: + pubid = pubid[1:-1] + if hasattr(self.target, "doctype"): + self.target.doctype(name, pubid, system[1:-1]) + self._doctype = None + + ## + # Feeds data to the parser. + # + # @param data Encoded data. + + def feed(self, data): + try: + self._parser.Parse(data, 0) + except self._error, v: + self._raiseerror(v) + + ## + # Finishes feeding data to the parser. + # + # @return An element structure. + # @defreturn Element + + def close(self): + try: + self._parser.Parse("", 1) # end of data + except self._error, v: + self._raiseerror(v) + tree = self.target.close() + del self.target, self._parser # get rid of circular references + return tree + +# compatibility +XMLTreeBuilder = XMLParser
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/elementtree/HTMLTreeBuilder.py Fri May 30 18:02:30 2008 +0200 @@ -0,0 +1,230 @@ +# +# ElementTree +# $Id: HTMLTreeBuilder.py 3265 2007-09-06 20:42:00Z fredrik $ +# +# a simple tree builder, for HTML input +# +# history: +# 2002-04-06 fl created +# 2002-04-07 fl ignore IMG and HR end tags +# 2002-04-07 fl added support for 1.5.2 and later +# 2003-04-13 fl added HTMLTreeBuilder alias +# 2004-12-02 fl don't feed non-ASCII charrefs/entities as 8-bit strings +# 2004-12-05 fl don't feed non-ASCII CDATA as 8-bit strings +# +# Copyright (c) 1999-2004 by Fredrik Lundh. All rights reserved. +# +# fredrik@pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2007 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +## +# Tools to build element trees from HTML files. +## + +import htmlentitydefs +import re, string, sys +import mimetools, StringIO + +import ElementTree + +AUTOCLOSE = "p", "li", "tr", "th", "td", "head", "body" +IGNOREEND = "img", "hr", "meta", "link", "br" + +if sys.version[:3] == "1.5": + is_not_ascii = re.compile(r"[\x80-\xff]").search # 1.5.2 +else: + is_not_ascii = re.compile(eval(r'u"[\u0080-\uffff]"')).search + +try: + from HTMLParser import HTMLParser +except ImportError: + from sgmllib import SGMLParser + # hack to use sgmllib's SGMLParser to emulate 2.2's HTMLParser + class HTMLParser(SGMLParser): + # the following only works as long as this class doesn't + # provide any do, start, or end handlers + def unknown_starttag(self, tag, attrs): + self.handle_starttag(tag, attrs) + def unknown_endtag(self, tag): + self.handle_endtag(tag) + +## +# ElementTree builder for HTML source code. This builder converts an +# HTML document or fragment to an ElementTree. +# <p> +# The parser is relatively picky, and requires balanced tags for most +# elements. However, elements belonging to the following group are +# automatically closed: P, LI, TR, TH, and TD. In addition, the +# parser automatically inserts end tags immediately after the start +# tag, and ignores any end tags for the following group: IMG, HR, +# META, and LINK. +# +# @keyparam builder Optional builder object. If omitted, the parser +# uses the standard <b>elementtree</b> builder. +# @keyparam encoding Optional character encoding, if known. If omitted, +# the parser looks for META tags inside the document. If no tags +# are found, the parser defaults to ISO-8859-1. Note that if your +# document uses a non-ASCII compatible encoding, you must decode +# the document before parsing. +# +# @see elementtree.ElementTree + +class HTMLTreeBuilder(HTMLParser): + + # FIXME: shouldn't this class be named Parser, not Builder? + + def __init__(self, builder=None, encoding=None): + self.__stack = [] + if builder is None: + builder = ElementTree.TreeBuilder() + self.__builder = builder + self.encoding = encoding or "iso-8859-1" + HTMLParser.__init__(self) + + ## + # Flushes parser buffers, and return the root element. + # + # @return An Element instance. + + def close(self): + HTMLParser.close(self) + return self.__builder.close() + + ## + # (Internal) Handles start tags. + + def handle_starttag(self, tag, attrs): + if tag == "meta": + # look for encoding directives + http_equiv = content = None + for k, v in attrs: + if k == "http-equiv": + http_equiv = string.lower(v) + elif k == "content": + content = v + if http_equiv == "content-type" and content: + # use mimetools to parse the http header + header = mimetools.Message( + StringIO.StringIO("%s: %s\n\n" % (http_equiv, content)) + ) + encoding = header.getparam("charset") + if encoding: + self.encoding = encoding + if tag in AUTOCLOSE: + if self.__stack and self.__stack[-1] == tag: + self.handle_endtag(tag) + self.__stack.append(tag) + attrib = {} + if attrs: + for k, v in attrs: + attrib[string.lower(k)] = v + self.__builder.start(tag, attrib) + if tag in IGNOREEND: + self.__stack.pop() + self.__builder.end(tag) + + ## + # (Internal) Handles end tags. + + def handle_endtag(self, tag): + if tag in IGNOREEND: + return + lasttag = self.__stack.pop() + if tag != lasttag and lasttag in AUTOCLOSE: + self.handle_endtag(lasttag) + self.__builder.end(tag) + + ## + # (Internal) Handles character references. + + def handle_charref(self, char): + if char[:1] == "x": + char = int(char[1:], 16) + else: + char = int(char) + if 0 <= char < 128: + self.__builder.data(chr(char)) + else: + self.__builder.data(unichr(char)) + + ## + # (Internal) Handles entity references. + + def handle_entityref(self, name): + entity = htmlentitydefs.entitydefs.get(name) + if entity: + if len(entity) == 1: + entity = ord(entity) + else: + entity = int(entity[2:-1]) + if 0 <= entity < 128: + self.__builder.data(chr(entity)) + else: + self.__builder.data(unichr(entity)) + else: + self.unknown_entityref(name) + + ## + # (Internal) Handles character data. + + def handle_data(self, data): + if isinstance(data, type('')) and is_not_ascii(data): + # convert to unicode, but only if necessary + data = unicode(data, self.encoding, "ignore") + self.__builder.data(data) + + ## + # (Hook) Handles unknown entity references. The default action + # is to ignore unknown entities. + + def unknown_entityref(self, name): + pass # ignore by default; override if necessary + +## +# An alias for the <b>HTMLTreeBuilder</b> class. + +TreeBuilder = HTMLTreeBuilder + +## +# Parse an HTML document or document fragment. +# +# @param source A filename or file object containing HTML data. +# @param encoding Optional character encoding, if known. If omitted, +# the parser looks for META tags inside the document. If no tags +# are found, the parser defaults to ISO-8859-1. +# @return An ElementTree instance + +def parse(source, encoding=None): + return ElementTree.parse(source, HTMLTreeBuilder(encoding=encoding)) + +if __name__ == "__main__": + import sys + ElementTree.dump(parse(open(sys.argv[1])))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/elementtree/SimpleXMLWriter.py Fri May 30 18:02:30 2008 +0200 @@ -0,0 +1,279 @@ +# +# SimpleXMLWriter +# $Id: SimpleXMLWriter.py 3265 2007-09-06 20:42:00Z fredrik $ +# +# a simple XML writer +# +# history: +# 2001-12-28 fl created +# 2002-11-25 fl fixed attribute encoding +# 2002-12-02 fl minor fixes for 1.5.2 +# 2004-06-17 fl added pythondoc markup +# 2004-07-23 fl added flush method (from Jay Graves) +# 2004-10-03 fl added declaration method +# +# Copyright (c) 2001-2004 by Fredrik Lundh +# +# fredrik@pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The SimpleXMLWriter module is +# +# Copyright (c) 2001-2004 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +## +# Tools to write XML files, without having to deal with encoding +# issues, well-formedness, etc. +# <p> +# The current version does not provide built-in support for +# namespaces. To create files using namespaces, you have to provide +# "xmlns" attributes and explicitly add prefixes to tags and +# attributes. +# +# <h3>Patterns</h3> +# +# The following example generates a small XHTML document. +# <pre> +# +# from elementtree.SimpleXMLWriter import XMLWriter +# import sys +# +# w = XMLWriter(sys.stdout) +# +# html = w.start("html") +# +# w.start("head") +# w.element("title", "my document") +# w.element("meta", name="generator", value="my application 1.0") +# w.end() +# +# w.start("body") +# w.element("h1", "this is a heading") +# w.element("p", "this is a paragraph") +# +# w.start("p") +# w.data("this is ") +# w.element("b", "bold") +# w.data(" and ") +# w.element("i", "italic") +# w.data(".") +# w.end("p") +# +# w.close(html) +# </pre> +## + +import re, sys, string + +try: + unicode("") +except NameError: + def encode(s, encoding): + # 1.5.2: application must use the right encoding + return s + _escape = re.compile(r"[&<>\"\x80-\xff]+") # 1.5.2 +else: + def encode(s, encoding): + return s.encode(encoding) + _escape = re.compile(eval(r'u"[&<>\"\u0080-\uffff]+"')) + +def encode_entity(text, pattern=_escape): + # map reserved and non-ascii characters to numerical entities + def escape_entities(m): + out = [] + for char in m.group(): + out.append("&#%d;" % ord(char)) + return string.join(out, "") + return encode(pattern.sub(escape_entities, text), "ascii") + +del _escape + +# +# the following functions assume an ascii-compatible encoding +# (or "utf-16") + +def escape_cdata(s, encoding=None, replace=string.replace): + s = replace(s, "&", "&") + s = replace(s, "<", "<") + s = replace(s, ">", ">") + if encoding: + try: + return encode(s, encoding) + except UnicodeError: + return encode_entity(s) + return s + +def escape_attrib(s, encoding=None, replace=string.replace): + s = replace(s, "&", "&") + s = replace(s, "'", "'") + s = replace(s, "\"", """) + s = replace(s, "<", "<") + s = replace(s, ">", ">") + if encoding: + try: + return encode(s, encoding) + except UnicodeError: + return encode_entity(s) + return s + +## +# XML writer class. +# +# @param file A file or file-like object. This object must implement +# a <b>write</b> method that takes an 8-bit string. +# @param encoding Optional encoding. + +class XMLWriter: + + def __init__(self, file, encoding="us-ascii"): + if not hasattr(file, "write"): + file = open(file, "w") + self.__write = file.write + if hasattr(file, "flush"): + self.flush = file.flush + self.__open = 0 # true if start tag is open + self.__tags = [] + self.__data = [] + self.__encoding = encoding + + def __flush(self): + # flush internal buffers + if self.__open: + self.__write(">") + self.__open = 0 + if self.__data: + data = string.join(self.__data, "") + self.__write(escape_cdata(data, self.__encoding)) + self.__data = [] + + ## + # Writes an XML declaration. + + def declaration(self): + encoding = self.__encoding + if encoding == "us-ascii" or encoding == "utf-8": + self.__write("<?xml version='1.0'?>\n") + else: + self.__write("<?xml version='1.0' encoding='%s'?>\n" % encoding) + + ## + # Opens a new element. Attributes can be given as keyword + # arguments, or as a string/string dictionary. You can pass in + # 8-bit strings or Unicode strings; the former are assumed to use + # the encoding passed to the constructor. The method returns an + # opaque identifier that can be passed to the <b>close</b> method, + # to close all open elements up to and including this one. + # + # @param tag Element tag. + # @param attrib Attribute dictionary. Alternatively, attributes + # can be given as keyword arguments. + # @return An element identifier. + + def start(self, tag, attrib={}, **extra): + self.__flush() + tag = escape_cdata(tag, self.__encoding) + self.__data = [] + self.__tags.append(tag) + self.__write("<%s" % tag) + if attrib or extra: + attrib = attrib.copy() + attrib.update(extra) + attrib = attrib.items() + attrib.sort() + for k, v in attrib: + k = escape_cdata(k, self.__encoding) + v = escape_attrib(v, self.__encoding) + self.__write(" %s=\"%s\"" % (k, v)) + self.__open = 1 + return len(self.__tags)-1 + + ## + # Adds a comment to the output stream. + # + # @param comment Comment text, as an 8-bit string or Unicode string. + + def comment(self, comment): + self.__flush() + self.__write("<!-- %s -->\n" % escape_cdata(comment, self.__encoding)) + + ## + # Adds character data to the output stream. + # + # @param text Character data, as an 8-bit string or Unicode string. + + def data(self, text): + self.__data.append(text) + + ## + # Closes the current element (opened by the most recent call to + # <b>start</b>). + # + # @param tag Element tag. If given, the tag must match the start + # tag. If omitted, the current element is closed. + + def end(self, tag=None): + if tag: + assert self.__tags, "unbalanced end(%s)" % tag + assert escape_cdata(tag, self.__encoding) == self.__tags[-1],\ + "expected end(%s), got %s" % (self.__tags[-1], tag) + else: + assert self.__tags, "unbalanced end()" + tag = self.__tags.pop() + if self.__data: + self.__flush() + elif self.__open: + self.__open = 0 + self.__write(" />") + return + self.__write("</%s>" % tag) + + ## + # Closes open elements, up to (and including) the element identified + # by the given identifier. + # + # @param id Element identifier, as returned by the <b>start</b> method. + + def close(self, id): + while len(self.__tags) > id: + self.end() + + ## + # Adds an entire element. This is the same as calling <b>start</b>, + # <b>data</b>, and <b>end</b> in sequence. The <b>text</b> argument + # can be omitted. + + def element(self, tag, text=None, attrib={}, **extra): + apply(self.start, (tag, attrib), extra) + if text: + self.data(text) + self.end() + + ## + # Flushes the output stream. + + def flush(self): + pass # replaced by the constructor
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/elementtree/TidyHTMLTreeBuilder.py Fri May 30 18:02:30 2008 +0200 @@ -0,0 +1,6 @@ +# +# ElementTree +# $Id: TidyHTMLTreeBuilder.py 3265 2007-09-06 20:42:00Z fredrik $ +# + +from elementtidy.TidyHTMLTreeBuilder import *
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/elementtree/TidyTools.py Fri May 30 18:02:30 2008 +0200 @@ -0,0 +1,128 @@ +# +# ElementTree +# $Id: TidyTools.py 3265 2007-09-06 20:42:00Z fredrik $ +# +# tools to run the "tidy" command on an HTML or XHTML file, and return +# the contents as an XHTML element tree. +# +# history: +# 2002-10-19 fl added to ElementTree library; added getzonebody function +# +# Copyright (c) 1999-2004 by Fredrik Lundh. All rights reserved. +# +# fredrik@pythonware.com +# http://www.pythonware.com +# + +## +# Tools to build element trees from HTML, using the external <b>tidy</b> +# utility. +## + +import glob, string, os, sys + +from ElementTree import ElementTree, Element + +NS_XHTML = "{http://www.w3.org/1999/xhtml}" + +## +# Convert an HTML or HTML-like file to XHTML, using the <b>tidy</b> +# command line utility. +# +# @param file Filename. +# @param new_inline_tags An optional list of valid but non-standard +# inline tags. +# @return An element tree, or None if not successful. + +def tidy(file, new_inline_tags=None): + + command = ["tidy", "-qn", "-asxml"] + + if new_inline_tags: + command.append("--new-inline-tags") + command.append(string.join(new_inline_tags, ",")) + + # FIXME: support more tidy options! + + # convert + os.system( + "%s %s >%s.out 2>%s.err" % (string.join(command), file, file, file) + ) + # check that the result is valid XML + try: + tree = ElementTree() + tree.parse(file + ".out") + except: + print "*** %s:%s" % sys.exc_info()[:2] + print ("*** %s is not valid XML " + "(check %s.err for info)" % (file, file)) + tree = None + else: + if os.path.isfile(file + ".out"): + os.remove(file + ".out") + if os.path.isfile(file + ".err"): + os.remove(file + ".err") + + return tree + +## +# Get document body from a an HTML or HTML-like file. This function +# uses the <b>tidy</b> function to convert HTML to XHTML, and cleans +# up the resulting XML tree. +# +# @param file Filename. +# @return A <b>body</b> element, or None if not successful. + +def getbody(file, **options): + # get clean body from text file + + # get xhtml tree + try: + tree = apply(tidy, (file,), options) + if tree is None: + return + except IOError, v: + print "***", v + return None + + NS = NS_XHTML + + # remove namespace uris + for node in tree.getiterator(): + if node.tag.startswith(NS): + node.tag = node.tag[len(NS):] + + body = tree.getroot().find("body") + + return body + +## +# Same as <b>getbody</b>, but turns plain text at the start of the +# document into an H1 tag. This function can be used to parse zone +# documents. +# +# @param file Filename. +# @return A <b>body</b> element, or None if not successful. + +def getzonebody(file, **options): + + body = getbody(file, **options) + if body is None: + return + + if body.text and string.strip(body.text): + title = Element("h1") + title.text = string.strip(body.text) + title.tail = "\n\n" + body.insert(0, title) + + body.text = None + + return body + +if __name__ == "__main__": + + import sys + for arg in sys.argv[1:]: + for file in glob.glob(arg): + print file, "...", tidy(file)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/elementtree/__init__.py Fri May 30 18:02:30 2008 +0200 @@ -0,0 +1,30 @@ +# $Id: __init__.py 3265 2007-09-06 20:42:00Z fredrik $ +# elementtree package + +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2007 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# --------------------------------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup.py Fri May 30 18:02:30 2008 +0200 @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# +# Setup script for the elementtree library +# $Id: setup.py 3239 2007-08-31 06:35:09Z fredrik $ +# +# Usage: python setup.py install +# + +from distutils.core import setup + +try: + # add download_url syntax to distutils + from distutils.dist import DistributionMetadata + DistributionMetadata.classifiers = None + DistributionMetadata.download_url = None +except: + pass + +DESCRIPTION="ElementTree - a light-weight XML object model for Python." + +LONG_DESCRIPTION="""\ +The Element type is a flexible container object, designed to store +hierarchical data structures in memory. Element structures can be +converted to and from XML.""" + +setup( + name="elementtree", + version=open("VERSION").read().strip(), + author="Fredrik Lundh", + author_email="fredrik@pythonware.com", + url="http://effbot.org/zone/element-index.htm", + description=DESCRIPTION, + long_description=LONG_DESCRIPTION, + download_url="http://effbot.org/downloads#elementtree", + license="Python (MIT style)", + packages=["elementtree"], + platforms="Python 1.5.2 and later.", + classifiers=[ + "Development Status :: 6 - Mature", + "Operating System :: OS Independent", + "Topic :: Text Processing :: Markup :: HTML", + "Topic :: Text Processing :: Markup :: XML", + ] + )