Skip to content

Commit d717be0

Browse files
gh-83122: Deprecate testing element truth values in ElementTree (#31149)
When testing element truth values, emit a DeprecationWarning in all implementations. This had emitted a FutureWarning in the rarely used python-only implementation since ~2.7 and has always been documented as a behavior not to rely on. Matching an element in a tree search but having it test False can be unexpected. Raising the warning enables making the choice to finally raise an exception for this ambiguous behavior in the future.
1 parent 997073c commit d717be0

File tree

6 files changed

+59
-5
lines changed

6 files changed

+59
-5
lines changed

Doc/library/xml.etree.elementtree.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,9 +1045,9 @@ Element Objects
10451045
:meth:`~object.__getitem__`, :meth:`~object.__setitem__`,
10461046
:meth:`~object.__len__`.
10471047

1048-
Caution: Elements with no subelements will test as ``False``. This behavior
1049-
will change in future versions. Use specific ``len(elem)`` or ``elem is
1050-
None`` test instead. ::
1048+
Caution: Elements with no subelements will test as ``False``. Testing the
1049+
truth value of an Element is deprecated and will raise an exception in
1050+
Python 3.14. Use specific ``len(elem)`` or ``elem is None`` test instead.::
10511051

10521052
element = root.find('foo')
10531053

@@ -1057,6 +1057,9 @@ Element Objects
10571057
if element is None:
10581058
print("element not found")
10591059

1060+
.. versionchanged:: 3.12
1061+
Testing the truth value of an Element emits :exc:`DeprecationWarning`.
1062+
10601063
Prior to Python 3.8, the serialisation order of the XML attributes of
10611064
elements was artificially made predictable by sorting the attributes by
10621065
their name. Based on the now guaranteed ordering of dicts, this arbitrary

Doc/whatsnew/3.12.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,11 @@ Deprecated
425425
is no current event loop set and it decides to create one.
426426
(Contributed by Serhiy Storchaka and Guido van Rossum in :gh:`100160`.)
427427

428+
* The :mod:`xml.etree.ElementTree` module now emits :exc:`DeprecationWarning`
429+
when testing the truth value of an :class:`xml.etree.ElementTree.Element`.
430+
Before, the Python implementation emitted :exc:`FutureWarning`, and the C
431+
implementation emitted nothing.
432+
428433

429434
Pending Removal in Python 3.13
430435
------------------------------
@@ -487,6 +492,9 @@ Pending Removal in Python 3.14
487492
* ``__package__`` and ``__cached__`` will cease to be set or taken
488493
into consideration by the import system (:gh:`97879`).
489494

495+
* Testing the truth value of an :class:`xml.etree.ElementTree.Element`
496+
is deprecated and will raise an exception in Python 3.14.
497+
490498

491499
Pending Removal in Future Versions
492500
----------------------------------

Lib/test/test_xml_etree.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3957,6 +3957,25 @@ def test_correct_import_pyET(self):
39573957
self.assertIsInstance(pyET.Element.__init__, types.FunctionType)
39583958
self.assertIsInstance(pyET.XMLParser.__init__, types.FunctionType)
39593959

3960+
# --------------------------------------------------------------------
3961+
3962+
class BoolTest(unittest.TestCase):
3963+
def test_warning(self):
3964+
e = ET.fromstring('<a style="new"></a>')
3965+
msg = (
3966+
r"Testing an element's truth value will raise an exception in "
3967+
r"future versions. "
3968+
r"Use specific 'len\(elem\)' or 'elem is not None' test instead.")
3969+
with self.assertWarnsRegex(DeprecationWarning, msg):
3970+
result = bool(e)
3971+
# Emulate prior behavior for now
3972+
self.assertIs(result, False)
3973+
3974+
# Element with children
3975+
ET.SubElement(e, 'b')
3976+
with self.assertWarnsRegex(DeprecationWarning, msg):
3977+
new_result = bool(e)
3978+
self.assertIs(new_result, True)
39603979

39613980
# --------------------------------------------------------------------
39623981

@@ -4223,6 +4242,7 @@ def test_main(module=None):
42234242
XMLPullParserTest,
42244243
BugsTest,
42254244
KeywordArgsTest,
4245+
BoolTest,
42264246
C14NTest,
42274247
]
42284248

Lib/xml/etree/ElementTree.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,9 +200,10 @@ def __len__(self):
200200

201201
def __bool__(self):
202202
warnings.warn(
203-
"The behavior of this method will change in future versions. "
203+
"Testing an element's truth value will raise an exception in "
204+
"future versions. "
204205
"Use specific 'len(elem)' or 'elem is not None' test instead.",
205-
FutureWarning, stacklevel=2
206+
DeprecationWarning, stacklevel=2
206207
)
207208
return len(self._children) != 0 # emulate old behaviour, for now
208209

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
The :mod:`xml.etree.ElementTree` module now emits :exc:`DeprecationWarning`
2+
when testing the truth value of an :class:`xml.etree.ElementTree.Element`.
3+
Before, the Python implementation emitted :exc:`FutureWarning`, and the C
4+
implementation emitted nothing.

Modules/_elementtree.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1449,6 +1449,23 @@ element_getitem(PyObject* self_, Py_ssize_t index)
14491449
return Py_NewRef(self->extra->children[index]);
14501450
}
14511451

1452+
static int
1453+
element_bool(PyObject* self_)
1454+
{
1455+
ElementObject* self = (ElementObject*) self_;
1456+
if (PyErr_WarnEx(PyExc_DeprecationWarning,
1457+
"Testing an element's truth value will raise an exception "
1458+
"in future versions. Use specific 'len(elem)' or "
1459+
"'elem is not None' test instead.",
1460+
1) < 0) {
1461+
return -1;
1462+
};
1463+
if (self->extra ? self->extra->length : 0) {
1464+
return 1;
1465+
}
1466+
return 0;
1467+
}
1468+
14521469
/*[clinic input]
14531470
_elementtree.Element.insert
14541471
@@ -4156,6 +4173,7 @@ static PyType_Slot element_slots[] = {
41564173
{Py_sq_length, element_length},
41574174
{Py_sq_item, element_getitem},
41584175
{Py_sq_ass_item, element_setitem},
4176+
{Py_nb_bool, element_bool},
41594177
{Py_mp_length, element_length},
41604178
{Py_mp_subscript, element_subscr},
41614179
{Py_mp_ass_subscript, element_ass_subscr},

0 commit comments

Comments
 (0)