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
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
docs. Fredrik Lundh's page is also the location of the development version of
the xml.etree.ElementTree.
docs.
.. versionchanged:: 3.2
The ElementTree API is updated to 1.3. For more information, see
`Introducing ElementTree 1.3
<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:

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`
module. Use Unicode filenames, instead of bytes filenames, to not depend on
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

View File

@ -16,9 +16,9 @@ import html
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 @@ def simplefind():
"""
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 @@ def path_cache():
"""
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,38 @@ class CleanContext(object):
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
if hasattr(ET, '_namespace_map'):
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
ElementTree._namespace_map = self._nsmap.copy()
self._nsmap_copy = self._nsmap.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
self._nsmap.clear()
self._nsmap.update(self._nsmap_copy)
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()

View File

@ -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 @@ def sanity():
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 @@ def test_main():
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()

View File

@ -68,8 +68,9 @@ __all__ = [
"tostring", "tostringlist",
"TreeBuilder",
"VERSION",
"XML",
"XML", "XMLID",
"XMLParser", "XMLTreeBuilder",
"register_namespace",
]
VERSION = "1.3.0"
@ -148,9 +149,9 @@ class ParseError(SyntaxError):
# @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 @@ class XMLParser:
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

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,
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 *

View File

@ -1886,6 +1886,9 @@ Library
- Issue #12191: Added shutil.chown() to change user and/or group owner of a
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
-----

View File

@ -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 @@ xmlparser_setevents(XMLParserObject* self, PyObject* args)
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 @@ PyInit__elementtree(void)
#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);