From e7d7435ca1fc8e4018b30afd438fd1b5bb2e2b44 Mon Sep 17 00:00:00 2001 From: tonghuaroot Date: Sat, 11 Jul 2026 11:49:27 +0800 Subject: [PATCH] gh-153537: Fix Element re-init memory leak in the C accelerator element_init allocated a new extra without releasing the previous one, leaking the element's children and attributes on a second __init__ call. Clear the old extra first, so re-init discards the previous children and attributes, matching the pure-Python implementation. --- Lib/test/test_xml_etree.py | 13 +++++++++++++ .../2026-07-11-11-36-15.gh-issue-153537.2KySPQ.rst | 3 +++ Modules/_elementtree.c | 3 +++ 3 files changed, 19 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-07-11-11-36-15.gh-issue-153537.2KySPQ.rst diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index acec4ec2ca257c4..78ccf4581f7c812 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -2654,6 +2654,19 @@ def f(): class BasicElementTest(ElementTestCase, unittest.TestCase): + def test_reinit_releases_children(self): + # gh-153537: re-init should release the previous children. + e = ET.Element('a', {'k': 'v'}) + children = [ET.SubElement(e, 'b') for _ in range(3)] + refs = [weakref.ref(c) for c in children] + del children + self.assertEqual(len(e), 3) + e.__init__('a') + gc_collect() + self.assertEqual(len(e), 0) + self.assertEqual(e.attrib, {}) + self.assertTrue(all(ref() is None for ref in refs)) + def test___init__(self): tag = "foo" attrib = { "zix": "wyp" } diff --git a/Misc/NEWS.d/next/Library/2026-07-11-11-36-15.gh-issue-153537.2KySPQ.rst b/Misc/NEWS.d/next/Library/2026-07-11-11-36-15.gh-issue-153537.2KySPQ.rst new file mode 100644 index 000000000000000..5fa070be5f7f1c1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-11-11-36-15.gh-issue-153537.2KySPQ.rst @@ -0,0 +1,3 @@ +Re-initializing an :class:`xml.etree.ElementTree.Element` in the C accelerator +now discards the previous children and attributes instead of leaking them, +matching the pure-Python implementation. Patch by tonghuaroot. diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index f827274eeffba83..fdd31925604bb29 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -449,6 +449,9 @@ element_init(PyObject *self, PyObject *args, PyObject *kwds) self_elem = (ElementObject *)self; + /* On re-init, drop any children and attrib from a previous init. */ + clear_extra(self_elem); + if (attrib != NULL && !is_empty_dict(attrib)) { if (create_extra(self_elem, attrib) < 0) { Py_DECREF(attrib);