Skip to content

Commit e7d7435

Browse files
committed
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.
1 parent c22e9c9 commit e7d7435

3 files changed

Lines changed: 19 additions & 0 deletions

File tree

Lib/test/test_xml_etree.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2654,6 +2654,19 @@ def f():
26542654

26552655
class BasicElementTest(ElementTestCase, unittest.TestCase):
26562656

2657+
def test_reinit_releases_children(self):
2658+
# gh-153537: re-init should release the previous children.
2659+
e = ET.Element('a', {'k': 'v'})
2660+
children = [ET.SubElement(e, 'b') for _ in range(3)]
2661+
refs = [weakref.ref(c) for c in children]
2662+
del children
2663+
self.assertEqual(len(e), 3)
2664+
e.__init__('a')
2665+
gc_collect()
2666+
self.assertEqual(len(e), 0)
2667+
self.assertEqual(e.attrib, {})
2668+
self.assertTrue(all(ref() is None for ref in refs))
2669+
26572670
def test___init__(self):
26582671
tag = "foo"
26592672
attrib = { "zix": "wyp" }
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Re-initializing an :class:`xml.etree.ElementTree.Element` in the C accelerator
2+
now discards the previous children and attributes instead of leaking them,
3+
matching the pure-Python implementation. Patch by tonghuaroot.

Modules/_elementtree.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,9 @@ element_init(PyObject *self, PyObject *args, PyObject *kwds)
449449

450450
self_elem = (ElementObject *)self;
451451

452+
/* On re-init, drop any children and attrib from a previous init. */
453+
clear_extra(self_elem);
454+
452455
if (attrib != NULL && !is_empty_dict(attrib)) {
453456
if (create_extra(self_elem, attrib) < 0) {
454457
Py_DECREF(attrib);

0 commit comments

Comments
 (0)