Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 41 additions & 48 deletions Lib/test/test_xml_etree.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# monkey-patched when running the "test_xml_etree_c" test suite.

import copy
import functools
import html
import io
import operator
Expand Down Expand Up @@ -90,6 +91,16 @@
"""


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):
def test_sanity(self):
# Import sanity.
Expand Down Expand Up @@ -690,6 +701,10 @@ def comment(self, data):
])


# 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()

Expand Down Expand Up @@ -1558,7 +1573,7 @@ def test_bug_200708_close(self):
class EchoTarget:
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')

Expand Down Expand Up @@ -2225,8 +2240,12 @@ def test_find_through_ElementTree(self):
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'] * 3)
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 self.assertWarnsRegex(FutureWarning, msg):
it = ET.ElementTree(e).findall('//tag')
self.assertEqual(summarize_list(it), ['tag'] * 3)


class ElementIterTest(unittest.TestCase):
Expand Down Expand Up @@ -2311,6 +2330,9 @@ def test_iter_by_tag(self):
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):
doc = ET.XML('''
<document>
Expand Down Expand Up @@ -2493,13 +2515,13 @@ def _check_sample_element(self, e):
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 self.assertWarnsRegex(DeprecationWarning, r'\bhtml\b'):
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())
Expand Down Expand Up @@ -3016,46 +3038,6 @@ def test_correct_import_pyET(self):
# --------------------------------------------------------------------


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):
# When invoked without a module, runs the Python ET tests by loading pyET.
# Otherwise, uses the given module as the ET.
Expand Down Expand Up @@ -3095,11 +3077,22 @@ def test_main(module=None):
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:
# 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 = pyET = None

Expand Down
3 changes: 2 additions & 1 deletion Lib/test/test_xml_etree_c.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
cET = import_fresh_module('xml.etree.ElementTree',
fresh=['_elementtree'])
cET_alias = import_fresh_module('xml.etree.cElementTree',
fresh=['_elementtree', 'xml.etree'])
fresh=['_elementtree', 'xml.etree'],
deprecated=True)


@unittest.skipUnless(cET, 'requires _elementtree')
Expand Down
7 changes: 6 additions & 1 deletion Lib/xml/etree/ElementTree.py
Original file line number Diff line number Diff line change
Expand Up @@ -1430,6 +1430,7 @@ def end(self, tag):
self._tail = 1
return self._last

_sentinel = ['sentinel']

# also see ElementTree and TreeBuilder
class XMLParser:
Expand All @@ -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:
from xml.parsers import expat
except ImportError:
Expand Down
4 changes: 4 additions & 0 deletions Misc/NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,10 @@ Extension Modules
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
and wrong types.

Expand Down
39 changes: 37 additions & 2 deletions Modules/_elementtree.c
Original file line number Diff line number Diff line change
Expand Up @@ -1366,7 +1366,12 @@ _elementtree_Element_getchildren_impl(ElementObject *self)
Py_ssize_t i;
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)
return PyList_New(0);
Expand Down Expand Up @@ -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]
_elementtree.Element.itertext

Expand Down Expand Up @@ -3244,6 +3271,14 @@ _elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *html,
PyObject *target, const char *encoding)
/*[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();
if (!self->entity)
return -1;
Expand Down Expand Up @@ -3716,7 +3751,7 @@ static PyMethodDef element_methods[] = {
_ELEMENTTREE_ELEMENT_ITERTEXT_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_ITEMS_METHODDEF
Expand Down
31 changes: 30 additions & 1 deletion Modules/clinic/_elementtree.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,35 @@ _elementtree_Element_iter(ElementObject *self, PyObject **args, Py_ssize_t nargs
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__,
"itertext($self, /)\n"
"--\n"
Expand Down Expand Up @@ -726,4 +755,4 @@ _elementtree_XMLParser__setevents(XMLParserObject *self, PyObject **args, Py_ssi
exit:
return return_value;
}
/*[clinic end generated code: output=b69fa98c40917f58 input=a9049054013a1b77]*/
/*[clinic end generated code: output=fbc92d64735adec0 input=a9049054013a1b77]*/