diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -16,9 +16,9 @@ import unittest from test import support -from test.support import findfile +from test.support import findfile, import_fresh_module -from xml.etree import ElementTree as ET +pyET = import_fresh_module('xml.etree.ElementTree', blocked=['_elementtree']) SIMPLE_XMLFILE = findfile("simple.xml", subdir="xmltestdata") try: @@ -275,7 +275,7 @@ """ Test find methods using the elementpath fallback. - >>> from xml.etree import ElementTree + >>> ElementTree = pyET >>> CurrentElementPath = ElementTree.ElementPath >>> ElementTree.ElementPath = ElementTree._SimpleElementPath() @@ -460,17 +460,19 @@ """ Check that the path cache behaves sanely. + >>> from xml.etree import ElementPath + >>> elem = ET.XML(SAMPLE_XML) >>> for i in range(10): ET.ElementTree(elem).find('./'+str(i)) - >>> cache_len_10 = len(ET.ElementPath._cache) + >>> cache_len_10 = len(ElementPath._cache) >>> for i in range(10): ET.ElementTree(elem).find('./'+str(i)) - >>> len(ET.ElementPath._cache) == cache_len_10 + >>> len(ElementPath._cache) == cache_len_10 True >>> for i in range(20): ET.ElementTree(elem).find('./'+str(i)) - >>> len(ET.ElementPath._cache) > cache_len_10 + >>> len(ElementPath._cache) > cache_len_10 True >>> for i in range(600): ET.ElementTree(elem).find('./'+str(i)) - >>> len(ET.ElementPath._cache) < 500 + >>> len(ElementPath._cache) < 500 True """ @@ -1879,37 +1881,32 @@ self.checkwarnings = support.check_warnings(*deprecations, quiet=quiet) def __enter__(self): - from xml.etree import ElementTree - self._nsmap = ElementTree._namespace_map - self._path_cache = ElementTree.ElementPath._cache + from xml.etree import ElementPath # Copy the default namespace mapping - ElementTree._namespace_map = self._nsmap.copy() + self._nsmap = ET._namespace_map.copy() # Copy the path cache (should be empty) - ElementTree.ElementPath._cache = self._path_cache.copy() + self._path_cache = ElementPath._cache + ElementPath._cache = self._path_cache.copy() self.checkwarnings.__enter__() def __exit__(self, *args): - from xml.etree import ElementTree + from xml.etree import ElementPath # Restore mapping and path cache - ElementTree._namespace_map = self._nsmap - ElementTree.ElementPath._cache = self._path_cache + ET._namespace_map.clear() + ET._namespace_map.update(self._nsmap) + ElementPath._cache = self._path_cache self.checkwarnings.__exit__(*args) -def test_main(module_name='xml.etree.ElementTree'): +def test_main(module=pyET): from test import test_xml_etree - use_py_module = (module_name == 'xml.etree.ElementTree') - # The same doctests are used for both the Python and the C implementations - assert test_xml_etree.ET.__name__ == module_name + test_xml_etree.ET = module # XXX the C module should give the same warnings as the Python module - with CleanContext(quiet=not use_py_module): + with CleanContext(quiet=(module is not pyET)): support.run_doctest(test_xml_etree, verbosity=True) - # The module should not be changed by the tests - assert test_xml_etree.ET.__name__ == module_name - if __name__ == '__main__': test_main() diff --git a/Lib/test/test_xml_etree_c.py b/Lib/test/test_xml_etree_c.py --- a/Lib/test/test_xml_etree_c.py +++ b/Lib/test/test_xml_etree_c.py @@ -1,10 +1,9 @@ # xml.etree test for cElementTree from test import support -from test.support import bigmemtest, _2G import unittest -cET = support.import_module('xml.etree.cElementTree') +from xml.etree import ElementTree as cET, cElementTree as cET_alias # cElementTree specific tests @@ -13,10 +12,9 @@ r""" Import sanity. - >>> from xml.etree import cElementTree - Issue #6697. + >>> cElementTree = cET >>> e = cElementTree.Element('a') >>> getattr(e, '\uD800') # doctest: +ELLIPSIS Traceback (most recent call last): @@ -55,19 +53,10 @@ support.run_unittest(MiscTests) - # Assign the C implementation before running the doctests - # Patch the __name__, to prevent confusion with the pure Python test - pyET = test_xml_etree.ET - py__name__ = test_xml_etree.__name__ - test_xml_etree.ET = cET - if __name__ != '__main__': - test_xml_etree.__name__ = __name__ - try: - # Run the same test suite as xml.etree.ElementTree - test_xml_etree.test_main(module_name='xml.etree.cElementTree') - finally: - test_xml_etree.ET = pyET - test_xml_etree.__name__ = py__name__ + # Run the same test suite as the Python module + test_xml_etree.test_main(module=cET) + # Exercise the deprecated alias + test_xml_etree.test_main(module=cET_alias) if __name__ == '__main__': test_main() diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -68,8 +68,9 @@ "tostring", "tostringlist", "TreeBuilder", "VERSION", - "XML", + "XML", "XMLID", "XMLParser", "XMLTreeBuilder", + "register_namespace", ] VERSION = "1.3.0" @@ -148,9 +149,9 @@ # @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") + # FIXME: not sure about this; + # isinstance(element, Element) or look for tag/attrib/text attributes + return hasattr(element, 'tag') ## # Element class. This class defines the Element interface, and @@ -1684,6 +1685,87 @@ del self.target, self._parser # get rid of circular references return tree + +# Import the C accelerators +try: + # Element, SubElement, ParseError, TreeBuilder, XMLParser + from _elementtree import * +except ImportError: + pass +else: + # Overwrite 'ElementTree.parse' and 'iterparse' to use the C XMLParser + + class ElementTree(ElementTree): + def parse(self, source, parser=None): + close_source = False + if not hasattr(source, 'read'): + source = open(source, 'rb') + close_source = True + try: + if parser is not None: + while True: + data = source.read(65536) + if not data: + break + parser.feed(data) + self._root = parser.close() + else: + parser = XMLParser() + self._root = parser._parse(source) + return self._root + finally: + if close_source: + source.close() + + class iterparse: + root = None + def __init__(self, file, events=None): + self._close_file = False + if not hasattr(file, 'read'): + file = open(file, 'rb') + self._close_file = True + self._file = file + self._events = [] + self._index = 0 + self._error = None + self.root = self._root = None + b = TreeBuilder() + self._parser = XMLParser(b) + self._parser._setevents(self._events, events) + + def __next__(self): + while True: + try: + item = self._events[self._index] + self._index += 1 + return item + except IndexError: + pass + if self._error: + e = self._error + self._error = None + raise e + if self._parser is None: + self.root = self._root + if self._close_file: + self._file.close() + raise StopIteration + # load event buffer + del self._events[:] + self._index = 0 + data = self._file.read(16384) + if data: + try: + self._parser.feed(data) + except SyntaxError as exc: + self._error = exc + else: + self._root = self._parser.close() + self._parser = None + + def __iter__(self): + return self + # compatibility XMLTreeBuilder = XMLParser diff --git a/Lib/xml/etree/cElementTree.py b/Lib/xml/etree/cElementTree.py --- a/Lib/xml/etree/cElementTree.py +++ b/Lib/xml/etree/cElementTree.py @@ -1,153 +1,4 @@ -# Wrapper module for _elementtree +# Deprecated alias for xml.tree.ElementTree -from xml.etree.ElementTree import (ElementTree, dump, iselement, QName, - fromstringlist, - tostring, tostringlist, VERSION) -# These ones are not in ElementTree.__all__ -from xml.etree.ElementTree import ElementPath, register_namespace - -# Import the C accelerators: -# Element, SubElement, TreeBuilder, XMLParser, ParseError -from _elementtree import * - - -class ElementTree(ElementTree): - - def parse(self, source, parser=None): - close_source = False - if not hasattr(source, 'read'): - source = open(source, 'rb') - close_source = True - try: - if parser is not None: - while True: - data = source.read(65536) - if not data: - break - parser.feed(data) - self._root = parser.close() - else: - parser = XMLParser() - self._root = parser._parse(source) - return self._root - finally: - if close_source: - source.close() - - -class iterparse: - root = None - - def __init__(self, file, events=None): - self._close_file = False - if not hasattr(file, 'read'): - file = open(file, 'rb') - self._close_file = True - self._file = file - self._events = [] - self._index = 0 - self._error = None - self.root = self._root = None - b = TreeBuilder() - self._parser = XMLParser(b) - self._parser._setevents(self._events, events) - - def __next__(self): - while True: - try: - item = self._events[self._index] - self._index += 1 - return item - except IndexError: - pass - if self._error: - e = self._error - self._error = None - raise e - if self._parser is None: - self.root = self._root - if self._close_file: - self._file.close() - raise StopIteration - # load event buffer - del self._events[:] - self._index = 0 - data = self._file.read(16384) - if data: - try: - self._parser.feed(data) - except SyntaxError as exc: - self._error = exc - else: - self._root = self._parser.close() - self._parser = None - - def __iter__(self): - return self - - -# ============================================================================= -# -# Everything below this line can be removed -# after cElementTree is folded behind ElementTree. -# -# ============================================================================= - -from xml.etree.ElementTree import Comment as _Comment, PI as _PI - - -def parse(source, parser=None): - tree = ElementTree() - tree.parse(source, parser) - return tree - - -def XML(text, parser=None): - if not parser: - parser = XMLParser() - parser = XMLParser() - parser.feed(text) - return parser.close() - - -def XMLID(text, parser=None): - tree = XML(text, parser=parser) - ids = {} - for elem in tree.iter(): - id = elem.get('id') - if id: - ids[id] = elem - return tree, ids - - -class CommentProxy: - - def __call__(self, text=None): - element = Element(_Comment) - element.text = text - return element - - def __eq__(self, other): - return _Comment == other - - -class PIProxy: - - def __call__(self, target, text=None): - element = Element(_PI) - element.text = target - if text: - element.text = element.text + ' ' + text - return element - - def __eq__(self, other): - return _PI == other - - -Comment = CommentProxy() -PI = ProcessingInstruction = PIProxy() -del CommentProxy, PIProxy - -# Aliases -fromstring = XML -XMLTreeBuilder = XMLParser +from xml.etree.ElementTree import * +from xml.etree.ElementTree import _namespace_map diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -70,7 +70,7 @@ helps if you have lots of leaf nodes with attributes). */ /* Also note that pymalloc always allocates blocks in multiples of - eight bytes. For the current version of cElementTree, this means + eight bytes. For the current C version of ElementTree, this means that the number of children should be an even number, at least on 32-bit platforms. */ @@ -2649,7 +2649,7 @@ if (!TreeBuilder_CheckExact(self->target)) { PyErr_SetString( PyExc_TypeError, - "event handling only supported for cElementTree.Treebuilder " + "event handling only supported for ElementTree.TreeBuilder " "targets" ); return NULL; @@ -2906,7 +2906,7 @@ #endif elementtree_parseerror_obj = PyErr_NewException( - "cElementTree.ParseError", PyExc_SyntaxError, NULL + "xml.etree.ElementTree.ParseError", PyExc_SyntaxError, NULL ); Py_INCREF(elementtree_parseerror_obj); PyModule_AddObject(m, "ParseError", elementtree_parseerror_obj);