diff -r 936fcd0ba6b1 Lib/test/test_xml_etree.py --- a/Lib/test/test_xml_etree.py Mon Nov 23 08:49:39 2015 +0200 +++ b/Lib/test/test_xml_etree.py Mon Nov 23 11:41:15 2015 +0200 @@ -182,10 +182,12 @@ class ElementTreeTest(unittest.TestCase) def check_element(element): self.assertTrue(ET.iselement(element), msg="not an element") - self.assertTrue(hasattr(element, "tag"), msg="no tag member") - self.assertTrue(hasattr(element, "attrib"), msg="no attrib member") - self.assertTrue(hasattr(element, "text"), msg="no text member") - self.assertTrue(hasattr(element, "tail"), msg="no tail member") + direlem = dir(element) + for attr in 'tag', 'attrib', 'text', 'tail': + self.assertTrue(hasattr(element, attr), + msg='no %s member' % attr) + self.assertIn(attr, direlem, + msg='no %s visible by dir' % attr) check_string(element.tag) check_mapping(element.attrib) diff -r 936fcd0ba6b1 Modules/_elementtree.c --- a/Modules/_elementtree.c Mon Nov 23 08:49:39 2015 +0200 +++ b/Modules/_elementtree.c Mon Nov 23 11:41:15 2015 +0200 @@ -1870,94 +1870,92 @@ element_ass_subscr(PyObject* self_, PyOb } static PyObject* -element_getattro(ElementObject* self, PyObject* nameobj) +element_get__tag(ElementObject *self, void *closure) { - PyObject* res; - 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; - + PyObject *res = self->tag; Py_INCREF(res); return res; } +static PyObject* +element_get__text(ElementObject *self, void *closure) +{ + PyObject *res = element_get_text(self); + Py_XINCREF(res); + return res; +} + +static PyObject* +element_get__tail(ElementObject *self, void *closure) +{ + PyObject *res = element_get_tail(self); + Py_XINCREF(res); + return res; +} + +static PyObject* +element_get__attrib(ElementObject *self, void *closure) +{ + PyObject *res; + if (!self->extra) { + if (create_extra(self, NULL) < 0) + return NULL; + } + res = element_get_attrib(self); + Py_XINCREF(res); + return res; +} + +/* macro for setter validation */ +#define _VALIDATE_ATTR_VALUE(V) \ + if ((V) == NULL) { \ + PyErr_SetString( \ + PyExc_AttributeError, \ + "can't delete element attribute"); \ + return -1; \ + } + static int -element_setattro(ElementObject* self, PyObject* nameobj, PyObject* value) +element_set__tag(ElementObject *self, PyObject *value, void *closure) { - char *name = ""; - - if (value == NULL) { - PyErr_SetString(PyExc_AttributeError, - "can't delete attribute"); - return -1; + _VALIDATE_ATTR_VALUE(value); + Py_INCREF(value); + Py_DECREF(self->tag); + self->tag = value; + return 0; +} + +static int +element_set__text(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_set__tail(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_set__attrib(ElementObject *self, PyObject *value, void *closure) +{ + _VALIDATE_ATTR_VALUE(value); + if (!self->extra) { + if (create_extra(self, NULL) < 0) + return -1; } - if (PyUnicode_Check(nameobj)) - name = _PyUnicode_AsString(nameobj); - if (name == NULL) - return -1; - - if (strcmp(name, "tag") == 0) { - Py_DECREF(self->tag); - self->tag = value; - Py_INCREF(self->tag); - } else if (strcmp(name, "text") == 0) { - Py_DECREF(JOIN_OBJ(self->text)); - self->text = value; - 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; - } - + Py_INCREF(value); + Py_DECREF(self->extra->attrib); + self->extra->attrib = value; return 0; } @@ -3770,6 +3768,28 @@ static PyMappingMethods element_as_mappi (objobjargproc) element_ass_subscr, }; +static PyGetSetDef element_getsetlist[] = { + {"attrib", + (getter)element_get__attrib, + (setter)element_set__attrib, + "A dictionary containing the element's attributes"}, + {"tag", + (getter)element_get__tag, + (setter)element_set__tag, + "A string identifying what kind of data this element represents"}, + {"text", + (getter)element_get__text, + (setter)element_set__text, + "The text attribute can be used to hold additional data associated " + "with the element"}, + {"tail", + (getter)element_get__tail, + (setter)element_set__tail, + "The tail attribute can be used to hold additional data associated " + "with the element"}, + {NULL}, +}; + static PyTypeObject Element_Type = { PyVarObject_HEAD_INIT(NULL, 0) "xml.etree.ElementTree.Element", sizeof(ElementObject), 0, @@ -3786,8 +3806,8 @@ static PyTypeObject Element_Type = { 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - (getattrofunc)element_getattro, /* tp_getattro */ - (setattrofunc)element_setattro, /* tp_setattro */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ @@ -3800,7 +3820,7 @@ static PyTypeObject Element_Type = { 0, /* tp_iternext */ element_methods, /* tp_methods */ 0, /* tp_members */ - 0, /* tp_getset */ + element_getsetlist, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */