# -*- coding: iso-8859-1 -*- # xml.etree test for cElementTree. Except for a few cases noted in the # comments, these tests are identical to those in test_xml_etree.py. import doctest import sys import tempfile import shutil import os from test import test_support ET = test_support.import_module('xml.etree.cElementTree') SAMPLE_XML = """ text
subtext
""" BIGGER_SAMPLE_XML = """ text
subtext
""" SAMPLE_XML_NS = """ text
subtext
""" def sanity(): """ Import sanity. >>> from xml.etree import cElementTree """ def check_method(method): if not callable(method): print method, "not callable" def serialize(ET, elem, encoding=None): import StringIO file = StringIO.StringIO() tree = ET.ElementTree(elem) if encoding: tree.write(file, encoding) else: tree.write(file) return file.getvalue() def summarize(elem): return elem.tag def summarize_list(seq): return map(summarize, seq) def interface(): """ Test element tree interface. >>> element = ET.Element("tag", key="value") >>> tree = ET.ElementTree(element) Make sure all standard element methods exist. >>> check_method(element.append) >>> check_method(element.insert) >>> check_method(element.remove) >>> check_method(element.getchildren) >>> check_method(element.find) >>> check_method(element.findall) >>> check_method(element.findtext) >>> check_method(element.clear) >>> check_method(element.get) >>> check_method(element.set) >>> check_method(element.keys) >>> check_method(element.items) >>> check_method(element.getiterator) Basic method sanity checks. >>> serialize(ET, element) # 1 '' >>> subelement = ET.Element("subtag") >>> element.append(subelement) >>> serialize(ET, element) # 2 '' >>> element.insert(0, subelement) >>> serialize(ET, element) # 3 '' >>> element.remove(subelement) >>> serialize(ET, element) # 4 '' >>> element.remove(subelement) >>> serialize(ET, element) # 5 '' >>> element.remove(subelement) Traceback (most recent call last): ValueError: list.remove(x): x not in list >>> serialize(ET, element) # 6 '' """ def simplefind(): """ Test find methods using the elementpath fallback. NOTE: This monkey patch used to test this doesn't work in cElementTree """ def find(): """ Test find methods (including xpath syntax). >>> elem = ET.XML(BIGGER_SAMPLE_XML) >>> elem.find("tag").tag 'tag' >>> ET.ElementTree(elem).find("tag").tag 'tag' >>> elem.find("section/tag").tag 'tag' >>> elem.find("./tag").tag 'tag' >>> ET.ElementTree(elem).find("./tag").tag 'tag' >>> ET.ElementTree(elem).find("/tag").tag 'tag' >>> elem.find("section/nexttag").tag 'nexttag' >>> ET.ElementTree(elem).find("section/tag").tag 'tag' >>> ET.ElementTree(elem).find("tog") >>> ET.ElementTree(elem).find("tog/foo") >>> elem.findtext("tag") 'text' >>> elem.findtext("section/nexttag") '' >>> elem.findtext("section/nexttag", "default") '' >>> elem.findtext("tog") >>> elem.findtext("tog", "default") 'default' >>> ET.ElementTree(elem).findtext("tag") 'text' >>> ET.ElementTree(elem).findtext("tog/foo") >>> ET.ElementTree(elem).findtext("tog/foo", "default") 'default' >>> ET.ElementTree(elem).findtext("./tag") 'text' >>> ET.ElementTree(elem).findtext("/tag") 'text' >>> elem.findtext("section/tag") 'subtext' >>> ET.ElementTree(elem).findtext("section/tag") 'subtext' >>> summarize_list(elem.findall("tag")) ['tag', 'tag'] >>> summarize_list(elem.findall("tog")) [] >>> summarize_list(elem.findall("tog/foo")) [] >>> summarize_list(elem.findall("*")) ['tag', 'tag', 'section'] >>> summarize_list(elem.findall(".//tag")) ['tag', 'tag', 'tag', 'tag'] >>> summarize_list(elem.findall("section/tag")) ['tag'] >>> summarize_list(elem.findall("section//tag")) ['tag', 'tag'] >>> summarize_list(elem.findall("section/*")) ['tag', 'nexttag', 'nextsection'] >>> summarize_list(elem.findall("section//*")) ['tag', 'nexttag', 'nextsection', 'tag'] >>> summarize_list(elem.findall("section/.//*")) ['tag', 'nexttag', 'nextsection', 'tag'] >>> summarize_list(elem.findall("*/*")) ['tag', 'nexttag', 'nextsection'] >>> summarize_list(elem.findall("*//*")) ['tag', 'nexttag', 'nextsection', 'tag'] >>> summarize_list(elem.findall("*/tag")) ['tag'] >>> summarize_list(elem.findall("*/./tag")) ['tag'] >>> summarize_list(elem.findall("./tag")) ['tag', 'tag'] >>> summarize_list(elem.findall(".//tag")) ['tag', 'tag', 'tag', 'tag'] >>> summarize_list(elem.findall("././tag")) ['tag', 'tag'] >>> summarize_list(ET.ElementTree(elem).findall("/tag")) ['tag', 'tag'] >>> summarize_list(ET.ElementTree(elem).findall("./tag")) ['tag', 'tag'] >>> elem = ET.XML(SAMPLE_XML_NS) >>> summarize_list(elem.findall("tag")) [] >>> summarize_list(elem.findall("{http://effbot.org/ns}tag")) ['{http://effbot.org/ns}tag', '{http://effbot.org/ns}tag'] >>> summarize_list(elem.findall(".//{http://effbot.org/ns}tag")) ['{http://effbot.org/ns}tag', '{http://effbot.org/ns}tag', '{http://effbot.org/ns}tag'] """ def file_init(): """ Test initialising ElementTree from a file-like object >>> import StringIO >>> stringfile = StringIO.StringIO(BIGGER_SAMPLE_XML) >>> tree = ET.ElementTree(file=stringfile) >>> tree.find("tag").tag 'tag' >>> tree.find("section/tag").tag 'tag' """ class TemporaryFolder(object): """Context manager for working in a temporary folder.""" def __enter__(self): """Create temporary folder.""" self._tempdir = tempfile.mkdtemp(suffix="_test_xml_etree_include") self._oldcwd = os.getcwd() os.chdir(self._tempdir) def __exit__(self, type, value, traceback): """Remove temporary include files.""" os.chdir(self._oldcwd) shutil.rmtree(self._tempdir) def filename_init(): """ Test initialising ElementTree from a filename. >>> with TemporaryFolder(): ... file("temp.xml", "wb").write(BIGGER_SAMPLE_XML) ... tree = ET.ElementTree(file="temp.xml") ... tree.find("tag").tag ... tree.find("section/tag").tag 'tag' 'tag' """ def bad_find(): """ Check bad or unsupported path expressions. >>> elem = ET.XML(SAMPLE_XML) >>> elem.findall("/tag") Traceback (most recent call last): SyntaxError: cannot use absolute path on element >>> elem.findall("../tag") Traceback (most recent call last): SyntaxError: unsupported path syntax (..) >>> elem.findall("section//") Traceback (most recent call last): SyntaxError: path cannot end with // >>> elem.findall("tag[tag]") Traceback (most recent call last): SyntaxError: expected path separator ([) """ def path_cache(): """ Check that the path cache behaves sanely. >>> elem = ET.XML(SAMPLE_XML) >>> for i in range(10): ET.ElementTree(elem).find('/'+str(i)) >>> cache_len_10 = len(ET.ElementPath._cache) >>> for i in range(10): ET.ElementTree(elem).find('/'+str(i)) >>> len(ET.ElementPath._cache) == cache_len_10 True >>> for i in range(20): ET.ElementTree(elem).find('/'+str(i)) >>> len(ET.ElementPath._cache) > cache_len_10 True >>> for i in range(600): ET.ElementTree(elem).find('/'+str(i)) >>> len(ET.ElementPath._cache) < 500 True """ def parseliteral(): r""" >>> element = ET.XML("text") >>> ET.ElementTree(element).write(sys.stdout) text >>> element = ET.fromstring("text") >>> ET.ElementTree(element).write(sys.stdout) text >>> print ET.tostring(element) text >>> print ET.tostring(element, "ascii") text >>> _, ids = ET.XMLID("text") >>> len(ids) 0 >>> _, ids = ET.XMLID("text") >>> len(ids) 1 >>> ids["body"].tag 'body' """ def iterparse(): """ Test iterparse interface. >>> import cStringIO >>> source = cStringIO.StringIO(SAMPLE_XML) >>> iterparse = ET.iterparse >>> context = iterparse(source) >>> for action, elem in context: ... print action, elem.tag end tag end tag end tag end section end body >>> context.root.tag 'body' >>> source = cStringIO.StringIO(SAMPLE_XML_NS) >>> context = iterparse(source) >>> for action, elem in context: ... print action, elem.tag end {http://effbot.org/ns}tag end {http://effbot.org/ns}tag end {http://effbot.org/ns}tag end {http://effbot.org/ns}section end {http://effbot.org/ns}body >>> source = cStringIO.StringIO(SAMPLE_XML) >>> events = () >>> context = iterparse(source, events) >>> for action, elem in context: ... print action, elem.tag >>> source = cStringIO.StringIO(SAMPLE_XML) >>> events = () >>> context = iterparse(source, events=events) >>> for action, elem in context: ... print action, elem.tag >>> source = cStringIO.StringIO(SAMPLE_XML) >>> events = ("start", "end") >>> context = iterparse(source, events) >>> for action, elem in context: ... print action, elem.tag start body start tag end tag start tag end tag start section start tag end tag end section end body >>> source = cStringIO.StringIO(SAMPLE_XML_NS) >>> events = ("start", "end", "start-ns", "end-ns") >>> context = iterparse(source, events) >>> for action, elem in context: ... if action in ("start", "end"): ... print action, elem.tag ... else: ... print action, elem start-ns ('', 'http://effbot.org/ns') start {http://effbot.org/ns}body start {http://effbot.org/ns}tag end {http://effbot.org/ns}tag start {http://effbot.org/ns}tag end {http://effbot.org/ns}tag start {http://effbot.org/ns}section start {http://effbot.org/ns}tag end {http://effbot.org/ns}tag end {http://effbot.org/ns}section end {http://effbot.org/ns}body end-ns None NOTE: cElementTree returns a string instead of unicode here. #>>> source = cStringIO.StringIO('text') #>>> events = ("start-ns",) #>>> context = iterparse(source, events) #>>> for action, elem in context: #... print action, elem #start-ns ('', u'http://\\xe9ffbot.org/ns') """ def iterparse_filename(): """ Test iterparse interface on filename source. >>> with TemporaryFolder(): ... file("temp.xml", "wb").write(SAMPLE_XML) ... context = ET.iterparse("temp.xml") ... for action, elem in context: ... print action, elem.tag end tag end tag end tag end section end body """ def writefile(): """ >>> elem = ET.Element("tag") >>> elem.text = "text" >>> serialize(ET, elem) 'text' >>> ET.SubElement(elem, "subtag").text = "subtext" >>> serialize(ET, elem) 'textsubtext' >>> comment = ET.Comment("This is a comment") >>> elem.append(comment) >>> serialize(ET, elem) 'textsubtext' """ def writestring(): """ >>> elem = ET.XML("text") >>> ET.tostring(elem) 'text' >>> elem = ET.fromstring("text") >>> ET.tostring(elem) 'text' """ def writefilename(): """ >>> elem = ET.XML("text") >>> with TemporaryFolder(): ... ET.ElementTree(elem).write("temp.xml") ... file("temp.xml").read() 'text' """ def check_encoding(ET, encoding): """ >>> check_encoding(ET, "ascii") >>> check_encoding(ET, "us-ascii") >>> check_encoding(ET, "iso-8859-1") >>> check_encoding(ET, "iso-8859-15") >>> check_encoding(ET, "cp437") >>> check_encoding(ET, "mac-roman") """ ET.XML("" % encoding) def test_iterate(): """ Test ElementTree iterating interfaces >>> elem = ET.XML(SAMPLE_XML) >>> tree = ET.ElementTree(elem) >>> [e.tag for e in elem.getiterator()] ['body', 'tag', 'tag', 'section', 'tag'] >>> [e.tag for e in tree.getiterator()] ['body', 'tag', 'tag', 'section', 'tag'] Not sure whether this AssertionError is part of the documented interface, but it seems like it's worth testing to avoid regressions >>> tree = ET.ElementTree(None) >>> tree.getiterator() Traceback (most recent call last): AssertionError """ def test_children(): """ >>> elem = ET.XML(SAMPLE_XML) >>> len(elem.getchildren()) 3 >>> len(elem[2].getchildren()) 1 >>> elem[:] == elem.getchildren() True >>> child1 = elem[0] >>> child2 = elem[2] >>> del elem[1:2] >>> len(elem.getchildren()) 2 >>> child1 == elem[0] True >>> child2 == elem[1] True >>> elem[0:2] = [child2, child1] >>> child2 == elem[0] True >>> child1 == elem[1] True >>> child1 == elem[0] False >>> elem.clear() >>> elem.getchildren() [] """ def test_attrib(): """ >>> elem = ET.Element('test') >>> elem.text = "aa" >>> elem.set('testa', 'testval') >>> elem.set('testb', 'test2') >>> ET.tostring(elem) 'aa' >>> sorted(elem.keys()) ['testa', 'testb'] >>> sorted(elem.items()) [('testa', 'testval'), ('testb', 'test2')] >>> elem.attrib['testb'] 'test2' >>> elem.attrib['testb'] = 'test1' >>> elem.attrib['testc'] = 'test2' >>> ET.tostring(elem) 'aa' """ # # entity encoding tests ENTITY_XML = """\ %user-entities; ]> &entity; """ def check_entity_decoding(): """ Test entity handling. 1) good entities >>> e = ET.XML("test") >>> serialize(ET, e) 'test' 2) bad entities NOTE: cElementTree throws SyntaxError instead of ExpatError. #>>> ET.XML("&entity;") #Traceback (most recent call last): #ExpatError: undefined entity: line 1, column 10 # #>>> ET.XML(ENTITY_XML) #Traceback (most recent call last): #ExpatError: undefined entity &entity;: line 5, column 10 # #(add more tests here) """ # # XML namespace related tests def namespace(): """ Test namespace issues. 1) xml namespace >>> elem = ET.XML("") >>> serialize(ET, elem) # 1.1 '' 2) other "well-known" namespaces >>> elem = ET.XML("") >>> serialize(ET, elem) # 2.1 '' >>> elem = ET.XML("") >>> serialize(ET, elem) # 2.2 '' >>> elem = ET.XML("") >>> serialize(ET, elem) # 2.3 '' 3) unknown namespaces >>> elem = ET.XML(SAMPLE_XML_NS) >>> serialize(ET, elem) '\\n text\\n \\n \\n subtext\\n \\n' """ def qname(): """ Test QName handling. 1) decorated tags >>> elem = ET.Element("{uri}tag") >>> serialize(ET, elem) # 1.1 '' >>> elem = ET.Element(ET.QName("{uri}tag")) >>> serialize(ET, elem) # 1.2 '' >>> elem = ET.Element(ET.QName("uri", "tag")) >>> serialize(ET, elem) # 1.3 '' 2) decorated attributes >>> elem.clear() >>> elem.attrib["{uri}key"] = "value" >>> serialize(ET, elem) # 2.1 '' >>> elem.clear() >>> elem.attrib[ET.QName("{uri}key")] = "value" >>> serialize(ET, elem) # 2.2 '' 3) decorated values are not converted by default, but the QName wrapper can be used for values >>> elem.clear() >>> elem.attrib["{uri}key"] = "{uri}value" >>> serialize(ET, elem) # 3.1 '' >>> elem.clear() >>> elem.attrib["{uri}key"] = ET.QName("{uri}value") >>> serialize(ET, elem) # 3.2 '' >>> elem.clear() >>> subelem = ET.Element("tag") >>> subelem.attrib["{uri1}key"] = ET.QName("{uri2}value") >>> elem.append(subelem) >>> elem.append(subelem) >>> serialize(ET, elem) # 3.3 '' 4) Direct QName tests >>> str(ET.QName('ns', 'tag')) '{ns}tag' >>> str(ET.QName('{ns}tag')) '{ns}tag' >>> q1 = ET.QName('ns', 'tag') >>> q2 = ET.QName('ns', 'tag') >>> q1 == q2 True >>> q2 = ET.QName('ns', 'other-tag') >>> q1 == q2 False >>> q1 == 'ns:tag' False >>> q1 == '{ns}tag' True """ def doctype_public(): """ Test PUBLIC doctype. >>> elem = ET.XML('' ... 'text') """ # # xinclude tests (samples from appendix C of the xinclude specification) XINCLUDE = {} XINCLUDE["C1.xml"] = """\

120 Mz is adequate for an average home user.

""" XINCLUDE["disclaimer.xml"] = """\

The opinions represented herein represent those of the individual and should not be interpreted as official policy endorsed by this organization.

""" XINCLUDE["C2.xml"] = """\

This document has been accessed times.

""" XINCLUDE["count.txt"] = "324387" XINCLUDE["C3.xml"] = """\

The following is the source of the "data.xml" resource:

""" XINCLUDE["data.xml"] = """\ """ XINCLUDE["C5.xml"] = """\ """ XINCLUDE["default.xml"] = """\

Example.

""" def xinclude_loader(href, parse="xml", encoding=None): try: data = XINCLUDE[href] except KeyError: raise IOError("resource not found") if parse == "xml": from xml.etree.ElementTree import XML return XML(data) return data def xinclude(): r""" Basic inclusion example (XInclude C.1) >>> from xml.etree import ElementInclude >>> document = xinclude_loader("C1.xml") >>> ElementInclude.include(document, xinclude_loader) >>> print serialize(ET, document) # C1

120 Mz is adequate for an average home user.

The opinions represented herein represent those of the individual and should not be interpreted as official policy endorsed by this organization.

Textual inclusion example (XInclude C.2) >>> document = xinclude_loader("C2.xml") >>> ElementInclude.include(document, xinclude_loader) >>> print serialize(ET, document) # C2

This document has been accessed 324387 times.

Textual inclusion of XML example (XInclude C.3) >>> document = xinclude_loader("C3.xml") >>> ElementInclude.include(document, xinclude_loader) >>> print serialize(ET, document) # C3

The following is the source of the "data.xml" resource:

<?xml version='1.0'?> <data> <item><![CDATA[Brooks & Shields]]></item> </data>
Fallback example (XInclude C.5) Note! Fallback support is not yet implemented >>> document = xinclude_loader("C5.xml") >>> ElementInclude.include(document, xinclude_loader) Traceback (most recent call last): IOError: resource not found >>> # print serialize(ET, document) # C5 """ class TemporaryIncludes(object): """Context manager for temporary include files.""" def __enter__(self): """Create temporary include files.""" self._tempdir = tempfile.mkdtemp(suffix="_test_xml_etree_include") self._oldcwd = os.getcwd() os.chdir(self._tempdir) for filename, data in XINCLUDE.items(): file(filename, "wb").write(data) def __exit__(self, type, value, traceback): """Remove temporary include files.""" os.chdir(self._oldcwd) shutil.rmtree(self._tempdir) def xinclude_default_loader(): r""" Basic inclusion example using default loader (XInclude C.1) >>> from xml.etree import ElementInclude >>> with TemporaryIncludes(): ... document = ElementInclude.default_loader("C1.xml", "xml") ... ElementInclude.include(document) ... print serialize(ET, document) # C1

120 Mz is adequate for an average home user.

The opinions represented herein represent those of the individual and should not be interpreted as official policy endorsed by this organization.

Textual inclusion example using default loader (XInclude C.2) >>> with TemporaryIncludes(): ... document = ElementInclude.default_loader("C2.xml", "xml") ... ElementInclude.include(document) ... print serialize(ET, document) # C2

This document has been accessed 324387 times.

Textual inclusion of XML example using default loader (XInclude C.3) >>> with TemporaryIncludes(): ... document = ElementInclude.default_loader("C3.xml", "xml") ... ElementInclude.include(document) ... print serialize(ET, document) # C3

The following is the source of the "data.xml" resource:

<?xml version='1.0'?> <data> <item><![CDATA[Brooks & Shields]]></item> </data>
Fallback example using default loader (XInclude C.5) Note! Fallback support is not yet implemented >>> with TemporaryIncludes(): #doctest: +IGNORE_EXCEPTION_DETAIL ... document = ElementInclude.default_loader("C5.xml", "xml") ... ElementInclude.include(document) ... Traceback (most recent call last): ... IOError: [Errno 2] No such file or directory: 'example.txt' >>> # print serialize(ET, document) # C5 Test default loader with encoding. >>> with TemporaryIncludes(): ... document = ElementInclude.default_loader("count.txt", "text", encoding="ascii") ... print document # count.txt 324387 """ # # badly formatted xi:include tags XINCLUDE_BAD = {} XINCLUDE_BAD["B1.xml"] = """\

120 Mz is adequate for an average home user.

""" XINCLUDE_BAD["B2.xml"] = """\
""" def xinclude_failures(): r""" Test failure to locate included XML file. >>> from xml.etree import ElementInclude >>> def none_loader(href, parser, encoding=None): ... return None ... >>> document = ET.XML(XINCLUDE["C1.xml"]) >>> ElementInclude.include(document, loader=none_loader) Traceback (most recent call last): ... FatalIncludeError: cannot load 'disclaimer.xml' as 'xml' Test failure to locate included text file. >>> document = ET.XML(XINCLUDE["C2.xml"]) >>> ElementInclude.include(document, loader=none_loader) Traceback (most recent call last): ... FatalIncludeError: cannot load 'count.txt' as 'text' Test bad parse type. >>> document = ET.XML(XINCLUDE_BAD["B1.xml"]) >>> ElementInclude.include(document, loader=none_loader) Traceback (most recent call last): ... FatalIncludeError: unknown parse type in xi:include tag ('BAD_TYPE') Test xi:fallback outside xi:include. >>> document = ET.XML(XINCLUDE_BAD["B2.xml"]) >>> ElementInclude.include(document, loader=none_loader) Traceback (most recent call last): ... FatalIncludeError: xi:fallback tag must be child of xi:include ('{http://www.w3.org/2001/XInclude}fallback') """ # -------------------------------------------------------------------- # reported bugs def bug_xmltoolkit21(): """ marshaller gives obscure errors for non-string values >>> elem = ET.Element(123) >>> serialize(ET, elem) # tag Traceback (most recent call last): TypeError: cannot serialize 123 (type int) >>> elem = ET.Element("elem") >>> elem.text = 123 >>> serialize(ET, elem) # text Traceback (most recent call last): TypeError: cannot serialize 123 (type int) >>> elem = ET.Element("elem") >>> elem.tail = 123 >>> serialize(ET, elem) # tail Traceback (most recent call last): TypeError: cannot serialize 123 (type int) >>> elem = ET.Element("elem") >>> elem.set(123, "123") >>> serialize(ET, elem) # attribute key Traceback (most recent call last): TypeError: cannot serialize 123 (type int) >>> elem = ET.Element("elem") >>> elem.set("123", 123) >>> serialize(ET, elem) # attribute value Traceback (most recent call last): TypeError: cannot serialize 123 (type int) """ def bug_xmltoolkit25(): """ typo in ElementTree.findtext >>> tree = ET.XML(SAMPLE_XML) >>> tree.findtext("tag") 'text' >>> tree.findtext("section/tag") 'subtext' """ def bug_xmltoolkit28(): """ .//tag causes exceptions >>> tree = ET.XML("
") >>> summarize_list(tree.findall(".//thead")) [] >>> summarize_list(tree.findall(".//tbody")) ['tbody'] """ def bug_xmltoolkitX1(): """ dump() doesn't flush the output buffer >>> tree = ET.XML("
") >>> ET.dump(tree); sys.stdout.write("tail")
tail """ def bug_xmltoolkit54(): """ problems handling internally defined entities >>> e = ET.XML("]>&ldots;") >>> serialize(ET, e) '' """ def bug_xmltoolkit55(): """ make sure we're reporting the first error, not the last NOTE: This bug still exists in cElementTree #>>> e = ET.XML("&ldots;&ndots;&rdots;") #Traceback (most recent call last): #ExpatError: undefined entity &ldots;: line 1, column 36 """ def bug_xmltoolkit39(): """ non-ascii element and attribute names doesn't work >>> tree = ET.XML("") >>> ET.tostring(tree, "utf-8") '' >>> tree = ET.XML("") >>> tree.attrib {u'\\xe4ttr': u'v\\xe4lue'} >>> ET.tostring(tree, "utf-8") '' >>> tree = ET.XML("text") >>> ET.tostring(tree, "utf-8") 'text' >>> tree = ET.Element(u"t\\xe4g") >>> ET.tostring(tree, "utf-8") '' >>> tree = ET.Element("tag") >>> tree.set(u"\\xe4ttr", u"v\\xe4lue") >>> ET.tostring(tree, "utf-8") '' """ def bug_1534630(): """ >>> bob = ET.TreeBuilder() >>> e = bob.data("data") >>> e = bob.start("tag", {}) >>> e = bob.end("tag") >>> e = bob.close() >>> serialize(ET, e) '' """ def test_main(): from test import test_xml_etree_c test_support.run_doctest(test_xml_etree_c, verbosity=True) if __name__ == '__main__': test_main()