Skip to content

Commit 762ec97

Browse files
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.
1 parent 722a3af commit 762ec97

File tree

6 files changed

+120
-53
lines changed

6 files changed

+120
-53
lines changed

Lib/test/test_xml_etree.py

Lines changed: 41 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
# monkey-patched when running the "test_xml_etree_c" test suite.
77

88
import copy
9+
import functools
910
import html
1011
import io
1112
import operator
@@ -90,6 +91,16 @@
9091
"""
9192

9293

94+
def checkwarnings(*filters, quiet=False):
95+
def decorator(test):
96+
def newtest(*args, **kwargs):
97+
with support.check_warnings(*filters, quiet=quiet):
98+
test(*args, **kwargs)
99+
functools.update_wrapper(newtest, test)
100+
return newtest
101+
return decorator
102+
103+
93104
class ModuleTest(unittest.TestCase):
94105
def test_sanity(self):
95106
# Import sanity.
@@ -690,6 +701,10 @@ def comment(self, data):
690701
])
691702

692703

704+
# Element.getchildren() and ElementTree.getiterator() are deprecated.
705+
@checkwarnings(("This method will be removed in future versions. "
706+
"Use .+ instead.",
707+
(DeprecationWarning, PendingDeprecationWarning)))
693708
def test_getchildren(self):
694709
# Test Element.getchildren()
695710

@@ -1558,7 +1573,7 @@ def test_bug_200708_close(self):
15581573
class EchoTarget:
15591574
def close(self):
15601575
return ET.Element("element") # simulate root
1561-
parser = ET.XMLParser(EchoTarget())
1576+
parser = ET.XMLParser(target=EchoTarget())
15621577
parser.feed("<element>some text</element>")
15631578
self.assertEqual(parser.close().tag, 'element')
15641579

@@ -2225,8 +2240,12 @@ def test_find_through_ElementTree(self):
22252240
self.assertEqual(summarize_list(ET.ElementTree(e).findall('tag')),
22262241
['tag'] * 2)
22272242
# this produces a warning
2228-
self.assertEqual(summarize_list(ET.ElementTree(e).findall('//tag')),
2229-
['tag'] * 3)
2243+
msg = ("This search is broken in 1.3 and earlier, and will be fixed "
2244+
"in a future version. If you rely on the current behaviour, "
2245+
"change it to '.+'")
2246+
with self.assertWarnsRegex(FutureWarning, msg):
2247+
it = ET.ElementTree(e).findall('//tag')
2248+
self.assertEqual(summarize_list(it), ['tag'] * 3)
22302249

22312250

22322251
class ElementIterTest(unittest.TestCase):
@@ -2311,6 +2330,9 @@ def test_iter_by_tag(self):
23112330
self.assertEqual(self._ilist(doc), all_tags)
23122331
self.assertEqual(self._ilist(doc, '*'), all_tags)
23132332

2333+
# Element.getiterator() is deprecated.
2334+
@checkwarnings(("This method will be removed in future versions. "
2335+
"Use .+ instead.", PendingDeprecationWarning))
23142336
def test_getiterator(self):
23152337
doc = ET.XML('''
23162338
<document>
@@ -2493,13 +2515,13 @@ def _check_sample_element(self, e):
24932515
def test_constructor_args(self):
24942516
# Positional args. The first (html) is not supported, but should be
24952517
# nevertheless correctly accepted.
2496-
parser = ET.XMLParser(None, ET.TreeBuilder(), 'utf-8')
2518+
with self.assertWarnsRegex(DeprecationWarning, r'\bhtml\b'):
2519+
parser = ET.XMLParser(None, ET.TreeBuilder(), 'utf-8')
24972520
parser.feed(self.sample1)
24982521
self._check_sample_element(parser.close())
24992522

25002523
# Now as keyword args.
25012524
parser2 = ET.XMLParser(encoding='utf-8',
2502-
html=[{}],
25032525
target=ET.TreeBuilder())
25042526
parser2.feed(self.sample1)
25052527
self._check_sample_element(parser2.close())
@@ -3016,46 +3038,6 @@ def test_correct_import_pyET(self):
30163038
# --------------------------------------------------------------------
30173039

30183040

3019-
class CleanContext(object):
3020-
"""Provide default namespace mapping and path cache."""
3021-
checkwarnings = None
3022-
3023-
def __init__(self, quiet=False):
3024-
if sys.flags.optimize >= 2:
3025-
# under -OO, doctests cannot be run and therefore not all warnings
3026-
# will be emitted
3027-
quiet = True
3028-
deprecations = (
3029-
# Search behaviour is broken if search path starts with "/".
3030-
("This search is broken in 1.3 and earlier, and will be fixed "
3031-
"in a future version. If you rely on the current behaviour, "
3032-
"change it to '.+'", FutureWarning),
3033-
# Element.getchildren() and Element.getiterator() are deprecated.
3034-
("This method will be removed in future versions. "
3035-
"Use .+ instead.", DeprecationWarning),
3036-
("This method will be removed in future versions. "
3037-
"Use .+ instead.", PendingDeprecationWarning))
3038-
self.checkwarnings = support.check_warnings(*deprecations, quiet=quiet)
3039-
3040-
def __enter__(self):
3041-
from xml.etree import ElementPath
3042-
self._nsmap = ET.register_namespace._namespace_map
3043-
# Copy the default namespace mapping
3044-
self._nsmap_copy = self._nsmap.copy()
3045-
# Copy the path cache (should be empty)
3046-
self._path_cache = ElementPath._cache
3047-
ElementPath._cache = self._path_cache.copy()
3048-
self.checkwarnings.__enter__()
3049-
3050-
def __exit__(self, *args):
3051-
from xml.etree import ElementPath
3052-
# Restore mapping and path cache
3053-
self._nsmap.clear()
3054-
self._nsmap.update(self._nsmap_copy)
3055-
ElementPath._cache = self._path_cache
3056-
self.checkwarnings.__exit__(*args)
3057-
3058-
30593041
def test_main(module=None):
30603042
# When invoked without a module, runs the Python ET tests by loading pyET.
30613043
# Otherwise, uses the given module as the ET.
@@ -3095,11 +3077,22 @@ def test_main(module=None):
30953077
NoAcceleratorTest,
30963078
])
30973079

3080+
# Provide default namespace mapping and path cache.
3081+
from xml.etree import ElementPath
3082+
nsmap = ET.register_namespace._namespace_map
3083+
# Copy the default namespace mapping
3084+
nsmap_copy = nsmap.copy()
3085+
# Copy the path cache (should be empty)
3086+
path_cache = ElementPath._cache
3087+
ElementPath._cache = path_cache.copy()
30983088
try:
3099-
# XXX the C module should give the same warnings as the Python module
3100-
with CleanContext(quiet=(pyET is not ET)):
3101-
support.run_unittest(*test_classes)
3089+
support.run_unittest(*test_classes)
31023090
finally:
3091+
from xml.etree import ElementPath
3092+
# Restore mapping and path cache
3093+
nsmap.clear()
3094+
nsmap.update(nsmap_copy)
3095+
ElementPath._cache = path_cache
31033096
# don't interfere with subsequent tests
31043097
ET = pyET = None
31053098

Lib/test/test_xml_etree_c.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
cET = import_fresh_module('xml.etree.ElementTree',
99
fresh=['_elementtree'])
1010
cET_alias = import_fresh_module('xml.etree.cElementTree',
11-
fresh=['_elementtree', 'xml.etree'])
11+
fresh=['_elementtree', 'xml.etree'],
12+
deprecated=True)
1213

1314

1415
@unittest.skipUnless(cET, 'requires _elementtree')

Lib/xml/etree/ElementTree.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1430,6 +1430,7 @@ def end(self, tag):
14301430
self._tail = 1
14311431
return self._last
14321432

1433+
_sentinel = ['sentinel']
14331434

14341435
# also see ElementTree and TreeBuilder
14351436
class XMLParser:
@@ -1443,7 +1444,11 @@ class XMLParser:
14431444
14441445
"""
14451446

1446-
def __init__(self, html=0, target=None, encoding=None):
1447+
def __init__(self, html=_sentinel, target=None, encoding=None):
1448+
if html is not _sentinel:
1449+
warnings.warn(
1450+
"The html argument of XMLParser() is deprecated",
1451+
DeprecationWarning, stacklevel=2)
14471452
try:
14481453
from xml.parsers import expat
14491454
except ImportError:

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,10 @@ Extension Modules
298298
Library
299299
-------
300300

301+
- bpo-29204: Element.getiterator() and the html parameter of XMLParser() were
302+
deprecated only in the documentation (since Python 3.2 and 3.4 correspondintly).
303+
Now using them emits a deprecation warning.
304+
301305
- bpo-27863: Fixed multiple crashes in ElementTree caused by race conditions
302306
and wrong types.
303307

Modules/_elementtree.c

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,7 +1366,12 @@ _elementtree_Element_getchildren_impl(ElementObject *self)
13661366
Py_ssize_t i;
13671367
PyObject* list;
13681368

1369-
/* FIXME: report as deprecated? */
1369+
if (PyErr_WarnEx(PyExc_DeprecationWarning,
1370+
"This method will be removed in future versions. "
1371+
"Use 'list(elem)' or iteration over elem instead.",
1372+
1) < 0) {
1373+
return NULL;
1374+
}
13701375

13711376
if (!self->extra)
13721377
return PyList_New(0);
@@ -1415,6 +1420,28 @@ _elementtree_Element_iter_impl(ElementObject *self, PyObject *tag)
14151420
}
14161421

14171422

1423+
/*[clinic input]
1424+
_elementtree.Element.getiterator
1425+
1426+
tag: object = None
1427+
1428+
[clinic start generated code]*/
1429+
1430+
static PyObject *
1431+
_elementtree_Element_getiterator_impl(ElementObject *self, PyObject *tag)
1432+
/*[clinic end generated code: output=cb69ff4a3742dfa1 input=500da1a03f7b9e28]*/
1433+
{
1434+
/* Change for a DeprecationWarning in 1.4 */
1435+
if (PyErr_WarnEx(PyExc_PendingDeprecationWarning,
1436+
"This method will be removed in future versions. "
1437+
"Use 'tree.iter()' or 'list(tree.iter())' instead.",
1438+
1) < 0) {
1439+
return NULL;
1440+
}
1441+
return _elementtree_Element_iter_impl(self, tag);
1442+
}
1443+
1444+
14181445
/*[clinic input]
14191446
_elementtree.Element.itertext
14201447
@@ -3244,6 +3271,14 @@ _elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *html,
32443271
PyObject *target, const char *encoding)
32453272
/*[clinic end generated code: output=d6a16c63dda54441 input=155bc5695baafffd]*/
32463273
{
3274+
if (html != NULL) {
3275+
if (PyErr_WarnEx(PyExc_DeprecationWarning,
3276+
"The html argument of XMLParser() is deprecated",
3277+
1) < 0) {
3278+
return -1;
3279+
}
3280+
}
3281+
32473282
self->entity = PyDict_New();
32483283
if (!self->entity)
32493284
return -1;
@@ -3716,7 +3751,7 @@ static PyMethodDef element_methods[] = {
37163751
_ELEMENTTREE_ELEMENT_ITERTEXT_METHODDEF
37173752
_ELEMENTTREE_ELEMENT_ITERFIND_METHODDEF
37183753

3719-
{"getiterator", (PyCFunction)_elementtree_Element_iter, METH_FASTCALL, _elementtree_Element_iter__doc__},
3754+
_ELEMENTTREE_ELEMENT_GETITERATOR_METHODDEF
37203755
_ELEMENTTREE_ELEMENT_GETCHILDREN_METHODDEF
37213756

37223757
_ELEMENTTREE_ELEMENT_ITEMS_METHODDEF

Modules/clinic/_elementtree.c.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,35 @@ _elementtree_Element_iter(ElementObject *self, PyObject **args, Py_ssize_t nargs
333333
return return_value;
334334
}
335335

336+
PyDoc_STRVAR(_elementtree_Element_getiterator__doc__,
337+
"getiterator($self, /, tag=None)\n"
338+
"--\n"
339+
"\n");
340+
341+
#define _ELEMENTTREE_ELEMENT_GETITERATOR_METHODDEF \
342+
{"getiterator", (PyCFunction)_elementtree_Element_getiterator, METH_FASTCALL, _elementtree_Element_getiterator__doc__},
343+
344+
static PyObject *
345+
_elementtree_Element_getiterator_impl(ElementObject *self, PyObject *tag);
346+
347+
static PyObject *
348+
_elementtree_Element_getiterator(ElementObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
349+
{
350+
PyObject *return_value = NULL;
351+
static const char * const _keywords[] = {"tag", NULL};
352+
static _PyArg_Parser _parser = {"|O:getiterator", _keywords, 0};
353+
PyObject *tag = Py_None;
354+
355+
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
356+
&tag)) {
357+
goto exit;
358+
}
359+
return_value = _elementtree_Element_getiterator_impl(self, tag);
360+
361+
exit:
362+
return return_value;
363+
}
364+
336365
PyDoc_STRVAR(_elementtree_Element_itertext__doc__,
337366
"itertext($self, /)\n"
338367
"--\n"
@@ -726,4 +755,4 @@ _elementtree_XMLParser__setevents(XMLParserObject *self, PyObject **args, Py_ssi
726755
exit:
727756
return return_value;
728757
}
729-
/*[clinic end generated code: output=b69fa98c40917f58 input=a9049054013a1b77]*/
758+
/*[clinic end generated code: output=fbc92d64735adec0 input=a9049054013a1b77]*/

0 commit comments

Comments
 (0)