bpo-30365: Backport warnings and fix bugs in ElementTree. (#1581)
Running Python with the -3 option now emits deprecation warnings for
getchildren() and getiterator() methods of the Element class in the
xml.etree.cElementTree module and when pass the html argument to
xml.etree.ElementTree.XMLParser().
Fixed a deprecation warning about the doctype() method of the
xml.etree.ElementTree.XMLParser class. Now it is emitted only when
define the doctype() method in the subclass of XMLParser.
Fixed a bug in the test_bug_200708_close test method. An EchoTarget
instance was incorrectly passed to XMLParser() as the html argument and
silently ignored.
Tests no longer failed when use the -m option for running only selected
test methods. Checking warnings now is more specific, warnings are
expected only when use deprecated features.
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
index 201266a..60b26ea 100644
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -8,6 +8,7 @@
import cgi
import copy
+import functools
import io
import pickle
import StringIO
@@ -86,6 +87,16 @@
"""
+def checkwarnings(*filters):
+ def decorator(test):
+ def newtest(*args, **kwargs):
+ with support.check_warnings(*filters):
+ test(*args, **kwargs)
+ functools.update_wrapper(newtest, test)
+ return newtest
+ return decorator
+
+
class ModuleTest(unittest.TestCase):
# TODO: this should be removed once we get rid of the global module vars
@@ -656,6 +667,10 @@
])
+ # Element.getchildren() and ElementTree.getiterator() are deprecated.
+ @checkwarnings(("This method will be removed in future versions. "
+ "Use .+ instead.",
+ (DeprecationWarning, PendingDeprecationWarning)))
def test_getchildren(self):
# Test Element.getchildren()
@@ -1356,9 +1371,15 @@
# Test custom builder.
class EchoTarget:
+ def start(self, tag, attrib):
+ pass
+ def end(self, tag):
+ pass
+ def data(self, text):
+ pass
def close(self):
return ET.Element("element") # simulate root
- parser = ET.XMLParser(EchoTarget())
+ parser = ET.XMLParser(target=EchoTarget())
parser.feed("<element>some text</element>")
self.assertEqual(parser.close().tag, 'element')
@@ -1908,7 +1929,12 @@
e = ET.XML(SAMPLE_XML)
self.assertEqual(ET.ElementTree(e).find('tag').tag, 'tag')
self.assertEqual(ET.ElementTree(e).find('./tag').tag, 'tag')
- self.assertEqual(ET.ElementTree(e).find('/tag').tag, 'tag')
+ # this produces a warning
+ msg = ("This search is broken in 1.3 and earlier, and will be fixed "
+ "in a future version. If you rely on the current behaviour, "
+ "change it to '.+'")
+ with support.check_warnings((msg, FutureWarning)):
+ self.assertEqual(ET.ElementTree(e).find('/tag').tag, 'tag')
e[2] = ET.XML(SAMPLE_SECTION)
self.assertEqual(ET.ElementTree(e).find('section/tag').tag, 'tag')
self.assertIsNone(ET.ElementTree(e).find('tog'))
@@ -1919,14 +1945,15 @@
self.assertEqual(ET.ElementTree(e).findtext('tog/foo', 'default'),
'default')
self.assertEqual(ET.ElementTree(e).findtext('./tag'), 'text')
- self.assertEqual(ET.ElementTree(e).findtext('/tag'), 'text')
+ with support.check_warnings((msg, FutureWarning)):
+ self.assertEqual(ET.ElementTree(e).findtext('/tag'), 'text')
self.assertEqual(ET.ElementTree(e).findtext('section/tag'), 'subtext')
self.assertEqual(summarize_list(ET.ElementTree(e).findall('./tag')),
['tag'] * 2)
- # this produces a warning
- self.assertEqual(summarize_list(ET.ElementTree(e).findall('/tag')),
- ['tag'] * 2)
+ with support.check_warnings((msg, FutureWarning)):
+ it = ET.ElementTree(e).findall('/tag')
+ self.assertEqual(summarize_list(it), ['tag'] * 2)
class ElementIterTest(unittest.TestCase):
@@ -2014,6 +2041,15 @@
self.assertEqual(self._ilist(doc, '*'), all_tags)
def test_getiterator(self):
+ # Element.getiterator() is deprecated.
+ if sys.py3kwarning or ET is pyET:
+ with support.check_warnings(("This method will be removed in future versions. "
+ "Use .+ instead.", PendingDeprecationWarning)):
+ self._test_getiterator()
+ else:
+ self._test_getiterator()
+
+ def _test_getiterator(self):
doc = ET.XML('''
<document>
<house>
@@ -2178,13 +2214,13 @@
def test_constructor_args(self):
# Positional args. The first (html) is not supported, but should be
# nevertheless correctly accepted.
- parser = ET.XMLParser(None, ET.TreeBuilder(), 'utf-8')
+ with support.check_py3k_warnings((r'.*\bhtml\b', DeprecationWarning)):
+ parser = ET.XMLParser(None, ET.TreeBuilder(), 'utf-8')
parser.feed(self.sample1)
self._check_sample_element(parser.close())
# Now as keyword args.
parser2 = ET.XMLParser(encoding='utf-8',
- html=[{}],
target=ET.TreeBuilder())
parser2.feed(self.sample1)
self._check_sample_element(parser2.close())
@@ -2593,44 +2629,6 @@
# --------------------------------------------------------------------
-class CleanContext(object):
- """Provide default namespace mapping and path cache."""
- checkwarnings = None
-
- def __init__(self, quiet=False):
- deprecations = (
- ("This method of XMLParser is deprecated. Define doctype\(\) "
- "method on the TreeBuilder target.", DeprecationWarning),
- # Search behaviour is broken if search path starts with "/".
- ("This search is broken in 1.3 and earlier, and will be fixed "
- "in a future version. If you rely on the current behaviour, "
- "change it to '.+'", FutureWarning),
- # Element.getchildren() and Element.getiterator() are deprecated.
- ("This method will be removed in future versions. "
- "Use .+ instead.", DeprecationWarning),
- ("This method will be removed in future versions. "
- "Use .+ instead.", PendingDeprecationWarning))
- self.checkwarnings = support.check_warnings(*deprecations, quiet=quiet)
-
- def __enter__(self):
- from xml.etree import ElementPath
- self._nsmap = pyET._namespace_map
- # Copy the default namespace mapping
- self._nsmap_copy = self._nsmap.copy()
- # Copy the path cache (should be empty)
- self._path_cache = ElementPath._cache
- ElementPath._cache = self._path_cache.copy()
- self.checkwarnings.__enter__()
-
- def __exit__(self, *args):
- from xml.etree import ElementPath
- # Restore mapping and path cache
- self._nsmap.clear()
- self._nsmap.update(self._nsmap_copy)
- ElementPath._cache = self._path_cache
- self.checkwarnings.__exit__(*args)
-
-
def test_main(module=None):
# When invoked without a module, runs the Python ET tests by loading pyET.
# Otherwise, uses the given module as the ET.
@@ -2666,11 +2664,22 @@
NoAcceleratorTest,
])
+ # Provide default namespace mapping and path cache.
+ from xml.etree import ElementPath
+ nsmap = pyET._namespace_map
+ # Copy the default namespace mapping
+ nsmap_copy = nsmap.copy()
+ # Copy the path cache (should be empty)
+ path_cache = ElementPath._cache
+ ElementPath._cache = path_cache.copy()
try:
- # XXX the C module should give the same warnings as the Python module
- with CleanContext(quiet=(pyET is not ET)):
- support.run_unittest(*test_classes)
+ support.run_unittest(*test_classes)
finally:
+ from xml.etree import ElementPath
+ # Restore mapping and path cache
+ nsmap.clear()
+ nsmap.update(nsmap_copy)
+ ElementPath._cache = path_cache
# don't interfere with subsequent tests
ET = None
diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py
index cf6402f..dca6910 100644
--- a/Lib/xml/etree/ElementTree.py
+++ b/Lib/xml/etree/ElementTree.py
@@ -1450,6 +1450,8 @@
self._tail = 1
return self._last
+_sentinel = ['sentinel']
+
##
# Element structure builder for XML source data, based on the
# <b>expat</b> parser.
@@ -1465,7 +1467,11 @@
class XMLParser(object):
- def __init__(self, html=0, target=None, encoding=None):
+ def __init__(self, html=_sentinel, target=None, encoding=None):
+ if html is not _sentinel:
+ warnings.warnpy3k(
+ "The html argument of XMLParser() is deprecated",
+ DeprecationWarning, stacklevel=2)
try:
from xml.parsers import expat
except ImportError:
@@ -1617,7 +1623,7 @@
pubid = pubid[1:-1]
if hasattr(self.target, "doctype"):
self.target.doctype(name, pubid, system[1:-1])
- elif self.doctype is not self._XMLParser__doctype:
+ elif self.doctype != self._XMLParser__doctype:
# warn about deprecated call
self._XMLParser__doctype(name, pubid, system[1:-1])
self.doctype(name, pubid, system[1:-1])
diff --git a/Misc/NEWS b/Misc/NEWS
index c3fb3c8..dd6ec1b 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -42,6 +42,15 @@
Library
-------
+- bpo-30365: Running Python with the -3 option now emits deprecation warnings
+ for getchildren() and getiterator() methods of the Element class in the
+ xml.etree.cElementTree module and when pass the html argument to
+ xml.etree.ElementTree.XMLParser().
+
+- bpo-30365: Fixed a deprecation warning about the doctype() method of the
+ xml.etree.ElementTree.XMLParser class. Now it is emitted only when define
+ the doctype() method in the subclass of XMLParser.
+
- bpo-30329: imaplib now catchs the Windows socket WSAEINVAL error
(code 10022) on shutdown(SHUT_RDWR): An invalid operation was attempted.
This error occurs sometimes on SSL connections.
diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c
index 0a01c3c..b9e9b3a 100644
--- a/Modules/_elementtree.c
+++ b/Modules/_elementtree.c
@@ -962,7 +962,11 @@
int i;
PyObject* list;
- /* FIXME: report as deprecated? */
+ if (PyErr_WarnPy3k("This method will be removed in future versions. "
+ "Use 'list(elem)' or iteration over elem instead.",
+ 1) < 0) {
+ return NULL;
+ }
if (!PyArg_ParseTuple(args, ":getchildren"))
return NULL;
@@ -984,13 +988,10 @@
}
static PyObject*
-element_iter(ElementObject* self, PyObject* args)
+element_iter_impl(ElementObject* self, PyObject* tag)
{
+ PyObject* args;
PyObject* result;
-
- PyObject* tag = Py_None;
- if (!PyArg_ParseTuple(args, "|O:iter", &tag))
- return NULL;
if (!elementtree_iter_obj) {
PyErr_SetString(
@@ -1014,6 +1015,34 @@
return result;
}
+static PyObject*
+element_iter(ElementObject* self, PyObject* args)
+{
+ PyObject* tag = Py_None;
+ if (!PyArg_ParseTuple(args, "|O:iter", &tag))
+ return NULL;
+
+ return element_iter_impl(self, tag);
+}
+
+static PyObject*
+element_getiterator(ElementObject* self, PyObject* args)
+{
+ PyObject* tag = Py_None;
+ if (!PyArg_ParseTuple(args, "|O:getiterator", &tag))
+ return NULL;
+
+ /* Change for a DeprecationWarning in 1.4 */
+ if (Py_Py3kWarningFlag &&
+ PyErr_WarnEx(PyExc_PendingDeprecationWarning,
+ "This method will be removed in future versions. "
+ "Use 'tree.iter()' or 'list(tree.iter())' instead.",
+ 1) < 0) {
+ return NULL;
+ }
+ return element_iter_impl(self, tag);
+}
+
static PyObject*
element_itertext(ElementObject* self, PyObject* args)
@@ -1510,7 +1539,7 @@
{"itertext", (PyCFunction) element_itertext, METH_VARARGS},
{"iterfind", (PyCFunction) element_iterfind, METH_VARARGS},
- {"getiterator", (PyCFunction) element_iter, METH_VARARGS},
+ {"getiterator", (PyCFunction) element_getiterator, METH_VARARGS},
{"getchildren", (PyCFunction) element_getchildren, METH_VARARGS},
{"items", (PyCFunction) element_items, METH_VARARGS},