mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Fixes and enhancements to _elementtree:
* Fixed refleak problems when GC collection is run (see messages in issue #14065) * Added weakref support to Element objects
This commit is contained in:
parent
5c73e8eaf4
commit
ebf37a2ffb
2 changed files with 80 additions and 22 deletions
|
@ -1859,6 +1859,41 @@ class BasicElementTest(unittest.TestCase):
|
||||||
gc_collect()
|
gc_collect()
|
||||||
self.assertIsNone(wref())
|
self.assertIsNone(wref())
|
||||||
|
|
||||||
|
# A longer cycle: d->e->e2->d
|
||||||
|
e = ET.Element('joe')
|
||||||
|
d = Dummy()
|
||||||
|
d.dummyref = e
|
||||||
|
wref = weakref.ref(d)
|
||||||
|
e2 = ET.SubElement(e, 'foo', attr=d)
|
||||||
|
del d, e, e2
|
||||||
|
gc_collect()
|
||||||
|
self.assertIsNone(wref())
|
||||||
|
|
||||||
|
# A cycle between Element objects as children of one another
|
||||||
|
# e1->e2->e3->e1
|
||||||
|
e1 = ET.Element('e1')
|
||||||
|
e2 = ET.Element('e2')
|
||||||
|
e3 = ET.Element('e3')
|
||||||
|
e1.append(e2)
|
||||||
|
e2.append(e2)
|
||||||
|
e3.append(e1)
|
||||||
|
wref = weakref.ref(e1)
|
||||||
|
del e1, e2, e3
|
||||||
|
gc_collect()
|
||||||
|
self.assertIsNone(wref())
|
||||||
|
|
||||||
|
def test_weakref(self):
|
||||||
|
flag = False
|
||||||
|
def wref_cb(w):
|
||||||
|
nonlocal flag
|
||||||
|
flag = True
|
||||||
|
e = ET.Element('e')
|
||||||
|
wref = weakref.ref(e, wref_cb)
|
||||||
|
self.assertEqual(wref().tag, 'e')
|
||||||
|
del e
|
||||||
|
self.assertEqual(flag, True)
|
||||||
|
self.assertEqual(wref(), None)
|
||||||
|
|
||||||
|
|
||||||
class ElementTreeTest(unittest.TestCase):
|
class ElementTreeTest(unittest.TestCase):
|
||||||
def test_istype(self):
|
def test_istype(self):
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
/* See http://www.python.org/psf/license for licensing details. */
|
/* See http://www.python.org/psf/license for licensing details. */
|
||||||
|
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
|
#include "structmember.h"
|
||||||
|
|
||||||
#define VERSION "1.0.6"
|
#define VERSION "1.0.6"
|
||||||
|
|
||||||
|
@ -229,6 +230,8 @@ typedef struct {
|
||||||
|
|
||||||
ElementObjectExtra* extra;
|
ElementObjectExtra* extra;
|
||||||
|
|
||||||
|
PyObject *weakreflist; /* For tp_weaklistoffset */
|
||||||
|
|
||||||
} ElementObject;
|
} ElementObject;
|
||||||
|
|
||||||
static PyTypeObject Element_Type;
|
static PyTypeObject Element_Type;
|
||||||
|
@ -261,17 +264,24 @@ create_extra(ElementObject* self, PyObject* attrib)
|
||||||
LOCAL(void)
|
LOCAL(void)
|
||||||
dealloc_extra(ElementObject* self)
|
dealloc_extra(ElementObject* self)
|
||||||
{
|
{
|
||||||
|
if (!self->extra)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Avoid DECREFs calling into this code again (cycles, etc.)
|
||||||
|
*/
|
||||||
|
ElementObjectExtra *myextra = self->extra;
|
||||||
|
self->extra = NULL;
|
||||||
|
|
||||||
|
Py_DECREF(myextra->attrib);
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
for (i = 0; i < myextra->length; i++)
|
||||||
|
Py_DECREF(myextra->children[i]);
|
||||||
|
|
||||||
Py_DECREF(self->extra->attrib);
|
if (myextra->children != myextra->_children)
|
||||||
|
PyObject_Free(myextra->children);
|
||||||
|
|
||||||
for (i = 0; i < self->extra->length; i++)
|
PyObject_Free(myextra);
|
||||||
Py_DECREF(self->extra->children[i]);
|
|
||||||
|
|
||||||
if (self->extra->children != self->extra->_children)
|
|
||||||
PyObject_Free(self->extra->children);
|
|
||||||
|
|
||||||
PyObject_Free(self->extra);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convenience internal function to create new Element objects with the given
|
/* Convenience internal function to create new Element objects with the given
|
||||||
|
@ -308,6 +318,8 @@ create_new_element(PyObject* tag, PyObject* attrib)
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
self->tail = Py_None;
|
self->tail = Py_None;
|
||||||
|
|
||||||
|
self->weakreflist = NULL;
|
||||||
|
|
||||||
ALLOC(sizeof(ElementObject), "create element");
|
ALLOC(sizeof(ElementObject), "create element");
|
||||||
PyObject_GC_Track(self);
|
PyObject_GC_Track(self);
|
||||||
return (PyObject*) self;
|
return (PyObject*) self;
|
||||||
|
@ -328,6 +340,7 @@ element_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
e->tail = Py_None;
|
e->tail = Py_None;
|
||||||
|
|
||||||
e->extra = NULL;
|
e->extra = NULL;
|
||||||
|
e->weakreflist = NULL;
|
||||||
}
|
}
|
||||||
return (PyObject *)e;
|
return (PyObject *)e;
|
||||||
}
|
}
|
||||||
|
@ -576,19 +589,28 @@ element_gc_traverse(ElementObject *self, visitproc visit, void *arg)
|
||||||
static int
|
static int
|
||||||
element_gc_clear(ElementObject *self)
|
element_gc_clear(ElementObject *self)
|
||||||
{
|
{
|
||||||
PyObject *text = JOIN_OBJ(self->text);
|
|
||||||
PyObject *tail = JOIN_OBJ(self->tail);
|
|
||||||
Py_CLEAR(self->tag);
|
Py_CLEAR(self->tag);
|
||||||
Py_CLEAR(text);
|
|
||||||
Py_CLEAR(tail);
|
/* The following is like Py_CLEAR for self->text and self->tail, but
|
||||||
|
* written explicitily because the real pointers hide behind access
|
||||||
|
* macros.
|
||||||
|
*/
|
||||||
|
if (self->text) {
|
||||||
|
PyObject *tmp = JOIN_OBJ(self->text);
|
||||||
|
self->text = NULL;
|
||||||
|
Py_DECREF(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->tail) {
|
||||||
|
PyObject *tmp = JOIN_OBJ(self->tail);
|
||||||
|
self->tail = NULL;
|
||||||
|
Py_DECREF(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
/* After dropping all references from extra, it's no longer valid anyway,
|
/* After dropping all references from extra, it's no longer valid anyway,
|
||||||
** so fully deallocate it (see also element_clearmethod)
|
* so fully deallocate it.
|
||||||
*/
|
*/
|
||||||
if (self->extra) {
|
|
||||||
dealloc_extra(self);
|
dealloc_extra(self);
|
||||||
self->extra = NULL;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -596,6 +618,10 @@ static void
|
||||||
element_dealloc(ElementObject* self)
|
element_dealloc(ElementObject* self)
|
||||||
{
|
{
|
||||||
PyObject_GC_UnTrack(self);
|
PyObject_GC_UnTrack(self);
|
||||||
|
|
||||||
|
if (self->weakreflist != NULL)
|
||||||
|
PyObject_ClearWeakRefs((PyObject *) self);
|
||||||
|
|
||||||
/* element_gc_clear clears all references and deallocates extra
|
/* element_gc_clear clears all references and deallocates extra
|
||||||
*/
|
*/
|
||||||
element_gc_clear(self);
|
element_gc_clear(self);
|
||||||
|
@ -626,10 +652,7 @@ element_clearmethod(ElementObject* self, PyObject* args)
|
||||||
if (!PyArg_ParseTuple(args, ":clear"))
|
if (!PyArg_ParseTuple(args, ":clear"))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (self->extra) {
|
|
||||||
dealloc_extra(self);
|
dealloc_extra(self);
|
||||||
self->extra = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
Py_DECREF(JOIN_OBJ(self->text));
|
Py_DECREF(JOIN_OBJ(self->text));
|
||||||
|
@ -1693,7 +1716,7 @@ static PyTypeObject Element_Type = {
|
||||||
(traverseproc)element_gc_traverse, /* tp_traverse */
|
(traverseproc)element_gc_traverse, /* tp_traverse */
|
||||||
(inquiry)element_gc_clear, /* tp_clear */
|
(inquiry)element_gc_clear, /* tp_clear */
|
||||||
0, /* tp_richcompare */
|
0, /* tp_richcompare */
|
||||||
0, /* tp_weaklistoffset */
|
offsetof(ElementObject, weakreflist), /* tp_weaklistoffset */
|
||||||
0, /* tp_iter */
|
0, /* tp_iter */
|
||||||
0, /* tp_iternext */
|
0, /* tp_iternext */
|
||||||
element_methods, /* tp_methods */
|
element_methods, /* tp_methods */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue