Skip to content

gh-135640: Adds type checking to ElementTree.ElementTree constructor #135643

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
28 changes: 28 additions & 0 deletions Lib/test/test_xml_etree.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,34 @@ class ElementTreeTest(unittest.TestCase):
def serialize_check(self, elem, expected):
self.assertEqual(serialize(elem), expected)

def test_constructor(self):
# Test constructor behavior.

with self.assertRaises(TypeError):
tree = ET.ElementTree("")
with self.assertRaises(TypeError):
tree = ET.ElementTree(ET.ElementTree())

# Test _setroot as well, since it also sets the _root object.

tree = ET.ElementTree()
with self.assertRaises(TypeError):
tree._setroot("")
with self.assertRaises(TypeError):
tree._setroot(ET.ElementTree())
Comment on lines +232 to +235
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test also _setroot(None).

_setroot() needs a separate test. It should also be tested with valid argument. getroot() should be used to check its effect.


# Make sure it accepts an Element-like object.

class ElementLike:
def __init__(self):
self.tag = "tag"

element_like = ElementLike()
try:
tree = ET.ElementTree(element_like)
except Exception as err:
self.fail(err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe test also _setroot() here?

Comment on lines +246 to +247
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed.


def test_interface(self):
# Test element tree interface.

Expand Down
14 changes: 12 additions & 2 deletions Lib/xml/etree/ElementTree.py
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,10 @@ class ElementTree:

"""
def __init__(self, element=None, file=None):
# assert element is None or iselement(element)
if element is not None and not iselement(element):
raise TypeError(f"expected an xml.etree.ElementTree.Element or "
f"Element-like object, not "
f"{type(element).__name__}")
Comment on lines +531 to +533
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
raise TypeError(f"expected an xml.etree.ElementTree.Element or "
f"Element-like object, not "
f"{type(element).__name__}")
raise TypeError('expected an Element, not %s' % type(e).__name__)

self._root = element # first node
if file:
self.parse(file)
Expand All @@ -543,7 +546,10 @@ def _setroot(self, element):
with the given element. Use with care!

"""
# assert iselement(element)
if not iselement(element):
raise TypeError(f"expected an xml.etree.ElementTree.Element or "
f"Element-like object, not "
f"{type(element).__name__}")
Comment on lines +550 to +552
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
raise TypeError(f"expected an xml.etree.ElementTree.Element or "
f"Element-like object, not "
f"{type(element).__name__}")
raise TypeError('expected an Element, not %s' % type(e).__name__)

self._root = element

def parse(self, source, parser=None):
Expand Down Expand Up @@ -709,6 +715,10 @@ def write(self, file_or_filename,
of start/end tags

"""
if not iselement(self._root):
raise TypeError(f"Root element must be "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need for f-strings.

f"xml.etree.ElementTree.Element "
f"or Element-like object")
Comment on lines +718 to +721
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if not iselement(self._root):
raise TypeError(f"Root element must be "
f"xml.etree.ElementTree.Element "
f"or Element-like object")
if self._root is None:
raise TypeError('ElementTree not initialized')

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add also a test for writing uninitialized ElementTree:

    def test_write_uninitialized(self):
        tree = ET.ElementTree()
        self.assertRaises(TypeError, tree.write, TESTFN)
        self.assertFalse(os.path.exists(TESTFN))

if not method:
method = "xml"
elif method not in _serialize:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Address bug where it was possible to call
:func:`xml.etree.ElementTree.ElementTree.write` on an ElementTree object with
an invalid root element. This behavior blanked the file passed to ``write``
if it already existed.
Loading