mirror of
https://github.com/python/cpython.git
synced 2025-12-10 11:00:14 +00:00
Issue #7990: dir() on ElementTree.Element now lists properties: "tag",
"text", "tail" and "attrib". Original patch by Santoso Wijaya.
This commit is contained in:
parent
14128d8bc5
commit
dde0815c35
3 changed files with 107 additions and 84 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue