[Python-checkins] gh-83122: Deprecate testing element truth values in `ElementTree` (#31149)

gpshead webhook-mailer at python.org
Sun Jan 22 20:16:56 EST 2023


https://github.com/python/cpython/commit/d717be04dc7876696cb21ce7901bda0214c4b2e0
commit: d717be04dc7876696cb21ce7901bda0214c4b2e0
branch: main
author: Jacob Walls <jacobtylerwalls at gmail.com>
committer: gpshead <greg at krypto.org>
date: 2023-01-22T17:16:48-08:00
summary:

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.

files:
A Misc/NEWS.d/next/Library/2022-02-05-12-01-58.bpo-38941.8IhvyG.rst
M Doc/library/xml.etree.elementtree.rst
M Doc/whatsnew/3.12.rst
M Lib/test/test_xml_etree.py
M Lib/xml/etree/ElementTree.py
M Modules/_elementtree.c

diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst
index 876de29b17ca..f9290f528e55 100644
--- a/Doc/library/xml.etree.elementtree.rst
+++ b/Doc/library/xml.etree.elementtree.rst
@@ -1045,9 +1045,9 @@ Element Objects
    :meth:`~object.__getitem__`, :meth:`~object.__setitem__`,
    :meth:`~object.__len__`.
 
-   Caution: Elements with no subelements will test as ``False``.  This behavior
-   will change in future versions.  Use specific ``len(elem)`` or ``elem is
-   None`` test instead. ::
+   Caution: Elements with no subelements will test as ``False``.  Testing the
+   truth value of an Element is deprecated and will raise an exception in
+   Python 3.14.  Use specific ``len(elem)`` or ``elem is None`` test instead.::
 
      element = root.find('foo')
 
@@ -1057,6 +1057,9 @@ Element Objects
      if element is None:
          print("element not found")
 
+   .. versionchanged:: 3.12
+      Testing the truth value of an Element emits :exc:`DeprecationWarning`.
+
    Prior to Python 3.8, the serialisation order of the XML attributes of
    elements was artificially made predictable by sorting the attributes by
    their name. Based on the now guaranteed ordering of dicts, this arbitrary
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index 164cd89522d2..3cab89712451 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -425,6 +425,11 @@ Deprecated
   is no current event loop set and it decides to create one.
   (Contributed by Serhiy Storchaka and Guido van Rossum in :gh:`100160`.)
 
+* The :mod:`xml.etree.ElementTree` module now emits :exc:`DeprecationWarning`
+  when testing the truth value of an :class:`xml.etree.ElementTree.Element`.
+  Before, the Python implementation emitted :exc:`FutureWarning`, and the C
+  implementation emitted nothing.
+
 
 Pending Removal in Python 3.13
 ------------------------------
@@ -487,6 +492,9 @@ Pending Removal in Python 3.14
 * ``__package__`` and ``__cached__`` will cease to be set or taken
   into consideration by the import system (:gh:`97879`).
 
+* Testing the truth value of an :class:`xml.etree.ElementTree.Element`
+  is deprecated and will raise an exception in Python 3.14.
+
 
 Pending Removal in Future Versions
 ----------------------------------
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
index ee3154e99f32..ca5bb562996b 100644
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -3957,6 +3957,25 @@ def test_correct_import_pyET(self):
         self.assertIsInstance(pyET.Element.__init__, types.FunctionType)
         self.assertIsInstance(pyET.XMLParser.__init__, types.FunctionType)
 
+# --------------------------------------------------------------------
+
+class BoolTest(unittest.TestCase):
+    def test_warning(self):
+        e = ET.fromstring('<a style="new"></a>')
+        msg = (
+            r"Testing an element's truth value will raise an exception in "
+            r"future versions.  "
+            r"Use specific 'len\(elem\)' or 'elem is not None' test instead.")
+        with self.assertWarnsRegex(DeprecationWarning, msg):
+            result = bool(e)
+        # Emulate prior behavior for now
+        self.assertIs(result, False)
+
+        # Element with children
+        ET.SubElement(e, 'b')
+        with self.assertWarnsRegex(DeprecationWarning, msg):
+            new_result = bool(e)
+        self.assertIs(new_result, True)
 
 # --------------------------------------------------------------------
 
@@ -4223,6 +4242,7 @@ def test_main(module=None):
         XMLPullParserTest,
         BugsTest,
         KeywordArgsTest,
+        BoolTest,
         C14NTest,
         ]
 
diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py
index df5d5191126a..42574eefd81b 100644
--- a/Lib/xml/etree/ElementTree.py
+++ b/Lib/xml/etree/ElementTree.py
@@ -200,9 +200,10 @@ def __len__(self):
 
     def __bool__(self):
         warnings.warn(
-            "The behavior of this method will change in future versions.  "
+            "Testing an element's truth value will raise an exception in "
+            "future versions.  "
             "Use specific 'len(elem)' or 'elem is not None' test instead.",
-            FutureWarning, stacklevel=2
+            DeprecationWarning, stacklevel=2
             )
         return len(self._children) != 0 # emulate old behaviour, for now
 
diff --git a/Misc/NEWS.d/next/Library/2022-02-05-12-01-58.bpo-38941.8IhvyG.rst b/Misc/NEWS.d/next/Library/2022-02-05-12-01-58.bpo-38941.8IhvyG.rst
new file mode 100644
index 000000000000..5f996042260d
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-02-05-12-01-58.bpo-38941.8IhvyG.rst
@@ -0,0 +1,4 @@
+The :mod:`xml.etree.ElementTree` module now emits :exc:`DeprecationWarning`
+when testing the truth value of an :class:`xml.etree.ElementTree.Element`.
+Before, the Python implementation emitted :exc:`FutureWarning`, and the C
+implementation emitted nothing.
diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c
index 3be098a7dbf6..df1ebc3cfc3b 100644
--- a/Modules/_elementtree.c
+++ b/Modules/_elementtree.c
@@ -1449,6 +1449,23 @@ element_getitem(PyObject* self_, Py_ssize_t index)
     return Py_NewRef(self->extra->children[index]);
 }
 
+static int
+element_bool(PyObject* self_)
+{
+    ElementObject* self = (ElementObject*) self_;
+    if (PyErr_WarnEx(PyExc_DeprecationWarning,
+                     "Testing an element's truth value will raise an exception "
+                     "in future versions.  Use specific 'len(elem)' or "
+                     "'elem is not None' test instead.",
+                     1) < 0) {
+        return -1;
+    };
+    if (self->extra ? self->extra->length : 0) {
+        return 1;
+    }
+    return 0;
+}
+
 /*[clinic input]
 _elementtree.Element.insert
 
@@ -4156,6 +4173,7 @@ static PyType_Slot element_slots[] = {
     {Py_sq_length, element_length},
     {Py_sq_item, element_getitem},
     {Py_sq_ass_item, element_setitem},
+    {Py_nb_bool, element_bool},
     {Py_mp_length, element_length},
     {Py_mp_subscript, element_subscr},
     {Py_mp_ass_subscript, element_ass_subscr},



More information about the Python-checkins mailing list