https://github.com/python/cpython/commit/f3759d21dd5e6510361d7409a1df53f35ebd9a58
commit: f3759d21dd5e6510361d7409a1df53f35ebd9a58
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-01-12T11:49:18+02:00
summary:
gh-142306: Improve errors for Element.remove() (GH-142308)
* Raise TypeError for non-element argument in the Python implementation
too.
* Include the repr of the elements in the ValueError error message.
files:
A Misc/NEWS.d/next/Library/2025-12-05-17-22-25.gh-issue-142306.Gj3_1m.rst
M Lib/test/test_xml_etree.py
M Lib/xml/etree/ElementTree.py
M Modules/_elementtree.c
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
index 7aa949b2819172..93162f52ba0344 100644
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -370,8 +370,7 @@ def test_simpleops(self):
self.serialize_check(element, '<tag key="value"><subtag /></tag>') # 4
element.remove(subelement)
self.serialize_check(element, '<tag key="value" />') # 5
- with self.assertRaisesRegex(ValueError,
- r'Element\.remove\(.+\): element not
found'):
+ with self.assertRaises(ValueError):
element.remove(subelement)
self.serialize_check(element, '<tag key="value" />') # 6
element[0:0] = [subelement, subelement, subelement]
@@ -2758,6 +2757,17 @@ def test_pickle_issue18997(self):
self.assertEqual(e2.tag, 'group')
self.assertEqual(e2[0].tag, 'dogs')
+ def test_remove_errors(self):
+ e = ET.Element('tag')
+ with self.assertRaisesRegex(ValueError,
+ r"<Element 'subtag'.*> not in <Element 'tag'.*>"):
+ e.remove(ET.Element('subtag'))
+ with self.assertRaisesRegex(TypeError,
+ r".*\bElement, not type"):
+ e.remove(ET.Element)
+ with self.assertRaisesRegex(TypeError,
+ r".*\bElement, not int"):
+ e.remove(1)
class BadElementTest(ElementTestCase, unittest.TestCase):
diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py
index 92f902b9a8b875..e3d81a2c4560d9 100644
--- a/Lib/xml/etree/ElementTree.py
+++ b/Lib/xml/etree/ElementTree.py
@@ -263,12 +263,15 @@ def remove(self, subelement):
ValueError is raised if a matching element could not be found.
"""
- # assert iselement(element)
try:
self._children.remove(subelement)
except ValueError:
+ # to align the error type with the C implementation
+ if isinstance(subelement, type) or not iselement(subelement):
+ raise TypeError('expected an Element, not %s' %
+ type(subelement).__name__) from None
# to align the error message with the C implementation
- raise ValueError("Element.remove(x): element not found") from None
+ raise ValueError(f"{subelement!r} not in {self!r}") from None
def find(self, path, namespaces=None):
"""Find first matching element by tag name or path.
diff --git
a/Misc/NEWS.d/next/Library/2025-12-05-17-22-25.gh-issue-142306.Gj3_1m.rst
b/Misc/NEWS.d/next/Library/2025-12-05-17-22-25.gh-issue-142306.Gj3_1m.rst
new file mode 100644
index 00000000000000..ac39490a31e7e2
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-12-05-17-22-25.gh-issue-142306.Gj3_1m.rst
@@ -0,0 +1,2 @@
+Improve errors for :meth:`Element.remove
+<xml.etree.ElementTree.Element.remove>`.
diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c
index 22d3205e6ad314..f60a4c295e6495 100644
--- a/Modules/_elementtree.c
+++ b/Modules/_elementtree.c
@@ -1679,8 +1679,7 @@ _elementtree_Element_remove_impl(ElementObject *self,
PyObject *subelement)
}
if (rc == 0) {
- PyErr_SetString(PyExc_ValueError,
- "Element.remove(x): element not found");
+ PyErr_Format(PyExc_ValueError, "%R not in %R", subelement, self);
return NULL;
}
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]