Issue #7990: dir() on ElementTree.Element now lists properties: "tag",

"text", "tail" and "attrib".  Original patch by Santoso Wijaya.
This commit is contained in:
Serhiy Storchaka 2015-11-25 15:28:13 +02:00
parent 14128d8bc5
commit dde0815c35
3 changed files with 107 additions and 84 deletions

View file

@ -182,10 +182,12 @@ class ElementTreeTest(unittest.TestCase):
def check_element(element): def check_element(element):
self.assertTrue(ET.iselement(element), msg="not an element") self.assertTrue(ET.iselement(element), msg="not an element")
self.assertTrue(hasattr(element, "tag"), msg="no tag member") direlem = dir(element)
self.assertTrue(hasattr(element, "attrib"), msg="no attrib member") for attr in 'tag', 'attrib', 'text', 'tail':
self.assertTrue(hasattr(element, "text"), msg="no text member") self.assertTrue(hasattr(element, attr),
self.assertTrue(hasattr(element, "tail"), msg="no tail member") msg='no %s member' % attr)
self.assertIn(attr, direlem,
msg='no %s visible by dir' % attr)
check_string(element.tag) check_string(element.tag)
check_mapping(element.attrib) check_mapping(element.attrib)

View file

@ -95,6 +95,9 @@ Core and Builtins
Library Library
------- -------
- Issue #7990: dir() on ElementTree.Element now lists properties: "tag",
"text", "tail" and "attrib". Original patch by Santoso Wijaya.
- Issue #25725: Fixed a reference leak in pickle.loads() when unpickling - Issue #25725: Fixed a reference leak in pickle.loads() when unpickling
invalid data including tuple instructions. invalid data including tuple instructions.

View file

@ -1870,94 +1870,92 @@ element_ass_subscr(PyObject* self_, PyObject* item, PyObject* value)
} }
static PyObject* static PyObject*
element_getattro(ElementObject* self, PyObject* nameobj) element_tag_getter(ElementObject *self, void *closure)
{ {
PyObject* res; PyObject *res = self->tag;
char *name = "";
if (PyUnicode_Check(nameobj))
name = _PyUnicode_AsString(nameobj);
if (name == NULL)
return NULL;
/* handle common attributes first */
if (strcmp(name, "tag") == 0) {
res = self->tag;
Py_INCREF(res);
return res;
} else if (strcmp(name, "text") == 0) {
res = element_get_text(self);
Py_XINCREF(res);
return res;
}
/* methods */
res = PyObject_GenericGetAttr((PyObject*) self, nameobj);
if (res)
return res;
/* less common attributes */
if (strcmp(name, "tail") == 0) {
PyErr_Clear();
res = element_get_tail(self);
} else if (strcmp(name, "attrib") == 0) {
PyErr_Clear();
if (!self->extra) {
if (create_extra(self, NULL) < 0)
return NULL;
}
res = element_get_attrib(self);
}
if (!res)
return NULL;
Py_INCREF(res); Py_INCREF(res);
return res; return res;
} }
static int static PyObject*
element_setattro(ElementObject* self, PyObject* nameobj, PyObject* value) element_text_getter(ElementObject *self, void *closure)
{ {
char *name = ""; PyObject *res = element_get_text(self);
Py_XINCREF(res);
return res;
}
if (value == NULL) { static PyObject*
PyErr_SetString(PyExc_AttributeError, element_tail_getter(ElementObject *self, void *closure)
"can't delete attribute"); {
return -1; PyObject *res = element_get_tail(self);
Py_XINCREF(res);
return res;
}
static PyObject*
element_attrib_getter(ElementObject *self, void *closure)
{
PyObject *res;
if (!self->extra) {
if (create_extra(self, NULL) < 0)
return NULL;
} }
if (PyUnicode_Check(nameobj)) res = element_get_attrib(self);
name = _PyUnicode_AsString(nameobj); Py_XINCREF(res);
if (name == NULL) return res;
return -1; }
if (strcmp(name, "tag") == 0) { /* macro for setter validation */
Py_DECREF(self->tag); #define _VALIDATE_ATTR_VALUE(V) \
self->tag = value; if ((V) == NULL) { \
Py_INCREF(self->tag); PyErr_SetString( \
} else if (strcmp(name, "text") == 0) { PyExc_AttributeError, \
Py_DECREF(JOIN_OBJ(self->text)); "can't delete element attribute"); \
self->text = value; return -1; \
Py_INCREF(self->text);
} else if (strcmp(name, "tail") == 0) {
Py_DECREF(JOIN_OBJ(self->tail));
self->tail = value;
Py_INCREF(self->tail);
} else if (strcmp(name, "attrib") == 0) {
if (!self->extra) {
if (create_extra(self, NULL) < 0)
return -1;
}
Py_DECREF(self->extra->attrib);
self->extra->attrib = value;
Py_INCREF(self->extra->attrib);
} else {
PyErr_SetString(PyExc_AttributeError,
"Can't set arbitrary attributes on Element");
return -1;
} }
static int
element_tag_setter(ElementObject *self, PyObject *value, void *closure)
{
_VALIDATE_ATTR_VALUE(value);
Py_INCREF(value);
Py_DECREF(self->tag);
self->tag = value;
return 0;
}
static int
element_text_setter(ElementObject *self, PyObject *value, void *closure)
{
_VALIDATE_ATTR_VALUE(value);
Py_INCREF(value);
Py_DECREF(JOIN_OBJ(self->text));
self->text = value;
return 0;
}
static int
element_tail_setter(ElementObject *self, PyObject *value, void *closure)
{
_VALIDATE_ATTR_VALUE(value);
Py_INCREF(value);
Py_DECREF(JOIN_OBJ(self->tail));
self->tail = value;
return 0;
}
static int
element_attrib_setter(ElementObject *self, PyObject *value, void *closure)
{
_VALIDATE_ATTR_VALUE(value);
if (!self->extra) {
if (create_extra(self, NULL) < 0)
return -1;
}
Py_INCREF(value);
Py_DECREF(self->extra->attrib);
self->extra->attrib = value;
return 0; return 0;
} }
@ -3770,6 +3768,26 @@ static PyMappingMethods element_as_mapping = {
(objobjargproc) element_ass_subscr, (objobjargproc) element_ass_subscr,
}; };
static PyGetSetDef element_getsetlist[] = {
{"tag",
(getter)element_tag_getter,
(setter)element_tag_setter,
"A string identifying what kind of data this element represents"},
{"text",
(getter)element_text_getter,
(setter)element_text_setter,
"A string of text directly after the start tag, or None"},
{"tail",
(getter)element_tail_getter,
(setter)element_tail_setter,
"A string of text directly after the end tag, or None"},
{"attrib",
(getter)element_attrib_getter,
(setter)element_attrib_setter,
"A dictionary containing the element's attributes"},
{NULL},
};
static PyTypeObject Element_Type = { static PyTypeObject Element_Type = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"xml.etree.ElementTree.Element", sizeof(ElementObject), 0, "xml.etree.ElementTree.Element", sizeof(ElementObject), 0,
@ -3786,8 +3804,8 @@ static PyTypeObject Element_Type = {
0, /* tp_hash */ 0, /* tp_hash */
0, /* tp_call */ 0, /* tp_call */
0, /* tp_str */ 0, /* tp_str */
(getattrofunc)element_getattro, /* tp_getattro */ PyObject_GenericGetAttr, /* tp_getattro */
(setattrofunc)element_setattro, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
/* tp_flags */ /* tp_flags */
@ -3800,7 +3818,7 @@ static PyTypeObject Element_Type = {
0, /* tp_iternext */ 0, /* tp_iternext */
element_methods, /* tp_methods */ element_methods, /* tp_methods */
0, /* tp_members */ 0, /* tp_members */
0, /* tp_getset */ element_getsetlist, /* tp_getset */
0, /* tp_base */ 0, /* tp_base */
0, /* tp_dict */ 0, /* tp_dict */
0, /* tp_descr_get */ 0, /* tp_descr_get */