mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Merge heads.
This commit is contained in:
commit
01d7058d6a
3 changed files with 90 additions and 37 deletions
|
@ -716,9 +716,9 @@ TreeBuilder Objects
|
||||||
Generic element structure builder. This builder converts a sequence of
|
Generic element structure builder. This builder converts a sequence of
|
||||||
start, data, and end method calls to a well-formed element structure. You
|
start, data, and end method calls to a well-formed element structure. You
|
||||||
can use this class to build an element structure using a custom XML parser,
|
can use this class to build an element structure using a custom XML parser,
|
||||||
or a parser for some other XML-like format. The *element_factory* is called
|
or a parser for some other XML-like format. *element_factory*, when given,
|
||||||
to create new :class:`Element` instances when given.
|
must be a callable accepting two positional arguments: a tag and
|
||||||
|
a dict of attributes. It is expected to return a new element instance.
|
||||||
|
|
||||||
.. method:: close()
|
.. method:: close()
|
||||||
|
|
||||||
|
|
|
@ -1959,6 +1959,8 @@ class TreeBuilderTest(unittest.TestCase):
|
||||||
' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
|
' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
|
||||||
'<html>text</html>')
|
'<html>text</html>')
|
||||||
|
|
||||||
|
sample2 = '''<toplevel>sometext</toplevel>'''
|
||||||
|
|
||||||
def test_dummy_builder(self):
|
def test_dummy_builder(self):
|
||||||
class BaseDummyBuilder:
|
class BaseDummyBuilder:
|
||||||
def close(self):
|
def close(self):
|
||||||
|
@ -1993,11 +1995,19 @@ class TreeBuilderTest(unittest.TestCase):
|
||||||
e = parser.close()
|
e = parser.close()
|
||||||
self.assertEqual(e.tag, 'html')
|
self.assertEqual(e.tag, 'html')
|
||||||
|
|
||||||
# XXX in _elementtree, the constructor of TreeBuilder expects no
|
|
||||||
# arguments
|
|
||||||
@unittest.expectedFailure
|
|
||||||
def test_element_factory(self):
|
def test_element_factory(self):
|
||||||
tb = ET.TreeBuilder(element_factory=lambda: ET.Element())
|
lst = []
|
||||||
|
def myfactory(tag, attrib):
|
||||||
|
nonlocal lst
|
||||||
|
lst.append(tag)
|
||||||
|
return ET.Element(tag, attrib)
|
||||||
|
|
||||||
|
tb = ET.TreeBuilder(element_factory=myfactory)
|
||||||
|
parser = ET.XMLParser(target=tb)
|
||||||
|
parser.feed(self.sample2)
|
||||||
|
parser.close()
|
||||||
|
|
||||||
|
self.assertEqual(lst, ['toplevel'])
|
||||||
|
|
||||||
@unittest.expectedFailure # XXX issue 14007 with C ElementTree
|
@unittest.expectedFailure # XXX issue 14007 with C ElementTree
|
||||||
def test_doctype(self):
|
def test_doctype(self):
|
||||||
|
|
|
@ -191,6 +191,15 @@ list_join(PyObject* list)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Is the given object an empty dictionary?
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
is_empty_dict(PyObject *obj)
|
||||||
|
{
|
||||||
|
return PyDict_CheckExact(obj) && PyDict_Size(obj) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
/* the Element type */
|
/* the Element type */
|
||||||
|
|
||||||
|
@ -297,14 +306,9 @@ create_new_element(PyObject* tag, PyObject* attrib)
|
||||||
self = PyObject_GC_New(ElementObject, &Element_Type);
|
self = PyObject_GC_New(ElementObject, &Element_Type);
|
||||||
if (self == NULL)
|
if (self == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* use None for empty dictionaries */
|
|
||||||
if (PyDict_CheckExact(attrib) && !PyDict_Size(attrib))
|
|
||||||
attrib = Py_None;
|
|
||||||
|
|
||||||
self->extra = NULL;
|
self->extra = NULL;
|
||||||
|
|
||||||
if (attrib != Py_None) {
|
if (attrib != Py_None && !is_empty_dict(attrib)) {
|
||||||
if (create_extra(self, attrib) < 0) {
|
if (create_extra(self, attrib) < 0) {
|
||||||
PyObject_Del(self);
|
PyObject_Del(self);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -416,22 +420,14 @@ element_init(PyObject *self, PyObject *args, PyObject *kwds)
|
||||||
|
|
||||||
self_elem = (ElementObject *)self;
|
self_elem = (ElementObject *)self;
|
||||||
|
|
||||||
/* Use None for empty dictionaries */
|
if (attrib != Py_None && !is_empty_dict(attrib)) {
|
||||||
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) {
|
if (create_extra(self_elem, attrib) < 0) {
|
||||||
PyObject_Del(self_elem);
|
PyObject_Del(self_elem);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If create_extra needed attrib, it took a reference to it, so we can
|
/* We own a reference to attrib here and it's no longer needed. */
|
||||||
* release ours anyway.
|
|
||||||
*/
|
|
||||||
Py_DECREF(attrib);
|
Py_DECREF(attrib);
|
||||||
|
|
||||||
/* Replace the objects already pointed to by tag, text and tail. */
|
/* Replace the objects already pointed to by tag, text and tail. */
|
||||||
|
@ -1813,6 +1809,8 @@ typedef struct {
|
||||||
PyObject *stack; /* element stack */
|
PyObject *stack; /* element stack */
|
||||||
Py_ssize_t index; /* current stack size (0 means empty) */
|
Py_ssize_t index; /* current stack size (0 means empty) */
|
||||||
|
|
||||||
|
PyObject *element_factory;
|
||||||
|
|
||||||
/* element tracing */
|
/* element tracing */
|
||||||
PyObject *events; /* list of events, or NULL if not collecting */
|
PyObject *events; /* list of events, or NULL if not collecting */
|
||||||
PyObject *start_event_obj; /* event objects (NULL to ignore) */
|
PyObject *start_event_obj; /* event objects (NULL to ignore) */
|
||||||
|
@ -1841,6 +1839,7 @@ treebuilder_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
t->last = (ElementObject *)Py_None;
|
t->last = (ElementObject *)Py_None;
|
||||||
|
|
||||||
t->data = NULL;
|
t->data = NULL;
|
||||||
|
t->element_factory = NULL;
|
||||||
t->stack = PyList_New(20);
|
t->stack = PyList_New(20);
|
||||||
if (!t->stack) {
|
if (!t->stack) {
|
||||||
Py_DECREF(t->this);
|
Py_DECREF(t->this);
|
||||||
|
@ -1859,11 +1858,38 @@ treebuilder_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
static int
|
static int
|
||||||
treebuilder_init(PyObject *self, PyObject *args, PyObject *kwds)
|
treebuilder_init(PyObject *self, PyObject *args, PyObject *kwds)
|
||||||
{
|
{
|
||||||
|
static char *kwlist[] = {"element_factory", NULL};
|
||||||
|
PyObject *element_factory = NULL;
|
||||||
|
TreeBuilderObject *self_tb = (TreeBuilderObject *)self;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:TreeBuilder", kwlist,
|
||||||
|
&element_factory)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element_factory) {
|
||||||
|
Py_INCREF(element_factory);
|
||||||
|
Py_XDECREF(self_tb->element_factory);
|
||||||
|
self_tb->element_factory = element_factory;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
treebuilder_dealloc(TreeBuilderObject *self)
|
treebuilder_gc_traverse(TreeBuilderObject *self, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
Py_VISIT(self->root);
|
||||||
|
Py_VISIT(self->this);
|
||||||
|
Py_VISIT(self->last);
|
||||||
|
Py_VISIT(self->data);
|
||||||
|
Py_VISIT(self->stack);
|
||||||
|
Py_VISIT(self->element_factory);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
treebuilder_gc_clear(TreeBuilderObject *self)
|
||||||
{
|
{
|
||||||
Py_XDECREF(self->end_ns_event_obj);
|
Py_XDECREF(self->end_ns_event_obj);
|
||||||
Py_XDECREF(self->start_ns_event_obj);
|
Py_XDECREF(self->start_ns_event_obj);
|
||||||
|
@ -1874,8 +1900,16 @@ treebuilder_dealloc(TreeBuilderObject *self)
|
||||||
Py_XDECREF(self->data);
|
Py_XDECREF(self->data);
|
||||||
Py_DECREF(self->last);
|
Py_DECREF(self->last);
|
||||||
Py_DECREF(self->this);
|
Py_DECREF(self->this);
|
||||||
|
Py_CLEAR(self->element_factory);
|
||||||
Py_XDECREF(self->root);
|
Py_XDECREF(self->root);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
treebuilder_dealloc(TreeBuilderObject *self)
|
||||||
|
{
|
||||||
|
PyObject_GC_UnTrack(self);
|
||||||
|
treebuilder_gc_clear(self);
|
||||||
Py_TYPE(self)->tp_free((PyObject *)self);
|
Py_TYPE(self)->tp_free((PyObject *)self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1904,9 +1938,14 @@ treebuilder_handle_start(TreeBuilderObject* self, PyObject* tag,
|
||||||
self->data = NULL;
|
self->data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = create_new_element(tag, attrib);
|
if (self->element_factory) {
|
||||||
if (!node)
|
node = PyObject_CallFunction(self->element_factory, "OO", tag, attrib);
|
||||||
|
} else {
|
||||||
|
node = create_new_element(tag, attrib);
|
||||||
|
}
|
||||||
|
if (!node) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
this = (PyObject*) self->this;
|
this = (PyObject*) self->this;
|
||||||
|
|
||||||
|
@ -2180,10 +2219,11 @@ static PyTypeObject TreeBuilder_Type = {
|
||||||
0, /* tp_getattro */
|
0, /* tp_getattro */
|
||||||
0, /* tp_setattro */
|
0, /* tp_setattro */
|
||||||
0, /* tp_as_buffer */
|
0, /* tp_as_buffer */
|
||||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
|
||||||
|
/* tp_flags */
|
||||||
0, /* tp_doc */
|
0, /* tp_doc */
|
||||||
0, /* tp_traverse */
|
(traverseproc)treebuilder_gc_traverse, /* tp_traverse */
|
||||||
0, /* tp_clear */
|
(inquiry)treebuilder_gc_clear, /* tp_clear */
|
||||||
0, /* tp_richcompare */
|
0, /* tp_richcompare */
|
||||||
0, /* tp_weaklistoffset */
|
0, /* tp_weaklistoffset */
|
||||||
0, /* tp_iter */
|
0, /* tp_iter */
|
||||||
|
@ -2443,17 +2483,20 @@ expat_start_handler(XMLParserObject* self, const XML_Char* tag_in,
|
||||||
attrib = Py_None;
|
attrib = Py_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TreeBuilder_CheckExact(self->target))
|
/* If we get None, pass an empty dictionary on */
|
||||||
|
if (attrib == Py_None) {
|
||||||
|
Py_DECREF(attrib);
|
||||||
|
attrib = PyDict_New();
|
||||||
|
if (!attrib)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TreeBuilder_CheckExact(self->target)) {
|
||||||
/* shortcut */
|
/* shortcut */
|
||||||
res = treebuilder_handle_start((TreeBuilderObject*) self->target,
|
res = treebuilder_handle_start((TreeBuilderObject*) self->target,
|
||||||
tag, attrib);
|
tag, attrib);
|
||||||
|
}
|
||||||
else if (self->handle_start) {
|
else if (self->handle_start) {
|
||||||
if (attrib == Py_None) {
|
|
||||||
Py_DECREF(attrib);
|
|
||||||
attrib = PyDict_New();
|
|
||||||
if (!attrib)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res = PyObject_CallFunction(self->handle_start, "OO", tag, attrib);
|
res = PyObject_CallFunction(self->handle_start, "OO", tag, attrib);
|
||||||
} else
|
} else
|
||||||
res = NULL;
|
res = NULL;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue