Issue #13988: cElementTree is deprecated and the _elementtree accelerator is automatically used whenever available.

This commit is contained in:
Florent Xicluna 2012-02-13 11:03:30 +01:00
parent d1c7b1afe8
commit a72a98f24a
8 changed files with 133 additions and 203 deletions

View File

@ -32,17 +32,18 @@ To create an element instance, use the :class:`Element` constructor or the
The :class:`ElementTree` class can be used to wrap an element structure, and The :class:`ElementTree` class can be used to wrap an element structure, and
convert it from and to XML. convert it from and to XML.
A C implementation of this API is available as :mod:`xml.etree.cElementTree`.
See http://effbot.org/zone/element-index.htm for tutorials and links to other See http://effbot.org/zone/element-index.htm for tutorials and links to other
docs. Fredrik Lundh's page is also the location of the development version of docs.
the xml.etree.ElementTree.
.. versionchanged:: 3.2 .. versionchanged:: 3.2
The ElementTree API is updated to 1.3. For more information, see The ElementTree API is updated to 1.3. For more information, see
`Introducing ElementTree 1.3 `Introducing ElementTree 1.3
<http://effbot.org/zone/elementtree-13-intro.htm>`_. <http://effbot.org/zone/elementtree-13-intro.htm>`_.
.. versionchanged:: 3.3
This module will use a fast implementation whenever available.
The :mod:`xml.etree.cElementTree` module is deprecated.
.. _elementtree-functions: .. _elementtree-functions:

View File

@ -842,6 +842,8 @@ Deprecated Python modules, functions and methods
* :issue:`13374`: The Windows bytes API has been deprecated in the :mod:`os` * :issue:`13374`: The Windows bytes API has been deprecated in the :mod:`os`
module. Use Unicode filenames, instead of bytes filenames, to not depend on module. Use Unicode filenames, instead of bytes filenames, to not depend on
the ANSI code page anymore and to support any filename. the ANSI code page anymore and to support any filename.
* :issue:`13988`: The :mod:`xml.etree.cElementTree` module is deprecated. The
accelerator is used automatically whenever available.
Deprecated functions and types of the C API Deprecated functions and types of the C API

View File

@ -16,9 +16,9 @@ import html
import unittest import unittest
from test import support 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") SIMPLE_XMLFILE = findfile("simple.xml", subdir="xmltestdata")
try: try:
@ -275,7 +275,7 @@ def simplefind():
""" """
Test find methods using the elementpath fallback. Test find methods using the elementpath fallback.
>>> from xml.etree import ElementTree >>> ElementTree = pyET
>>> CurrentElementPath = ElementTree.ElementPath >>> CurrentElementPath = ElementTree.ElementPath
>>> ElementTree.ElementPath = ElementTree._SimpleElementPath() >>> ElementTree.ElementPath = ElementTree._SimpleElementPath()
@ -460,17 +460,19 @@ def path_cache():
""" """
Check that the path cache behaves sanely. Check that the path cache behaves sanely.
>>> from xml.etree import ElementPath
>>> elem = ET.XML(SAMPLE_XML) >>> elem = ET.XML(SAMPLE_XML)
>>> for i in range(10): ET.ElementTree(elem).find('./'+str(i)) >>> 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)) >>> 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 True
>>> for i in range(20): ET.ElementTree(elem).find('./'+str(i)) >>> 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 True
>>> for i in range(600): ET.ElementTree(elem).find('./'+str(i)) >>> for i in range(600): ET.ElementTree(elem).find('./'+str(i))
>>> len(ET.ElementPath._cache) < 500 >>> len(ElementPath._cache) < 500
True True
""" """
@ -1879,37 +1881,38 @@ class CleanContext(object):
self.checkwarnings = support.check_warnings(*deprecations, quiet=quiet) self.checkwarnings = support.check_warnings(*deprecations, quiet=quiet)
def __enter__(self): def __enter__(self):
from xml.etree import ElementTree from xml.etree import ElementPath
self._nsmap = ElementTree._namespace_map if hasattr(ET, '_namespace_map'):
self._path_cache = ElementTree.ElementPath._cache self._nsmap = ET._namespace_map
else:
# when testing the cElementTree alias
from xml.etree.ElementTree import _namespace_map
self._nsmap = _namespace_map
# Copy the default namespace mapping # Copy the default namespace mapping
ElementTree._namespace_map = self._nsmap.copy() self._nsmap_copy = self._nsmap.copy()
# Copy the path cache (should be empty) # 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__() self.checkwarnings.__enter__()
def __exit__(self, *args): def __exit__(self, *args):
from xml.etree import ElementTree from xml.etree import ElementPath
# Restore mapping and path cache # Restore mapping and path cache
ElementTree._namespace_map = self._nsmap self._nsmap.clear()
ElementTree.ElementPath._cache = self._path_cache self._nsmap.update(self._nsmap_copy)
ElementPath._cache = self._path_cache
self.checkwarnings.__exit__(*args) self.checkwarnings.__exit__(*args)
def test_main(module_name='xml.etree.ElementTree'): def test_main(module=pyET):
from test import test_xml_etree 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 # 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 # 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) 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__': if __name__ == '__main__':
test_main() test_main()

View File

@ -1,10 +1,9 @@
# xml.etree test for cElementTree # xml.etree test for cElementTree
from test import support from test import support
from test.support import bigmemtest, _2G
import unittest import unittest
cET = support.import_module('xml.etree.cElementTree') from xml.etree import ElementTree as cET, cElementTree as cET_alias
# cElementTree specific tests # cElementTree specific tests
@ -13,10 +12,9 @@ def sanity():
r""" r"""
Import sanity. Import sanity.
>>> from xml.etree import cElementTree
Issue #6697. Issue #6697.
>>> cElementTree = cET
>>> e = cElementTree.Element('a') >>> e = cElementTree.Element('a')
>>> getattr(e, '\uD800') # doctest: +ELLIPSIS >>> getattr(e, '\uD800') # doctest: +ELLIPSIS
Traceback (most recent call last): Traceback (most recent call last):
@ -55,19 +53,10 @@ def test_main():
support.run_unittest(MiscTests) support.run_unittest(MiscTests)
# Assign the C implementation before running the doctests # Run the same test suite as the Python module
# Patch the __name__, to prevent confusion with the pure Python test test_xml_etree.test_main(module=cET)
pyET = test_xml_etree.ET # Exercise the deprecated alias
py__name__ = test_xml_etree.__name__ test_xml_etree.test_main(module=cET_alias)
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__
if __name__ == '__main__': if __name__ == '__main__':
test_main() test_main()

View File

@ -68,8 +68,9 @@ __all__ = [
"tostring", "tostringlist", "tostring", "tostringlist",
"TreeBuilder", "TreeBuilder",
"VERSION", "VERSION",
"XML", "XML", "XMLID",
"XMLParser", "XMLTreeBuilder", "XMLParser", "XMLTreeBuilder",
"register_namespace",
] ]
VERSION = "1.3.0" VERSION = "1.3.0"
@ -148,9 +149,9 @@ class ParseError(SyntaxError):
# @defreturn flag # @defreturn flag
def iselement(element): def iselement(element):
# FIXME: not sure about this; might be a better idea to look # FIXME: not sure about this;
# for tag/attrib/text attributes # isinstance(element, Element) or look for tag/attrib/text attributes
return isinstance(element, Element) or hasattr(element, "tag") return hasattr(element, 'tag')
## ##
# Element class. This class defines the Element interface, and # Element class. This class defines the Element interface, and
@ -1684,6 +1685,87 @@ class XMLParser:
del self.target, self._parser # get rid of circular references del self.target, self._parser # get rid of circular references
return tree 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 # compatibility
XMLTreeBuilder = XMLParser XMLTreeBuilder = XMLParser

View File

@ -1,153 +1,3 @@
# Wrapper module for _elementtree # Deprecated alias for xml.etree.ElementTree
from xml.etree.ElementTree import (ElementTree, dump, iselement, QName, from xml.etree.ElementTree import *
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

View File

@ -1886,6 +1886,9 @@ Library
- Issue #12191: Added shutil.chown() to change user and/or group owner of a - Issue #12191: Added shutil.chown() to change user and/or group owner of a
given path also specifying their names. given path also specifying their names.
- Issue #13988: The _elementtree accelerator is used whenever available.
Now xml.etree.cElementTree becomes a deprecated alias to ElementTree.
Build Build
----- -----

View File

@ -70,7 +70,7 @@
helps if you have lots of leaf nodes with attributes). */ helps if you have lots of leaf nodes with attributes). */
/* Also note that pymalloc always allocates blocks in multiples of /* 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 that the number of children should be an even number, at least on
32-bit platforms. */ 32-bit platforms. */
@ -2649,7 +2649,7 @@ xmlparser_setevents(XMLParserObject* self, PyObject* args)
if (!TreeBuilder_CheckExact(self->target)) { if (!TreeBuilder_CheckExact(self->target)) {
PyErr_SetString( PyErr_SetString(
PyExc_TypeError, PyExc_TypeError,
"event handling only supported for cElementTree.Treebuilder " "event handling only supported for ElementTree.TreeBuilder "
"targets" "targets"
); );
return NULL; return NULL;
@ -2906,7 +2906,7 @@ PyInit__elementtree(void)
#endif #endif
elementtree_parseerror_obj = PyErr_NewException( elementtree_parseerror_obj = PyErr_NewException(
"cElementTree.ParseError", PyExc_SyntaxError, NULL "xml.etree.ElementTree.ParseError", PyExc_SyntaxError, NULL
); );
Py_INCREF(elementtree_parseerror_obj); Py_INCREF(elementtree_parseerror_obj);
PyModule_AddObject(m, "ParseError", elementtree_parseerror_obj); PyModule_AddObject(m, "ParseError", elementtree_parseerror_obj);