Skip to content

Commit 09b5247

Browse files
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.
1 parent 800e4b7 commit 09b5247

File tree

4 files changed

+111
-58
lines changed

4 files changed

+111
-58
lines changed

Lib/test/test_xml_etree.py

Lines changed: 58 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import cgi
1010
import copy
11+
import functools
1112
import io
1213
import pickle
1314
import StringIO
@@ -86,6 +87,16 @@
8687
"""
8788

8889

90+
def checkwarnings(*filters):
91+
def decorator(test):
92+
def newtest(*args, **kwargs):
93+
with support.check_warnings(*filters):
94+
test(*args, **kwargs)
95+
functools.update_wrapper(newtest, test)
96+
return newtest
97+
return decorator
98+
99+
89100
class ModuleTest(unittest.TestCase):
90101
# TODO: this should be removed once we get rid of the global module vars
91102

@@ -656,6 +667,10 @@ def comment(self, data):
656667
])
657668

658669

670+
# Element.getchildren() and ElementTree.getiterator() are deprecated.
671+
@checkwarnings(("This method will be removed in future versions. "
672+
"Use .+ instead.",
673+
(DeprecationWarning, PendingDeprecationWarning)))
659674
def test_getchildren(self):
660675
# Test Element.getchildren()
661676

@@ -1356,9 +1371,15 @@ def test_bug_200708_close(self):
13561371

13571372
# Test custom builder.
13581373
class EchoTarget:
1374+
def start(self, tag, attrib):
1375+
pass
1376+
def end(self, tag):
1377+
pass
1378+
def data(self, text):
1379+
pass
13591380
def close(self):
13601381
return ET.Element("element") # simulate root
1361-
parser = ET.XMLParser(EchoTarget())
1382+
parser = ET.XMLParser(target=EchoTarget())
13621383
parser.feed("<element>some text</element>")
13631384
self.assertEqual(parser.close().tag, 'element')
13641385

@@ -1908,7 +1929,12 @@ def test_find_through_ElementTree(self):
19081929
e = ET.XML(SAMPLE_XML)
19091930
self.assertEqual(ET.ElementTree(e).find('tag').tag, 'tag')
19101931
self.assertEqual(ET.ElementTree(e).find('./tag').tag, 'tag')
1911-
self.assertEqual(ET.ElementTree(e).find('/tag').tag, 'tag')
1932+
# this produces a warning
1933+
msg = ("This search is broken in 1.3 and earlier, and will be fixed "
1934+
"in a future version. If you rely on the current behaviour, "
1935+
"change it to '.+'")
1936+
with support.check_warnings((msg, FutureWarning)):
1937+
self.assertEqual(ET.ElementTree(e).find('/tag').tag, 'tag')
19121938
e[2] = ET.XML(SAMPLE_SECTION)
19131939
self.assertEqual(ET.ElementTree(e).find('section/tag').tag, 'tag')
19141940
self.assertIsNone(ET.ElementTree(e).find('tog'))
@@ -1919,14 +1945,15 @@ def test_find_through_ElementTree(self):
19191945
self.assertEqual(ET.ElementTree(e).findtext('tog/foo', 'default'),
19201946
'default')
19211947
self.assertEqual(ET.ElementTree(e).findtext('./tag'), 'text')
1922-
self.assertEqual(ET.ElementTree(e).findtext('/tag'), 'text')
1948+
with support.check_warnings((msg, FutureWarning)):
1949+
self.assertEqual(ET.ElementTree(e).findtext('/tag'), 'text')
19231950
self.assertEqual(ET.ElementTree(e).findtext('section/tag'), 'subtext')
19241951

19251952
self.assertEqual(summarize_list(ET.ElementTree(e).findall('./tag')),
19261953
['tag'] * 2)
1927-
# this produces a warning
1928-
self.assertEqual(summarize_list(ET.ElementTree(e).findall('/tag')),
1929-
['tag'] * 2)
1954+
with support.check_warnings((msg, FutureWarning)):
1955+
it = ET.ElementTree(e).findall('/tag')
1956+
self.assertEqual(summarize_list(it), ['tag'] * 2)
19301957

19311958

19321959
class ElementIterTest(unittest.TestCase):
@@ -2014,6 +2041,15 @@ def test_iter_by_tag(self):
20142041
self.assertEqual(self._ilist(doc, '*'), all_tags)
20152042

20162043
def test_getiterator(self):
2044+
# Element.getiterator() is deprecated.
2045+
if sys.py3kwarning or ET is pyET:
2046+
with support.check_warnings(("This method will be removed in future versions. "
2047+
"Use .+ instead.", PendingDeprecationWarning)):
2048+
self._test_getiterator()
2049+
else:
2050+
self._test_getiterator()
2051+
2052+
def _test_getiterator(self):
20172053
doc = ET.XML('''
20182054
<document>
20192055
<house>
@@ -2178,13 +2214,13 @@ def _check_sample_element(self, e):
21782214
def test_constructor_args(self):
21792215
# Positional args. The first (html) is not supported, but should be
21802216
# nevertheless correctly accepted.
2181-
parser = ET.XMLParser(None, ET.TreeBuilder(), 'utf-8')
2217+
with support.check_py3k_warnings((r'.*\bhtml\b', DeprecationWarning)):
2218+
parser = ET.XMLParser(None, ET.TreeBuilder(), 'utf-8')
21822219
parser.feed(self.sample1)
21832220
self._check_sample_element(parser.close())
21842221

21852222
# Now as keyword args.
21862223
parser2 = ET.XMLParser(encoding='utf-8',
2187-
html=[{}],
21882224
target=ET.TreeBuilder())
21892225
parser2.feed(self.sample1)
21902226
self._check_sample_element(parser2.close())
@@ -2593,44 +2629,6 @@ def test_correct_import_pyET(self):
25932629
# --------------------------------------------------------------------
25942630

25952631

2596-
class CleanContext(object):
2597-
"""Provide default namespace mapping and path cache."""
2598-
checkwarnings = None
2599-
2600-
def __init__(self, quiet=False):
2601-
deprecations = (
2602-
("This method of XMLParser is deprecated. Define doctype\(\) "
2603-
"method on the TreeBuilder target.", DeprecationWarning),
2604-
# Search behaviour is broken if search path starts with "/".
2605-
("This search is broken in 1.3 and earlier, and will be fixed "
2606-
"in a future version. If you rely on the current behaviour, "
2607-
"change it to '.+'", FutureWarning),
2608-
# Element.getchildren() and Element.getiterator() are deprecated.
2609-
("This method will be removed in future versions. "
2610-
"Use .+ instead.", DeprecationWarning),
2611-
("This method will be removed in future versions. "
2612-
"Use .+ instead.", PendingDeprecationWarning))
2613-
self.checkwarnings = support.check_warnings(*deprecations, quiet=quiet)
2614-
2615-
def __enter__(self):
2616-
from xml.etree import ElementPath
2617-
self._nsmap = pyET._namespace_map
2618-
# Copy the default namespace mapping
2619-
self._nsmap_copy = self._nsmap.copy()
2620-
# Copy the path cache (should be empty)
2621-
self._path_cache = ElementPath._cache
2622-
ElementPath._cache = self._path_cache.copy()
2623-
self.checkwarnings.__enter__()
2624-
2625-
def __exit__(self, *args):
2626-
from xml.etree import ElementPath
2627-
# Restore mapping and path cache
2628-
self._nsmap.clear()
2629-
self._nsmap.update(self._nsmap_copy)
2630-
ElementPath._cache = self._path_cache
2631-
self.checkwarnings.__exit__(*args)
2632-
2633-
26342632
def test_main(module=None):
26352633
# When invoked without a module, runs the Python ET tests by loading pyET.
26362634
# Otherwise, uses the given module as the ET.
@@ -2666,11 +2664,22 @@ def test_main(module=None):
26662664
NoAcceleratorTest,
26672665
])
26682666

2667+
# Provide default namespace mapping and path cache.
2668+
from xml.etree import ElementPath
2669+
nsmap = pyET._namespace_map
2670+
# Copy the default namespace mapping
2671+
nsmap_copy = nsmap.copy()
2672+
# Copy the path cache (should be empty)
2673+
path_cache = ElementPath._cache
2674+
ElementPath._cache = path_cache.copy()
26692675
try:
2670-
# XXX the C module should give the same warnings as the Python module
2671-
with CleanContext(quiet=(pyET is not ET)):
2672-
support.run_unittest(*test_classes)
2676+
support.run_unittest(*test_classes)
26732677
finally:
2678+
from xml.etree import ElementPath
2679+
# Restore mapping and path cache
2680+
nsmap.clear()
2681+
nsmap.update(nsmap_copy)
2682+
ElementPath._cache = path_cache
26742683
# don't interfere with subsequent tests
26752684
ET = None
26762685

Lib/xml/etree/ElementTree.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1450,6 +1450,8 @@ def end(self, tag):
14501450
self._tail = 1
14511451
return self._last
14521452

1453+
_sentinel = ['sentinel']
1454+
14531455
##
14541456
# Element structure builder for XML source data, based on the
14551457
# <b>expat</b> parser.
@@ -1465,7 +1467,11 @@ def end(self, tag):
14651467

14661468
class XMLParser(object):
14671469

1468-
def __init__(self, html=0, target=None, encoding=None):
1470+
def __init__(self, html=_sentinel, target=None, encoding=None):
1471+
if html is not _sentinel:
1472+
warnings.warnpy3k(
1473+
"The html argument of XMLParser() is deprecated",
1474+
DeprecationWarning, stacklevel=2)
14691475
try:
14701476
from xml.parsers import expat
14711477
except ImportError:
@@ -1617,7 +1623,7 @@ def _default(self, text):
16171623
pubid = pubid[1:-1]
16181624
if hasattr(self.target, "doctype"):
16191625
self.target.doctype(name, pubid, system[1:-1])
1620-
elif self.doctype is not self._XMLParser__doctype:
1626+
elif self.doctype != self._XMLParser__doctype:
16211627
# warn about deprecated call
16221628
self._XMLParser__doctype(name, pubid, system[1:-1])
16231629
self.doctype(name, pubid, system[1:-1])

Misc/NEWS

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ Extension Modules
4242
Library
4343
-------
4444

45+
- bpo-30365: Running Python with the -3 option now emits deprecation warnings
46+
for getchildren() and getiterator() methods of the Element class in the
47+
xml.etree.cElementTree module and when pass the html argument to
48+
xml.etree.ElementTree.XMLParser().
49+
50+
- bpo-30365: Fixed a deprecation warning about the doctype() method of the
51+
xml.etree.ElementTree.XMLParser class. Now it is emitted only when define
52+
the doctype() method in the subclass of XMLParser.
53+
4554
- bpo-30329: imaplib now catchs the Windows socket WSAEINVAL error
4655
(code 10022) on shutdown(SHUT_RDWR): An invalid operation was attempted.
4756
This error occurs sometimes on SSL connections.

Modules/_elementtree.c

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -962,7 +962,11 @@ element_getchildren(ElementObject* self, PyObject* args)
962962
int i;
963963
PyObject* list;
964964

965-
/* FIXME: report as deprecated? */
965+
if (PyErr_WarnPy3k("This method will be removed in future versions. "
966+
"Use 'list(elem)' or iteration over elem instead.",
967+
1) < 0) {
968+
return NULL;
969+
}
966970

967971
if (!PyArg_ParseTuple(args, ":getchildren"))
968972
return NULL;
@@ -984,13 +988,10 @@ element_getchildren(ElementObject* self, PyObject* args)
984988
}
985989

986990
static PyObject*
987-
element_iter(ElementObject* self, PyObject* args)
991+
element_iter_impl(ElementObject* self, PyObject* tag)
988992
{
993+
PyObject* args;
989994
PyObject* result;
990-
991-
PyObject* tag = Py_None;
992-
if (!PyArg_ParseTuple(args, "|O:iter", &tag))
993-
return NULL;
994995

995996
if (!elementtree_iter_obj) {
996997
PyErr_SetString(
@@ -1014,6 +1015,34 @@ element_iter(ElementObject* self, PyObject* args)
10141015
return result;
10151016
}
10161017

1018+
static PyObject*
1019+
element_iter(ElementObject* self, PyObject* args)
1020+
{
1021+
PyObject* tag = Py_None;
1022+
if (!PyArg_ParseTuple(args, "|O:iter", &tag))
1023+
return NULL;
1024+
1025+
return element_iter_impl(self, tag);
1026+
}
1027+
1028+
static PyObject*
1029+
element_getiterator(ElementObject* self, PyObject* args)
1030+
{
1031+
PyObject* tag = Py_None;
1032+
if (!PyArg_ParseTuple(args, "|O:getiterator", &tag))
1033+
return NULL;
1034+
1035+
/* Change for a DeprecationWarning in 1.4 */
1036+
if (Py_Py3kWarningFlag &&
1037+
PyErr_WarnEx(PyExc_PendingDeprecationWarning,
1038+
"This method will be removed in future versions. "
1039+
"Use 'tree.iter()' or 'list(tree.iter())' instead.",
1040+
1) < 0) {
1041+
return NULL;
1042+
}
1043+
return element_iter_impl(self, tag);
1044+
}
1045+
10171046

10181047
static PyObject*
10191048
element_itertext(ElementObject* self, PyObject* args)
@@ -1510,7 +1539,7 @@ static PyMethodDef element_methods[] = {
15101539
{"itertext", (PyCFunction) element_itertext, METH_VARARGS},
15111540
{"iterfind", (PyCFunction) element_iterfind, METH_VARARGS},
15121541

1513-
{"getiterator", (PyCFunction) element_iter, METH_VARARGS},
1542+
{"getiterator", (PyCFunction) element_getiterator, METH_VARARGS},
15141543
{"getchildren", (PyCFunction) element_getchildren, METH_VARARGS},
15151544

15161545
{"items", (PyCFunction) element_items, METH_VARARGS},

0 commit comments

Comments
 (0)