diff --git a/Lib/test/test_xml_etree_c.py b/Lib/test/test_xml_etree_c.py index 3a0fc572f457ff..aae8c3b0019951 100644 --- a/Lib/test/test_xml_etree_c.py +++ b/Lib/test/test_xml_etree_c.py @@ -234,7 +234,7 @@ class SizeofTest(unittest.TestCase): def setUp(self): self.elementsize = support.calcobjsize('5P') # extra - self.extra = struct.calcsize('PnnP4P') + self.extra = struct.calcsize('PnnP4PN') check_sizeof = support.check_sizeof diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index e134e096e044b7..3e206aa4318eee 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -230,6 +230,8 @@ typedef struct { PyObject* _children[STATIC_CHILDREN]; + /* incremented whenever the object is externally mutated */ + size_t version; } ElementObjectExtra; typedef struct { @@ -279,6 +281,8 @@ create_extra(ElementObject* self, PyObject* attrib) self->extra->allocated = STATIC_CHILDREN; self->extra->children = self->extra->_children; + self->extra->version = 0; + return 0; } @@ -506,6 +510,7 @@ element_resize(ElementObject* self, Py_ssize_t extra) } self->extra->children = children; self->extra->allocated = size; + self->extra->version++; } return 0; @@ -539,6 +544,7 @@ element_add_subelement(elementtreestate *st, ElementObject *self, self->extra->children[self->extra->length] = Py_NewRef(element); self->extra->length++; + self->extra->version++; return 0; } @@ -780,6 +786,7 @@ _elementtree_Element___copy___impl(ElementObject *self, PyTypeObject *cls) assert(!element->extra->length); element->extra->length = self->extra->length; + element->extra->version = self->extra->version; } return (PyObject*) element; @@ -847,6 +854,7 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo) if (element_resize(element, self->extra->length) < 0) goto error; + // TODO(picnixz): check for an evil child's __deepcopy__ on 'self' for (i = 0; i < self->extra->length; i++) { PyObject* child = deepcopy(st, self->extra->children[i], memo); if (!child || !Element_Check(st, child)) { @@ -862,6 +870,7 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo) assert(!element->extra->length); element->extra->length = self->extra->length; + element->extra->version = 0; } /* add object to memo dictionary (so deepcopy won't visit it again) */ @@ -1548,6 +1557,7 @@ _elementtree_Element_insert_impl(ElementObject *self, Py_ssize_t index, self->extra->children[index] = Py_NewRef(subelement); self->extra->length++; + self->extra->version++; Py_RETURN_NONE; } @@ -1645,6 +1655,7 @@ _elementtree_Element_remove_impl(ElementObject *self, PyObject *subelement) return NULL; } + // TODO(picnixz): check against evil __eq__ for (i = 0; i < self->extra->length; i++) { if (self->extra->children[i] == subelement) break; @@ -1669,6 +1680,7 @@ _elementtree_Element_remove_impl(ElementObject *self, PyObject *subelement) self->extra->length--; for (; i < self->extra->length; i++) self->extra->children[i] = self->extra->children[i+1]; + self->extra->version++; Py_DECREF(found); Py_RETURN_NONE; @@ -1724,6 +1736,7 @@ _elementtree_Element_set_impl(ElementObject *self, PyObject *key, if (PyDict_SetItem(attrib, key, value) < 0) return NULL; + self->extra->version++; Py_RETURN_NONE; } @@ -1757,6 +1770,7 @@ element_setitem(PyObject* self_, Py_ssize_t index, PyObject* item) self->extra->children[i] = self->extra->children[i+1]; } + self->extra->version++; Py_DECREF(old); return 0; @@ -1907,6 +1921,7 @@ element_ass_subscr(PyObject* self_, PyObject* item, PyObject* value) } self->extra->length -= slicelen; + self->extra->version++; /* Discard the recycle list with all the deleted sub-elements */ Py_DECREF(recycle); @@ -1982,6 +1997,7 @@ element_ass_subscr(PyObject* self_, PyObject* item, PyObject* value) } self->extra->length += newlen - slicelen; + self->extra->version++; Py_DECREF(seq); @@ -2078,6 +2094,7 @@ element_attrib_setter(ElementObject *self, PyObject *value, void *closure) return -1; } Py_XSETREF(self->extra->attrib, Py_NewRef(value)); + self->extra->version++; return 0; }