Skip to content

Commit 84b22d4

Browse files
committed
Make iselement stricter + incorporate feedback
Adjusts iselement() based off feedback from picnixz. Since ElementTree needs to be compatible with element-like objects, the function now checks for all attributes that are necessary to instantiate an ElementTree object and successfully write it to a file (preventing issue #135640). This commit also incorporates other feedback requesting moving the tests and adding more tests, and adds a news entry.
1 parent 6d4d65f commit 84b22d4

3 files changed

Lines changed: 44 additions & 11 deletions

File tree

Lib/test/test_xml_etree.py

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,42 @@ class ElementTreeTest(unittest.TestCase):
218218
def serialize_check(self, elem, expected):
219219
self.assertEqual(serialize(elem), expected)
220220

221+
def test_constructor(self):
222+
# Test constructor behavior.
223+
224+
with self.assertRaises(TypeError):
225+
tree = ET.ElementTree("")
226+
with self.assertRaises(TypeError):
227+
tree = ET.ElementTree(ET.ElementTree())
228+
229+
# Test _setroot as well, since it also sets the _root object.
230+
231+
tree = ET.ElementTree()
232+
with self.assertRaises(TypeError):
233+
tree._setroot("")
234+
with self.assertRaises(TypeError):
235+
tree._setroot(ET.ElementTree())
236+
237+
# Make sure it accepts an Element-like object.
238+
239+
class ElementLike:
240+
def __init__(self):
241+
self.tag = "tag"
242+
self.text = None
243+
self.tail = None
244+
def iter(self):
245+
pass
246+
def items(self):
247+
pass
248+
def __len__(self):
249+
pass
250+
251+
element_like = ElementLike()
252+
try:
253+
tree = ET.ElementTree(element_like)
254+
except Exception as err:
255+
self.fail(err)
256+
221257
def test_interface(self):
222258
# Test element tree interface.
223259

@@ -247,13 +283,6 @@ def check_element(element):
247283
self.assertRegex(repr(element), r"^<Element 't\xe4g' at 0x.*>$")
248284
element = ET.Element("tag", key="value")
249285

250-
# Verify type checking for ElementTree constructor
251-
252-
with self.assertRaises(TypeError):
253-
tree = ET.ElementTree("")
254-
with self.assertRaises(TypeError):
255-
tree = ET.ElementTree(ET.ElementTree())
256-
257286
# Make sure all standard element methods exist.
258287

259288
def check_method(method):

Lib/xml/etree/ElementTree.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,9 @@ class ParseError(SyntaxError):
120120

121121
def iselement(element):
122122
"""Return True if *element* appears to be an Element."""
123-
return hasattr(element, 'tag')
124-
123+
return (hasattr(element, 'tag') and hasattr(element, 'text') and
124+
hasattr(element, 'tail') and callable(element.iter) and
125+
callable(element.items) and callable(element.__len__))
125126

126127
class Element:
127128
"""An XML element.
@@ -528,7 +529,7 @@ class ElementTree:
528529
"""
529530
def __init__(self, element=None, file=None):
530531
if element is not None and not iselement(element):
531-
raise TypeError(f"element must be etree.Element, "
532+
raise TypeError(f"element must be xml.etree.Element, "
532533
f"not {type(element).__name__}")
533534
self._root = element # first node
534535
if file:
@@ -546,7 +547,7 @@ def _setroot(self, element):
546547
547548
"""
548549
if not iselement(element):
549-
raise TypeError(f"element must be etree.Element, "
550+
raise TypeError(f"element must be xml.etree.Element, "
550551
f"not {type(element).__name__}")
551552
self._root = element
552553

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Address bug where calling :func:`xml.etree.ElementTree.ElementTree.write` on
2+
an ElementTree object with an invalid root element would blank the file
3+
passed to ``write`` if it already existed.

0 commit comments

Comments
 (0)