Skip to content

Commit f081fd8

Browse files
bpo-35013: Add more type checks for children of Element. (GH-9944)
It is now guarantied that children of xml.etree.ElementTree.Element are Elements (at least in C implementation). Previously methods __setitem__(), __setstate__() and __deepcopy__() could be used for adding non-Element children.
1 parent 68def05 commit f081fd8

File tree

3 files changed

+71
-50
lines changed

3 files changed

+71
-50
lines changed

Lib/test/test_xml_etree.py

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,6 +1795,28 @@ def test_augmentation_type_errors(self):
17951795
self.assertRaises(TypeError, e.append, 'b')
17961796
self.assertRaises(TypeError, e.extend, [ET.Element('bar'), 'foo'])
17971797
self.assertRaises(TypeError, e.insert, 0, 'foo')
1798+
e[:] = [ET.Element('bar')]
1799+
with self.assertRaises(TypeError):
1800+
e[0] = 'foo'
1801+
with self.assertRaises(TypeError):
1802+
e[:] = [ET.Element('bar'), 'foo']
1803+
1804+
if hasattr(e, '__setstate__'):
1805+
state = {
1806+
'tag': 'tag',
1807+
'_children': [None], # non-Element
1808+
'attrib': 'attr',
1809+
'tail': 'tail',
1810+
'text': 'text',
1811+
}
1812+
self.assertRaises(TypeError, e.__setstate__, state)
1813+
1814+
if hasattr(e, '__deepcopy__'):
1815+
class E(ET.Element):
1816+
def __deepcopy__(self, memo):
1817+
return None # non-Element
1818+
e[:] = [E('bar')]
1819+
self.assertRaises(TypeError, copy.deepcopy, e)
17981820

17991821
def test_cyclic_gc(self):
18001822
class Dummy:
@@ -1981,26 +2003,6 @@ def __del__(self):
19812003
elem = b.close()
19822004
self.assertEqual(elem[0].tail, 'ABCDEFGHIJKL')
19832005

1984-
def test_element_iter(self):
1985-
# Issue #27863
1986-
state = {
1987-
'tag': 'tag',
1988-
'_children': [None], # non-Element
1989-
'attrib': 'attr',
1990-
'tail': 'tail',
1991-
'text': 'text',
1992-
}
1993-
1994-
e = ET.Element('tag')
1995-
try:
1996-
e.__setstate__(state)
1997-
except AttributeError:
1998-
e.__dict__ = state
1999-
2000-
it = e.iter()
2001-
self.assertIs(next(it), e)
2002-
self.assertRaises(AttributeError, next, it)
2003-
20042006
def test_subscr(self):
20052007
# Issue #27863
20062008
class X:

Lib/xml/etree/ElementTree.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -217,11 +217,11 @@ def __getitem__(self, index):
217217
return self._children[index]
218218

219219
def __setitem__(self, index, element):
220-
# if isinstance(index, slice):
221-
# for elt in element:
222-
# assert iselement(elt)
223-
# else:
224-
# assert iselement(element)
220+
if isinstance(index, slice):
221+
for elt in element:
222+
self._assert_is_element(elt)
223+
else:
224+
self._assert_is_element(element)
225225
self._children[index] = element
226226

227227
def __delitem__(self, index):

Modules/_elementtree.c

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -480,11 +480,24 @@ element_resize(ElementObject* self, Py_ssize_t extra)
480480
return -1;
481481
}
482482

483+
LOCAL(void)
484+
raise_type_error(PyObject *element)
485+
{
486+
PyErr_Format(PyExc_TypeError,
487+
"expected an Element, not \"%.200s\"",
488+
Py_TYPE(element)->tp_name);
489+
}
490+
483491
LOCAL(int)
484492
element_add_subelement(ElementObject* self, PyObject* element)
485493
{
486494
/* add a child element to a parent */
487495

496+
if (!Element_Check(element)) {
497+
raise_type_error(element);
498+
return -1;
499+
}
500+
488501
if (element_resize(self, 1) < 0)
489502
return -1;
490503

@@ -803,7 +816,11 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
803816

804817
for (i = 0; i < self->extra->length; i++) {
805818
PyObject* child = deepcopy(self->extra->children[i], memo);
806-
if (!child) {
819+
if (!child || !Element_Check(child)) {
820+
if (child) {
821+
raise_type_error(child);
822+
Py_DECREF(child);
823+
}
807824
element->extra->length = i;
808825
goto error;
809826
}
@@ -1024,8 +1041,15 @@ element_setstate_from_attributes(ElementObject *self,
10241041

10251042
/* Copy children */
10261043
for (i = 0; i < nchildren; i++) {
1027-
self->extra->children[i] = PyList_GET_ITEM(children, i);
1028-
Py_INCREF(self->extra->children[i]);
1044+
PyObject *child = PyList_GET_ITEM(children, i);
1045+
if (!Element_Check(child)) {
1046+
raise_type_error(child);
1047+
self->extra->length = i;
1048+
dealloc_extra(oldextra);
1049+
return NULL;
1050+
}
1051+
Py_INCREF(child);
1052+
self->extra->children[i] = child;
10291053
}
10301054

10311055
assert(!self->extra->length);
@@ -1167,16 +1191,6 @@ _elementtree_Element_extend(ElementObject *self, PyObject *elements)
11671191
for (i = 0; i < PySequence_Fast_GET_SIZE(seq); i++) {
11681192
PyObject* element = PySequence_Fast_GET_ITEM(seq, i);
11691193
Py_INCREF(element);
1170-
if (!Element_Check(element)) {
1171-
PyErr_Format(
1172-
PyExc_TypeError,
1173-
"expected an Element, not \"%.200s\"",
1174-
Py_TYPE(element)->tp_name);
1175-
Py_DECREF(seq);
1176-
Py_DECREF(element);
1177-
return NULL;
1178-
}
1179-
11801194
if (element_add_subelement(self, element) < 0) {
11811195
Py_DECREF(seq);
11821196
Py_DECREF(element);
@@ -1219,8 +1233,7 @@ _elementtree_Element_find_impl(ElementObject *self, PyObject *path,
12191233
for (i = 0; i < self->extra->length; i++) {
12201234
PyObject* item = self->extra->children[i];
12211235
int rc;
1222-
if (!Element_Check(item))
1223-
continue;
1236+
assert(Element_Check(item));
12241237
Py_INCREF(item);
12251238
rc = PyObject_RichCompareBool(((ElementObject*)item)->tag, path, Py_EQ);
12261239
if (rc > 0)
@@ -1266,8 +1279,7 @@ _elementtree_Element_findtext_impl(ElementObject *self, PyObject *path,
12661279
for (i = 0; i < self->extra->length; i++) {
12671280
PyObject *item = self->extra->children[i];
12681281
int rc;
1269-
if (!Element_Check(item))
1270-
continue;
1282+
assert(Element_Check(item));
12711283
Py_INCREF(item);
12721284
rc = PyObject_RichCompareBool(((ElementObject*)item)->tag, path, Py_EQ);
12731285
if (rc > 0) {
@@ -1323,8 +1335,7 @@ _elementtree_Element_findall_impl(ElementObject *self, PyObject *path,
13231335
for (i = 0; i < self->extra->length; i++) {
13241336
PyObject* item = self->extra->children[i];
13251337
int rc;
1326-
if (!Element_Check(item))
1327-
continue;
1338+
assert(Element_Check(item));
13281339
Py_INCREF(item);
13291340
rc = PyObject_RichCompareBool(((ElementObject*)item)->tag, path, Py_EQ);
13301341
if (rc != 0 && (rc < 0 || PyList_Append(out, item) < 0)) {
@@ -1736,6 +1747,10 @@ element_setitem(PyObject* self_, Py_ssize_t index, PyObject* item)
17361747
old = self->extra->children[index];
17371748

17381749
if (item) {
1750+
if (!Element_Check(item)) {
1751+
raise_type_error(item);
1752+
return -1;
1753+
}
17391754
Py_INCREF(item);
17401755
self->extra->children[index] = item;
17411756
} else {
@@ -1930,6 +1945,15 @@ element_ass_subscr(PyObject* self_, PyObject* item, PyObject* value)
19301945
}
19311946
}
19321947

1948+
for (i = 0; i < newlen; i++) {
1949+
PyObject *element = PySequence_Fast_GET_ITEM(seq, i);
1950+
if (!Element_Check(element)) {
1951+
raise_type_error(element);
1952+
Py_DECREF(seq);
1953+
return -1;
1954+
}
1955+
}
1956+
19331957
if (slicelen > 0) {
19341958
/* to avoid recursive calls to this method (via decref), move
19351959
old items to the recycle bin here, and get rid of them when
@@ -2207,12 +2231,7 @@ elementiter_next(ElementIterObject *it)
22072231
continue;
22082232
}
22092233

2210-
if (!Element_Check(extra->children[child_index])) {
2211-
PyErr_Format(PyExc_AttributeError,
2212-
"'%.100s' object has no attribute 'iter'",
2213-
Py_TYPE(extra->children[child_index])->tp_name);
2214-
return NULL;
2215-
}
2234+
assert(Element_Check(extra->children[child_index]));
22162235
elem = (ElementObject *)extra->children[child_index];
22172236
item->child_index++;
22182237
Py_INCREF(elem);

0 commit comments

Comments
 (0)