Commit 762ec97e authored by Serhiy Storchaka's avatar Serhiy Storchaka Committed by GitHub

bpo-29204: Emit warnings for already deprecated ElementTree features. (#773)

Element.getiterator() and the html parameter of XMLParser() were
deprecated only in the documentation (since Python 3.2 and 3.4 correspondintly).
Now using them emits a deprecation warning.

* Don’t need check_warnings any more.
parent 722a3af0
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
# monkey-patched when running the "test_xml_etree_c" test suite. # monkey-patched when running the "test_xml_etree_c" test suite.
import copy import copy
import functools
import html import html
import io import io
import operator import operator
...@@ -90,6 +91,16 @@ ENTITY_XML = """\ ...@@ -90,6 +91,16 @@ ENTITY_XML = """\
""" """
def checkwarnings(*filters, quiet=False):
def decorator(test):
def newtest(*args, **kwargs):
with support.check_warnings(*filters, quiet=quiet):
test(*args, **kwargs)
functools.update_wrapper(newtest, test)
return newtest
return decorator
class ModuleTest(unittest.TestCase): class ModuleTest(unittest.TestCase):
def test_sanity(self): def test_sanity(self):
# Import sanity. # Import sanity.
...@@ -690,6 +701,10 @@ class ElementTreeTest(unittest.TestCase): ...@@ -690,6 +701,10 @@ class ElementTreeTest(unittest.TestCase):
]) ])
# Element.getchildren() and ElementTree.getiterator() are deprecated.
@checkwarnings(("This method will be removed in future versions. "
"Use .+ instead.",
(DeprecationWarning, PendingDeprecationWarning)))
def test_getchildren(self): def test_getchildren(self):
# Test Element.getchildren() # Test Element.getchildren()
...@@ -1558,7 +1573,7 @@ class BugsTest(unittest.TestCase): ...@@ -1558,7 +1573,7 @@ class BugsTest(unittest.TestCase):
class EchoTarget: class EchoTarget:
def close(self): def close(self):
return ET.Element("element") # simulate root return ET.Element("element") # simulate root
parser = ET.XMLParser(EchoTarget()) parser = ET.XMLParser(target=EchoTarget())
parser.feed("<element>some text</element>") parser.feed("<element>some text</element>")
self.assertEqual(parser.close().tag, 'element') self.assertEqual(parser.close().tag, 'element')
...@@ -2225,8 +2240,12 @@ class ElementFindTest(unittest.TestCase): ...@@ -2225,8 +2240,12 @@ class ElementFindTest(unittest.TestCase):
self.assertEqual(summarize_list(ET.ElementTree(e).findall('tag')), self.assertEqual(summarize_list(ET.ElementTree(e).findall('tag')),
['tag'] * 2) ['tag'] * 2)
# this produces a warning # this produces a warning
self.assertEqual(summarize_list(ET.ElementTree(e).findall('//tag')), msg = ("This search is broken in 1.3 and earlier, and will be fixed "
['tag'] * 3) "in a future version. If you rely on the current behaviour, "
"change it to '.+'")
with self.assertWarnsRegex(FutureWarning, msg):
it = ET.ElementTree(e).findall('//tag')
self.assertEqual(summarize_list(it), ['tag'] * 3)
class ElementIterTest(unittest.TestCase): class ElementIterTest(unittest.TestCase):
...@@ -2311,6 +2330,9 @@ class ElementIterTest(unittest.TestCase): ...@@ -2311,6 +2330,9 @@ class ElementIterTest(unittest.TestCase):
self.assertEqual(self._ilist(doc), all_tags) self.assertEqual(self._ilist(doc), all_tags)
self.assertEqual(self._ilist(doc, '*'), all_tags) self.assertEqual(self._ilist(doc, '*'), all_tags)
# Element.getiterator() is deprecated.
@checkwarnings(("This method will be removed in future versions. "
"Use .+ instead.", PendingDeprecationWarning))
def test_getiterator(self): def test_getiterator(self):
doc = ET.XML(''' doc = ET.XML('''
<document> <document>
...@@ -2493,13 +2515,13 @@ class XMLParserTest(unittest.TestCase): ...@@ -2493,13 +2515,13 @@ class XMLParserTest(unittest.TestCase):
def test_constructor_args(self): def test_constructor_args(self):
# Positional args. The first (html) is not supported, but should be # Positional args. The first (html) is not supported, but should be
# nevertheless correctly accepted. # nevertheless correctly accepted.
parser = ET.XMLParser(None, ET.TreeBuilder(), 'utf-8') with self.assertWarnsRegex(DeprecationWarning, r'\bhtml\b'):
parser = ET.XMLParser(None, ET.TreeBuilder(), 'utf-8')
parser.feed(self.sample1) parser.feed(self.sample1)
self._check_sample_element(parser.close()) self._check_sample_element(parser.close())
# Now as keyword args. # Now as keyword args.
parser2 = ET.XMLParser(encoding='utf-8', parser2 = ET.XMLParser(encoding='utf-8',
html=[{}],
target=ET.TreeBuilder()) target=ET.TreeBuilder())
parser2.feed(self.sample1) parser2.feed(self.sample1)
self._check_sample_element(parser2.close()) self._check_sample_element(parser2.close())
...@@ -3016,46 +3038,6 @@ class NoAcceleratorTest(unittest.TestCase): ...@@ -3016,46 +3038,6 @@ class NoAcceleratorTest(unittest.TestCase):
# -------------------------------------------------------------------- # --------------------------------------------------------------------
class CleanContext(object):
"""Provide default namespace mapping and path cache."""
checkwarnings = None
def __init__(self, quiet=False):
if sys.flags.optimize >= 2:
# under -OO, doctests cannot be run and therefore not all warnings
# will be emitted
quiet = True
deprecations = (
# 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 = ET.register_namespace._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): def test_main(module=None):
# When invoked without a module, runs the Python ET tests by loading pyET. # When invoked without a module, runs the Python ET tests by loading pyET.
# Otherwise, uses the given module as the ET. # Otherwise, uses the given module as the ET.
...@@ -3095,11 +3077,22 @@ def test_main(module=None): ...@@ -3095,11 +3077,22 @@ def test_main(module=None):
NoAcceleratorTest, NoAcceleratorTest,
]) ])
# Provide default namespace mapping and path cache.
from xml.etree import ElementPath
nsmap = ET.register_namespace._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: try:
# XXX the C module should give the same warnings as the Python module support.run_unittest(*test_classes)
with CleanContext(quiet=(pyET is not ET)):
support.run_unittest(*test_classes)
finally: 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 # don't interfere with subsequent tests
ET = pyET = None ET = pyET = None
......
...@@ -8,7 +8,8 @@ import unittest ...@@ -8,7 +8,8 @@ import unittest
cET = import_fresh_module('xml.etree.ElementTree', cET = import_fresh_module('xml.etree.ElementTree',
fresh=['_elementtree']) fresh=['_elementtree'])
cET_alias = import_fresh_module('xml.etree.cElementTree', cET_alias = import_fresh_module('xml.etree.cElementTree',
fresh=['_elementtree', 'xml.etree']) fresh=['_elementtree', 'xml.etree'],
deprecated=True)
@unittest.skipUnless(cET, 'requires _elementtree') @unittest.skipUnless(cET, 'requires _elementtree')
......
...@@ -1430,6 +1430,7 @@ class TreeBuilder: ...@@ -1430,6 +1430,7 @@ class TreeBuilder:
self._tail = 1 self._tail = 1
return self._last return self._last
_sentinel = ['sentinel']
# also see ElementTree and TreeBuilder # also see ElementTree and TreeBuilder
class XMLParser: class XMLParser:
...@@ -1443,7 +1444,11 @@ class XMLParser: ...@@ -1443,7 +1444,11 @@ class XMLParser:
""" """
def __init__(self, html=0, target=None, encoding=None): def __init__(self, html=_sentinel, target=None, encoding=None):
if html is not _sentinel:
warnings.warn(
"The html argument of XMLParser() is deprecated",
DeprecationWarning, stacklevel=2)
try: try:
from xml.parsers import expat from xml.parsers import expat
except ImportError: except ImportError:
......
...@@ -298,6 +298,10 @@ Extension Modules ...@@ -298,6 +298,10 @@ Extension Modules
Library Library
------- -------
- bpo-29204: Element.getiterator() and the html parameter of XMLParser() were
deprecated only in the documentation (since Python 3.2 and 3.4 correspondintly).
Now using them emits a deprecation warning.
- bpo-27863: Fixed multiple crashes in ElementTree caused by race conditions - bpo-27863: Fixed multiple crashes in ElementTree caused by race conditions
and wrong types. and wrong types.
......
...@@ -1366,7 +1366,12 @@ _elementtree_Element_getchildren_impl(ElementObject *self) ...@@ -1366,7 +1366,12 @@ _elementtree_Element_getchildren_impl(ElementObject *self)
Py_ssize_t i; Py_ssize_t i;
PyObject* list; PyObject* list;
/* FIXME: report as deprecated? */ if (PyErr_WarnEx(PyExc_DeprecationWarning,
"This method will be removed in future versions. "
"Use 'list(elem)' or iteration over elem instead.",
1) < 0) {
return NULL;
}
if (!self->extra) if (!self->extra)
return PyList_New(0); return PyList_New(0);
...@@ -1415,6 +1420,28 @@ _elementtree_Element_iter_impl(ElementObject *self, PyObject *tag) ...@@ -1415,6 +1420,28 @@ _elementtree_Element_iter_impl(ElementObject *self, PyObject *tag)
} }
/*[clinic input]
_elementtree.Element.getiterator
tag: object = None
[clinic start generated code]*/
static PyObject *
_elementtree_Element_getiterator_impl(ElementObject *self, PyObject *tag)
/*[clinic end generated code: output=cb69ff4a3742dfa1 input=500da1a03f7b9e28]*/
{
/* Change for a DeprecationWarning in 1.4 */
if (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 _elementtree_Element_iter_impl(self, tag);
}
/*[clinic input] /*[clinic input]
_elementtree.Element.itertext _elementtree.Element.itertext
...@@ -3244,6 +3271,14 @@ _elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *html, ...@@ -3244,6 +3271,14 @@ _elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *html,
PyObject *target, const char *encoding) PyObject *target, const char *encoding)
/*[clinic end generated code: output=d6a16c63dda54441 input=155bc5695baafffd]*/ /*[clinic end generated code: output=d6a16c63dda54441 input=155bc5695baafffd]*/
{ {
if (html != NULL) {
if (PyErr_WarnEx(PyExc_DeprecationWarning,
"The html argument of XMLParser() is deprecated",
1) < 0) {
return -1;
}
}
self->entity = PyDict_New(); self->entity = PyDict_New();
if (!self->entity) if (!self->entity)
return -1; return -1;
...@@ -3716,7 +3751,7 @@ static PyMethodDef element_methods[] = { ...@@ -3716,7 +3751,7 @@ static PyMethodDef element_methods[] = {
_ELEMENTTREE_ELEMENT_ITERTEXT_METHODDEF _ELEMENTTREE_ELEMENT_ITERTEXT_METHODDEF
_ELEMENTTREE_ELEMENT_ITERFIND_METHODDEF _ELEMENTTREE_ELEMENT_ITERFIND_METHODDEF
{"getiterator", (PyCFunction)_elementtree_Element_iter, METH_FASTCALL, _elementtree_Element_iter__doc__}, _ELEMENTTREE_ELEMENT_GETITERATOR_METHODDEF
_ELEMENTTREE_ELEMENT_GETCHILDREN_METHODDEF _ELEMENTTREE_ELEMENT_GETCHILDREN_METHODDEF
_ELEMENTTREE_ELEMENT_ITEMS_METHODDEF _ELEMENTTREE_ELEMENT_ITEMS_METHODDEF
......
...@@ -333,6 +333,35 @@ exit: ...@@ -333,6 +333,35 @@ exit:
return return_value; return return_value;
} }
PyDoc_STRVAR(_elementtree_Element_getiterator__doc__,
"getiterator($self, /, tag=None)\n"
"--\n"
"\n");
#define _ELEMENTTREE_ELEMENT_GETITERATOR_METHODDEF \
{"getiterator", (PyCFunction)_elementtree_Element_getiterator, METH_FASTCALL, _elementtree_Element_getiterator__doc__},
static PyObject *
_elementtree_Element_getiterator_impl(ElementObject *self, PyObject *tag);
static PyObject *
_elementtree_Element_getiterator(ElementObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
static const char * const _keywords[] = {"tag", NULL};
static _PyArg_Parser _parser = {"|O:getiterator", _keywords, 0};
PyObject *tag = Py_None;
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
&tag)) {
goto exit;
}
return_value = _elementtree_Element_getiterator_impl(self, tag);
exit:
return return_value;
}
PyDoc_STRVAR(_elementtree_Element_itertext__doc__, PyDoc_STRVAR(_elementtree_Element_itertext__doc__,
"itertext($self, /)\n" "itertext($self, /)\n"
"--\n" "--\n"
...@@ -726,4 +755,4 @@ _elementtree_XMLParser__setevents(XMLParserObject *self, PyObject **args, Py_ssi ...@@ -726,4 +755,4 @@ _elementtree_XMLParser__setevents(XMLParserObject *self, PyObject **args, Py_ssi
exit: exit:
return return_value; return return_value;
} }
/*[clinic end generated code: output=b69fa98c40917f58 input=a9049054013a1b77]*/ /*[clinic end generated code: output=fbc92d64735adec0 input=a9049054013a1b77]*/
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment