Issue #25902: Fixed various refcount issues in ElementTree iteration.

This commit is contained in:
Serhiy Storchaka 2015-12-21 11:09:48 +02:00
parent 0744641668
commit 66c08d90f6
4 changed files with 117 additions and 36 deletions

View File

@ -1666,6 +1666,57 @@ class BugsTest(unittest.TestCase):
ET.register_namespace('test10777', 'http://myuri/') ET.register_namespace('test10777', 'http://myuri/')
ET.register_namespace('test10777', 'http://myuri/') ET.register_namespace('test10777', 'http://myuri/')
def test_lost_text(self):
# Issue #25902: Borrowed text can disappear
class Text:
def __bool__(self):
e.text = 'changed'
return True
e = ET.Element('tag')
e.text = Text()
i = e.itertext()
t = next(i)
self.assertIsInstance(t, Text)
self.assertIsInstance(e.text, str)
self.assertEqual(e.text, 'changed')
def test_lost_tail(self):
# Issue #25902: Borrowed tail can disappear
class Text:
def __bool__(self):
e[0].tail = 'changed'
return True
e = ET.Element('root')
e.append(ET.Element('tag'))
e[0].tail = Text()
i = e.itertext()
t = next(i)
self.assertIsInstance(t, Text)
self.assertIsInstance(e[0].tail, str)
self.assertEqual(e[0].tail, 'changed')
def test_lost_elem(self):
# Issue #25902: Borrowed element can disappear
class Tag:
def __eq__(self, other):
e[0] = ET.Element('changed')
next(i)
return True
e = ET.Element('root')
e.append(ET.Element(Tag()))
e.append(ET.Element('tag'))
i = e.iter('tag')
try:
t = next(i)
except ValueError:
self.skipTest('generators are not reentrant')
self.assertIsInstance(t.tag, Tag)
self.assertIsInstance(e[0].tag, str)
self.assertEqual(e[0].tag, 'changed')
# -------------------------------------------------------------------- # --------------------------------------------------------------------

View File

@ -428,12 +428,14 @@ class Element:
tag = self.tag tag = self.tag
if not isinstance(tag, str) and tag is not None: if not isinstance(tag, str) and tag is not None:
return return
if self.text: t = self.text
yield self.text if t:
yield t
for e in self: for e in self:
yield from e.itertext() yield from e.itertext()
if e.tail: t = e.tail
yield e.tail if t:
yield t
def SubElement(parent, tag, attrib={}, **extra): def SubElement(parent, tag, attrib={}, **extra):

View File

@ -28,6 +28,8 @@ Core and Builtins
Library Library
------- -------
- Issue #25902: Fixed various refcount issues in ElementTree iteration.
- Issue #25717: Restore the previous behaviour of tolerating most fstat() - Issue #25717: Restore the previous behaviour of tolerating most fstat()
errors when opening files. This was a regression in 3.5a1, and stopped errors when opening files. This was a regression in 3.5a1, and stopped
anonymous temporary files from working in special cases. anonymous temporary files from working in special cases.

View File

@ -2072,6 +2072,7 @@ elementiter_next(ElementIterObject *it)
ElementObject *cur_parent; ElementObject *cur_parent;
Py_ssize_t child_index; Py_ssize_t child_index;
int rc; int rc;
ElementObject *elem;
while (1) { while (1) {
/* Handle the case reached in the beginning and end of iteration, where /* Handle the case reached in the beginning and end of iteration, where
@ -2085,38 +2086,47 @@ elementiter_next(ElementIterObject *it)
PyErr_SetNone(PyExc_StopIteration); PyErr_SetNone(PyExc_StopIteration);
return NULL; return NULL;
} else { } else {
elem = it->root_element;
it->parent_stack = parent_stack_push_new(it->parent_stack, it->parent_stack = parent_stack_push_new(it->parent_stack,
it->root_element); elem);
if (!it->parent_stack) { if (!it->parent_stack) {
PyErr_NoMemory(); PyErr_NoMemory();
return NULL; return NULL;
} }
Py_INCREF(elem);
it->root_done = 1; it->root_done = 1;
rc = (it->sought_tag == Py_None); rc = (it->sought_tag == Py_None);
if (!rc) { if (!rc) {
rc = PyObject_RichCompareBool(it->root_element->tag, rc = PyObject_RichCompareBool(elem->tag,
it->sought_tag, Py_EQ); it->sought_tag, Py_EQ);
if (rc < 0) if (rc < 0) {
Py_DECREF(elem);
return NULL; return NULL;
}
} }
if (rc) { if (rc) {
if (it->gettext) { if (it->gettext) {
PyObject *text = element_get_text(it->root_element); PyObject *text = element_get_text(elem);
if (!text) if (!text) {
Py_DECREF(elem);
return NULL; return NULL;
}
Py_INCREF(text);
Py_DECREF(elem);
rc = PyObject_IsTrue(text); rc = PyObject_IsTrue(text);
if (rc > 0)
return text;
Py_DECREF(text);
if (rc < 0) if (rc < 0)
return NULL; return NULL;
if (rc) {
Py_INCREF(text);
return text;
}
} else { } else {
Py_INCREF(it->root_element); return (PyObject *)elem;
return (PyObject *)it->root_element;
} }
} }
else {
Py_DECREF(elem);
}
} }
} }
@ -2126,54 +2136,68 @@ elementiter_next(ElementIterObject *it)
cur_parent = it->parent_stack->parent; cur_parent = it->parent_stack->parent;
child_index = it->parent_stack->child_index; child_index = it->parent_stack->child_index;
if (cur_parent->extra && child_index < cur_parent->extra->length) { if (cur_parent->extra && child_index < cur_parent->extra->length) {
ElementObject *child = (ElementObject *) elem = (ElementObject *)cur_parent->extra->children[child_index];
cur_parent->extra->children[child_index];
it->parent_stack->child_index++; it->parent_stack->child_index++;
it->parent_stack = parent_stack_push_new(it->parent_stack, it->parent_stack = parent_stack_push_new(it->parent_stack,
child); elem);
if (!it->parent_stack) { if (!it->parent_stack) {
PyErr_NoMemory(); PyErr_NoMemory();
return NULL; return NULL;
} }
Py_INCREF(elem);
if (it->gettext) { if (it->gettext) {
PyObject *text = element_get_text(child); PyObject *text = element_get_text(elem);
if (!text) if (!text) {
Py_DECREF(elem);
return NULL; return NULL;
}
Py_INCREF(text);
Py_DECREF(elem);
rc = PyObject_IsTrue(text); rc = PyObject_IsTrue(text);
if (rc > 0)
return text;
Py_DECREF(text);
if (rc < 0) if (rc < 0)
return NULL; return NULL;
if (rc) {
Py_INCREF(text);
return text;
}
} else { } else {
rc = (it->sought_tag == Py_None); rc = (it->sought_tag == Py_None);
if (!rc) { if (!rc) {
rc = PyObject_RichCompareBool(child->tag, rc = PyObject_RichCompareBool(elem->tag,
it->sought_tag, Py_EQ); it->sought_tag, Py_EQ);
if (rc < 0) if (rc < 0) {
Py_DECREF(elem);
return NULL; return NULL;
}
} }
if (rc) { if (rc) {
Py_INCREF(child); return (PyObject *)elem;
return (PyObject *)child;
} }
Py_DECREF(elem);
} }
} }
else { else {
PyObject *tail; PyObject *tail;
ParentLocator *next = it->parent_stack->next; ParentLocator *next;
if (it->gettext) { if (it->gettext) {
Py_INCREF(cur_parent);
tail = element_get_tail(cur_parent); tail = element_get_tail(cur_parent);
if (!tail) if (!tail) {
Py_DECREF(cur_parent);
return NULL; return NULL;
}
Py_INCREF(tail);
Py_DECREF(cur_parent);
} }
else else {
tail = Py_None; tail = Py_None;
Py_XDECREF(it->parent_stack->parent); Py_INCREF(tail);
}
next = it->parent_stack->next;
cur_parent = it->parent_stack->parent;
PyObject_Free(it->parent_stack); PyObject_Free(it->parent_stack);
it->parent_stack = next; it->parent_stack = next;
Py_XDECREF(cur_parent);
/* Note that extra condition on it->parent_stack->parent here; /* Note that extra condition on it->parent_stack->parent here;
* this is because itertext() is supposed to only return *inner* * this is because itertext() is supposed to only return *inner*
@ -2181,12 +2205,14 @@ elementiter_next(ElementIterObject *it)
*/ */
if (it->parent_stack->parent) { if (it->parent_stack->parent) {
rc = PyObject_IsTrue(tail); rc = PyObject_IsTrue(tail);
if (rc > 0)
return tail;
Py_DECREF(tail);
if (rc < 0) if (rc < 0)
return NULL; return NULL;
if (rc) { }
Py_INCREF(tail); else {
return tail; Py_DECREF(tail);
}
} }
} }
} }