Issue #25902: Fixed various refcount issues in ElementTree iteration.
This commit is contained in:
parent
0744641668
commit
66c08d90f6
|
@ -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')
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue