class TestAcceleratorNotImported(unittest.TestCase):
# Test that the C accelerator was not imported for pyET
def test_correct_import_pyET(self):
- self.assertEqual(pyET.Element.__module__, 'xml.etree.ElementTree')
+ self.assertEqual(pyET.SubElement.__module__, 'xml.etree.ElementTree')
+
+
+class TestElementClass(unittest.TestCase):
+ def test_Element_is_a_type(self):
+ self.assertIsInstance(ET.Element, type)
+
+ def test_Element_subclass_trivial(self):
+ class MyElement(ET.Element):
+ pass
+
+ mye = MyElement('foo')
+ self.assertIsInstance(mye, ET.Element)
+ self.assertIsInstance(mye, MyElement)
+ self.assertEqual(mye.tag, 'foo')
+
+ def test_Element_subclass_constructor(self):
+ class MyElement(ET.Element):
+ def __init__(self, tag, attrib={}, **extra):
+ super(MyElement, self).__init__(tag + '__', attrib, **extra)
+
+ mye = MyElement('foo', {'a': 1, 'b': 2}, c=3, d=4)
+ self.assertEqual(mye.tag, 'foo__')
+ self.assertEqual(sorted(mye.items()),
+ [('a', 1), ('b', 2), ('c', 3), ('d', 4)])
+
+ def test_Element_subclass_new_method(self):
+ class MyElement(ET.Element):
+ def newmethod(self):
+ return self.tag
+
+ mye = MyElement('joe')
+ self.assertEqual(mye.newmethod(), 'joe')
def test_main(module=pyET):
from test import test_xml_etree
+ # Run the tests specific to the Python implementation
+ support.run_unittest(TestAcceleratorNotImported)
+
# The same doctests are used for both the Python and the C implementations
test_xml_etree.ET = module
- support.run_unittest(TestAcceleratorNotImported)
+ support.run_unittest(TestElementClass)
# XXX the C module should give the same warnings as the Python module
with CleanContext(quiet=(module is not pyET)):
}
/* -------------------------------------------------------------------- */
-/* the element type */
+/* the Element type */
typedef struct {
#define Element_CheckExact(op) (Py_TYPE(op) == &Element_Type)
/* -------------------------------------------------------------------- */
-/* element constructor and destructor */
+/* Element constructors and destructor */
LOCAL(int)
-element_new_extra(ElementObject* self, PyObject* attrib)
+create_extra(ElementObject* self, PyObject* attrib)
{
self->extra = PyObject_Malloc(sizeof(ElementObjectExtra));
if (!self->extra)
}
LOCAL(void)
-element_dealloc_extra(ElementObject* self)
+dealloc_extra(ElementObject* self)
{
int i;
PyObject_Free(self->extra);
}
+/* Convenience internal function to create new Element objects with the given
+ * tag and attributes.
+*/
LOCAL(PyObject*)
-element_new(PyObject* tag, PyObject* attrib)
+create_new_element(PyObject* tag, PyObject* attrib)
{
ElementObject* self;
self->extra = NULL;
if (attrib != Py_None) {
-
- if (element_new_extra(self, attrib) < 0) {
+ if (create_extra(self, attrib) < 0) {
PyObject_Del(self);
return NULL;
}
-
- self->extra->length = 0;
- self->extra->allocated = STATIC_CHILDREN;
- self->extra->children = self->extra->_children;
-
}
Py_INCREF(tag);
return (PyObject*) self;
}
+static PyObject *
+element_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ ElementObject *e = (ElementObject *)type->tp_alloc(type, 0);
+ if (e != NULL) {
+ Py_INCREF(Py_None);
+ e->tag = Py_None;
+
+ Py_INCREF(Py_None);
+ e->text = Py_None;
+
+ Py_INCREF(Py_None);
+ e->tail = Py_None;
+
+ e->extra = NULL;
+ }
+ return (PyObject *)e;
+}
+
+static int
+element_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *tag;
+ PyObject *tmp;
+ PyObject *attrib = NULL;
+ ElementObject *self_elem;
+
+ if (!PyArg_ParseTuple(args, "O|O!:Element", &tag, &PyDict_Type, &attrib))
+ return -1;
+
+ if (attrib || kwds) {
+ attrib = (attrib) ? PyDict_Copy(attrib) : PyDict_New();
+ if (!attrib)
+ return -1;
+ if (kwds)
+ PyDict_Update(attrib, kwds);
+ } else {
+ Py_INCREF(Py_None);
+ attrib = Py_None;
+ }
+
+ self_elem = (ElementObject *)self;
+
+ /* Use None for empty dictionaries */
+ if (PyDict_CheckExact(attrib) && PyDict_Size(attrib) == 0) {
+ Py_INCREF(Py_None);
+ attrib = Py_None;
+ }
+
+ if (attrib != Py_None) {
+ if (create_extra(self_elem, attrib) < 0) {
+ PyObject_Del(self_elem);
+ return -1;
+ }
+ }
+
+ /* If create_extra needed attrib, it took a reference to it, so we can
+ * release ours anyway.
+ */
+ Py_DECREF(attrib);
+
+ /* Replace the objects already pointed to by tag, text and tail. */
+ tmp = self_elem->tag;
+ self_elem->tag = tag;
+ Py_INCREF(tag);
+ Py_DECREF(tmp);
+
+ tmp = self_elem->text;
+ self_elem->text = Py_None;
+ Py_INCREF(Py_None);
+ Py_DECREF(JOIN_OBJ(tmp));
+
+ tmp = self_elem->tail;
+ self_elem->tail = Py_None;
+ Py_INCREF(Py_None);
+ Py_DECREF(JOIN_OBJ(tmp));
+
+ return 0;
+}
+
LOCAL(int)
element_resize(ElementObject* self, int extra)
{
elements. set an exception and return -1 if allocation failed */
if (!self->extra)
- element_new_extra(self, NULL);
+ create_extra(self, NULL);
size = self->extra->length + extra;
return res;
}
-static PyObject*
-element(PyObject* self, PyObject* args, PyObject* kw)
-{
- PyObject* elem;
-
- PyObject* tag;
- PyObject* attrib = NULL;
- if (!PyArg_ParseTuple(args, "O|O!:Element", &tag,
- &PyDict_Type, &attrib))
- return NULL;
-
- if (attrib || kw) {
- attrib = (attrib) ? PyDict_Copy(attrib) : PyDict_New();
- if (!attrib)
- return NULL;
- if (kw)
- PyDict_Update(attrib, kw);
- } else {
- Py_INCREF(Py_None);
- attrib = Py_None;
- }
-
- elem = element_new(tag, attrib);
-
- Py_DECREF(attrib);
-
- return elem;
-}
-
static PyObject*
subelement(PyObject* self, PyObject* args, PyObject* kw)
{
attrib = Py_None;
}
- elem = element_new(tag, attrib);
+ elem = create_new_element(tag, attrib);
Py_DECREF(attrib);
element_dealloc(ElementObject* self)
{
if (self->extra)
- element_dealloc_extra(self);
+ dealloc_extra(self);
/* discard attributes */
Py_DECREF(self->tag);
RELEASE(sizeof(ElementObject), "destroy element");
- PyObject_Del(self);
+ Py_TYPE(self)->tp_free((PyObject *)self);
}
/* -------------------------------------------------------------------- */
return NULL;
if (self->extra) {
- element_dealloc_extra(self);
+ dealloc_extra(self);
self->extra = NULL;
}
if (!PyArg_ParseTuple(args, ":__copy__"))
return NULL;
- element = (ElementObject*) element_new(
+ element = (ElementObject*) create_new_element(
self->tag, (self->extra) ? self->extra->attrib : Py_None
);
if (!element)
attrib = Py_None;
}
- element = (ElementObject*) element_new(tag, attrib);
+ element = (ElementObject*) create_new_element(tag, attrib);
Py_DECREF(tag);
Py_DECREF(attrib);
return NULL;
if (!self->extra)
- element_new_extra(self, NULL);
+ create_extra(self, NULL);
if (index < 0) {
index += self->extra->length;
if (!attrib)
return NULL;
- elem = element_new(tag, attrib);
+ elem = create_new_element(tag, attrib);
Py_DECREF(attrib);
static PyObject*
element_repr(ElementObject* self)
{
- return PyUnicode_FromFormat("<Element %R at %p>", self->tag, self);
+ if (self->tag)
+ return PyUnicode_FromFormat("<Element %R at %p>", self->tag, self);
+ else
+ return PyUnicode_FromFormat("<Element at %p>", self);
}
static PyObject*
return NULL;
if (!self->extra)
- element_new_extra(self, NULL);
+ create_extra(self, NULL);
attrib = element_get_attrib(self);
if (!attrib)
PyObject* seq = NULL;
if (!self->extra)
- element_new_extra(self, NULL);
+ create_extra(self, NULL);
if (PySlice_GetIndicesEx(item,
self->extra->length,
} else if (strcmp(name, "attrib") == 0) {
PyErr_Clear();
if (!self->extra)
- element_new_extra(self, NULL);
+ create_extra(self, NULL);
res = element_get_attrib(self);
}
Py_INCREF(self->tail);
} else if (strcmp(name, "attrib") == 0) {
if (!self->extra)
- element_new_extra(self, NULL);
+ create_extra(self, NULL);
Py_DECREF(self->extra->attrib);
self->extra->attrib = value;
Py_INCREF(self->extra->attrib);
PyVarObject_HEAD_INIT(NULL, 0)
"Element", sizeof(ElementObject), 0,
/* methods */
- (destructor)element_dealloc, /* tp_dealloc */
- 0, /* tp_print */
- 0, /* tp_getattr */
- (setattrfunc)element_setattr, /* tp_setattr */
- 0, /* tp_reserved */
- (reprfunc)element_repr, /* tp_repr */
- 0, /* tp_as_number */
- &element_as_sequence, /* tp_as_sequence */
- &element_as_mapping, /* tp_as_mapping */
- 0, /* tp_hash */
- 0, /* tp_call */
- 0, /* tp_str */
- (getattrofunc)element_getattro, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT, /* tp_flags */
- 0, /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- element_methods, /* tp_methods */
- 0, /* tp_members */
+ (destructor)element_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ (setattrfunc)element_setattr, /* tp_setattr */
+ 0, /* tp_reserved */
+ (reprfunc)element_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ &element_as_sequence, /* tp_as_sequence */
+ &element_as_mapping, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ (getattrofunc)element_getattro, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ 0, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ element_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)element_init, /* tp_init */
+ PyType_GenericAlloc, /* tp_alloc */
+ element_new, /* tp_new */
+ 0, /* tp_free */
};
/* ==================================================================== */
self->data = NULL;
}
- node = element_new(tag, attrib);
+ node = create_new_element(tag, attrib);
if (!node)
return NULL;
/* python module interface */
static PyMethodDef _functions[] = {
- {"Element", (PyCFunction) element, METH_VARARGS|METH_KEYWORDS},
{"SubElement", (PyCFunction) subelement, METH_VARARGS|METH_KEYWORDS},
{"TreeBuilder", (PyCFunction) treebuilder, METH_VARARGS},
#if defined(USE_EXPAT)
Py_INCREF(elementtree_parseerror_obj);
PyModule_AddObject(m, "ParseError", elementtree_parseerror_obj);
+ Py_INCREF((PyObject *)&Element_Type);
+ PyModule_AddObject(m, "Element", (PyObject *)&Element_Type);
+
return m;
}