https://github.com/python/cpython/commit/a2f4f1913b5bf6d07295d21f9ab78d366d44fce0
commit: a2f4f1913b5bf6d07295d21f9ab78d366d44fce0
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-09-13T13:24:17Z
summary:
gh-63102: Support custom targets in the pull parser (GH-156888)
The list of events was collected by the TreeBuilder in the C implementation,
so the pull parser only worked with the standard target. The parser itself
now collects the events, and reports what the target returns. XMLPullParser
and iterparse() get the target parameter, which makes it possible to parse a
large document incrementally without building a tree for it.
The namespace events no longer need a separate code path: the parser reports
the prefix and the uri if the target does not implement start_ns()/end_ns(),
as the Python implementation already did.
files:
A Misc/NEWS.d/next/Library/2026-09-01-02-30-00.gh-issue-63102.Pz3wK8.rst
M Doc/library/xml.etree.elementtree.rst
M Doc/whatsnew/3.16.rst
M Lib/test/test_xml_etree.py
M Lib/xml/etree/ElementTree.py
M Modules/_elementtree.c
diff --git a/Doc/library/xml.etree.elementtree.rst
b/Doc/library/xml.etree.elementtree.rst
index 1869d9b47780a9b..4f2497c8246be30 100644
--- a/Doc/library/xml.etree.elementtree.rst
+++ b/Doc/library/xml.etree.elementtree.rst
@@ -640,10 +640,11 @@ Functions
element instance. Return ``True`` if this is an element object.
-.. function:: iterparse(source, events=None, parser=None)
+.. function:: iterparse(source, events=None, parser=None, *, target=None)
- Parses an XML section into an element tree incrementally, and reports what's
- going on to the user. *source* is a filename or :term:`file object`
+ Parses an XML section incrementally, and reports what's going on to the
+ user. Unless a custom target is used, an element tree is built.
+ *source* is a filename or :term:`file object`
containing XML data. *events* is a sequence of events to report back. The
supported events are the strings ``"start"``, ``"end"``, ``"comment"``,
``"pi"``, ``"start-ns"`` and ``"end-ns"``
@@ -651,11 +652,18 @@ Functions
information). If *events* is omitted, only ``"end"`` events are reported.
*parser* is an optional parser instance.
If not given, the standard :class:`XMLParser` parser is used.
- *parser* must be an instance of :class:`XMLParser` or its subclass
- and can only use the default :class:`TreeBuilder` as a target.
- Returns an :term:`iterator` providing ``(event, elem)`` pairs;
+ *parser* must be an instance of :class:`XMLParser` or its subclass.
+ *target* is the target of the standard parser,
+ as for :class:`XMLPullParser`;
+ it cannot be used together with *parser*.
+ Returns an :term:`iterator` providing ``(event, obj)`` pairs,
+ as described for :meth:`XMLPullParser.read_events`;
it has a ``root`` attribute that references the root element of the
- resulting XML tree once *source* is fully read.
+ resulting XML tree, or the value returned by the ``close()`` method
+ of a custom target, once *source* is fully read.
+ If a custom target is used, it is set to the value returned
+ by the :meth:`!close` method of the target.
+
The iterator has the :meth:`!close` method that closes the internal
file object if *source* is a filename.
@@ -691,6 +699,9 @@ Functions
A :exc:`ResourceWarning` is now emitted if the iterator opened a file
and is not explicitly closed.
+ .. versionchanged:: next
+ Added the *target* parameter.
+
.. function:: parse(source, parser=None)
@@ -1524,7 +1535,7 @@ XMLParser Objects
XMLPullParser Objects
^^^^^^^^^^^^^^^^^^^^^
-.. class:: XMLPullParser(events=None)
+.. class:: XMLPullParser(events=None, *, target=None)
A pull parser suitable for non-blocking applications. Its input-side API is
similar to that of :class:`XMLParser`, but instead of pushing calls to a
@@ -1535,6 +1546,20 @@ XMLPullParser Objects
are used to get detailed namespace information). If *events* is omitted,
only ``"end"`` events are reported.
+ *target* is the target object of the underlying :class:`XMLParser`.
+ If omitted, the standard :class:`TreeBuilder` is used,
+ and the reported objects are :class:`Element` instances.
+ With other targets the reported object is the value returned
+ by the corresponding method of the target,
+ so no tree is built if the target does not build one.
+ The target must implement the methods for all requested events,
+ except :meth:`!start_ns` and :meth:`!end_ns`:
+ if they are not implemented, a ``(prefix, uri)`` tuple and ``None``
+ are reported for the ``"start-ns"`` and ``"end-ns"`` events.
+
+ .. versionchanged:: next
+ Added the *target* parameter.
+
.. method:: feed(data)
Feed the given data to the parser. *data* is a string
@@ -1567,9 +1592,10 @@ XMLPullParser Objects
Return an iterator over the events which have been encountered in the
data fed to the
- parser. The iterator yields ``(event, elem)`` pairs, where *event* is a
- string representing the type of event (e.g. ``"end"``) and *elem* is the
- encountered :class:`Element` object, or other context value as follows.
+ parser. The iterator yields ``(event, obj)`` pairs, where *event* is a
+ string representing the type of event (e.g. ``"end"``) and *obj* is the
+ object returned by the corresponding method of the target.
+ With the standard :class:`TreeBuilder` it is as follows.
* ``start``, ``end``: the current Element.
* ``comment``, ``pi``: the current comment / processing instruction
diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst
index 1098b152e51eb41..2eccc8e35605596 100644
--- a/Doc/whatsnew/3.16.rst
+++ b/Doc/whatsnew/3.16.rst
@@ -708,6 +708,13 @@ xml
rather than defaulted from the DTD.
(Contributed by Jason Orendorff and Serhiy Storchaka in :gh:`44871`.)
+* :class:`~xml.etree.ElementTree.XMLPullParser` and
+ :func:`~xml.etree.ElementTree.iterparse` now support the *target* parameter.
+ The reported object is the value returned by the corresponding method of
+ the target, so a large document can be parsed incrementally without
+ building a tree for it.
+ (Contributed by Serhiy Storchaka in :gh:`63102`.)
+
zipfile
-------
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
index 90e556ec95308bc..f87a47045dd1713 100644
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -1656,6 +1656,43 @@ def test_unknown_events(self):
del cm
gc_collect()
+ class Target:
+ # a target which does not build a tree
+ def start(self, tag, attrib):
+ return tag
+ def end(self, tag):
+ return tag
+ def data(self, data):
+ pass
+
+ def test_target(self):
+ # gh-63102: a custom target reports its own objects
+ with open(SIMPLE_XMLFILE, 'rb') as f:
+ it = ET.iterparse(f, events=('start', 'end'), target=self.Target())
+ self.assertEqual(list(it), [
+ ('start', 'root'),
+ ('start', 'element'),
+ ('end', 'element'),
+ ('start', 'element'),
+ ('end', 'element'),
+ ('start', 'empty-element'),
+ ('end', 'empty-element'),
+ ('end', 'root'),
+ ])
+ self.assertIsNone(it.root)
+
+ def test_parser_with_target(self):
+ with open(SIMPLE_XMLFILE, 'rb') as f:
+ parser = ET.XMLParser(target=self.Target())
+ it = ET.iterparse(f, events=('start',), parser=parser)
+ self.assertEqual(next(it), ('start', 'root'))
+
+ def test_target_and_parser(self):
+ with self.assertRaisesRegex(ValueError,
+ "can't specify both parser and target"):
+ ET.iterparse(SIMPLE_XMLFILE, parser=ET.XMLParser(),
+ target=self.Target())
+
def test_non_utf8(self):
source = io.BytesIO(
b"<?xml version='1.0' encoding='iso-8859-1'?>\n"
@@ -2067,6 +2104,76 @@ def __next__(self):
self._feed(parser, "<foo>bar</foo>")
self.assert_event_tags(parser, [('start', 'foo'), ('end', 'foo')])
+ # gh-63102: the pull parser reports events from any target
+ class SimpleTarget:
+ def start(self, tag, attrib):
+ return ('start', tag)
+ def end(self, tag):
+ return ('end', tag)
+ def data(self, data):
+ pass
+ def comment(self, text):
+ return ('comment', text)
+ def pi(self, target, data=None):
+ return ('pi', target)
+ def close(self):
+ return 'closed'
+
+ def test_custom_target(self):
+ parser = ET.XMLPullParser(events=('start', 'end'),
+ target=self.SimpleTarget())
+ self._feed(parser, "<root><element/></root>")
+ self.assert_event_tuples(parser, [
+ ('start', ('start', 'root')),
+ ('start', ('start', 'element')),
+ ('end', ('end', 'element')),
+ ('end', ('end', 'root')),
+ ])
+
+ def test_custom_target_comment_pi(self):
+ parser = ET.XMLPullParser(events=('comment', 'pi'),
+ target=self.SimpleTarget())
+ self._feed(parser, "<root><!-- text --><?pitarget data?></root>")
+ self.assert_event_tuples(parser, [
+ ('comment', ('comment', ' text ')),
+ ('pi', ('pi', 'pitarget')),
+ ])
+
+ def test_custom_target_without_method(self):
+ class Target:
+ def close(self):
+ pass
+ for event in ('start', 'end', 'comment', 'pi'):
+ with self.subTest(event=event):
+ with self.assertRaisesRegex(TypeError,
+ "the target does not support %r events" % event):
+ ET.XMLPullParser(events=(event,), target=Target())
+ # the namespace events do not need methods of the target
+ parser = ET.XMLPullParser(events=('start-ns', 'end-ns'),
+ target=Target())
+ self._feed(parser, "<root xmlns='namespace' />")
+ self.assert_event_tuples(parser, [
+ ('start-ns', ('', 'namespace')),
+ ('end-ns', None),
+ ])
+
+ def test_custom_target_ns_events(self):
+ # the target does not implement start_ns()/end_ns(),
+ # so the prefix and the uri are reported
+ parser = ET.XMLPullParser(events=('start-ns', 'end-ns'),
+ target=self.SimpleTarget())
+ self._feed(parser, "<root xmlns='namespace' />")
+ self.assert_event_tuples(parser, [
+ ('start-ns', ('', 'namespace')),
+ ('end-ns', None),
+ ])
+
+ def test_custom_target_close(self):
+ parser = ET.XMLPullParser(events=('end',), target=self.SimpleTarget())
+ self._feed(parser, "<root/>")
+ parser.close()
+ self.assert_event_tuples(parser, [('end', ('end', 'root'))])
+
def test_unknown_event(self):
with self.assertRaises(ValueError):
ET.XMLPullParser(events=('start', 'end', 'bogus'))
diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py
index ce98e4dc24a0d3a..3b4bfa3bd483c2d 100644
--- a/Lib/xml/etree/ElementTree.py
+++ b/Lib/xml/etree/ElementTree.py
@@ -1239,7 +1239,7 @@ def parse(source, parser=None):
return tree
-def iterparse(source, events=None, parser=None):
+def iterparse(source, events=None, parser=None, *, target=None):
"""Incrementally parse XML document into ElementTree.
This class also reports what's going on to the user based on the
@@ -1250,14 +1250,14 @@ def iterparse(source, events=None, parser=None):
*source* is a filename or file object containing XML data, *events* is
a list of events to report back, *parser* is an optional parser
- instance.
+ instance, *target* is an optional target of the standard parser.
Returns an iterator providing (event, elem) pairs.
"""
# Use the internal, undocumented _parser argument for now; When the
# parser argument of iterparse is removed, this can be killed.
- pullparser = XMLPullParser(events=events, _parser=parser)
+ pullparser = XMLPullParser(events=events, target=target, _parser=parser)
if not hasattr(source, "read"):
source = open(source, "rb")
@@ -1309,13 +1309,19 @@ def __del__(self, _warn=warnings.warn):
class XMLPullParser:
- def __init__(self, events=None, *, _parser=None):
+ def __init__(self, events=None, *, target=None, _parser=None):
# The _parser argument is for internal use only and must not be relied
# upon in user code. It will be removed in a future release.
# See https://bugs.python.org/issue17741 for more details.
self._events_queue = collections.deque()
- self._parser = _parser or XMLParser(target=TreeBuilder())
+ if _parser is None:
+ if target is None:
+ target = TreeBuilder()
+ _parser = XMLParser(target=target)
+ elif target is not None:
+ raise ValueError("can't specify both parser and target")
+ self._parser = _parser
# wire up the parser for event reporting
if events is None:
events = ("end",)
@@ -1611,6 +1617,10 @@ def _setevents(self, events_queue, events_to_report):
parser = self._parser
append = events_queue.append
for event_name in events_to_report:
+ if (event_name in ("start", "end", "comment", "pi")
+ and not hasattr(self.target, event_name)):
+ raise TypeError("the target does not support %r events"
+ % event_name)
if event_name == "start":
parser.ordered_attributes = 1
def handler(tag, attrib_in, event=event_name, append=append,
@@ -1643,13 +1653,14 @@ def handler(prefix, event=event_name, append=append):
append((event, None))
parser.EndNamespaceDeclHandler = handler
elif event_name == 'comment':
- def handler(text, event=event_name, append=append, self=self):
- append((event, self.target.comment(text)))
+ def handler(text, event=event_name, append=append,
+ comment=self.target.comment):
+ append((event, comment(text)))
parser.CommentHandler = handler
elif event_name == 'pi':
def handler(pi_target, data, event=event_name, append=append,
- self=self):
- append((event, self.target.pi(pi_target, data)))
+ pi=self.target.pi):
+ append((event, pi(pi_target, data)))
parser.ProcessingInstructionHandler = handler
else:
raise ValueError("unknown event %r" % event_name)
diff --git
a/Misc/NEWS.d/next/Library/2026-09-01-02-30-00.gh-issue-63102.Pz3wK8.rst
b/Misc/NEWS.d/next/Library/2026-09-01-02-30-00.gh-issue-63102.Pz3wK8.rst
new file mode 100644
index 000000000000000..cd769c804aab6c6
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-09-01-02-30-00.gh-issue-63102.Pz3wK8.rst
@@ -0,0 +1,6 @@
+:class:`~xml.etree.ElementTree.XMLPullParser` and
+:func:`~xml.etree.ElementTree.iterparse` now support the *target* parameter.
+The reported object is the value returned by the corresponding method
+of the target, so no tree is built if the target does not build one.
+Only the standard :class:`~xml.etree.ElementTree.TreeBuilder` was supported
+in the C implementation before.
diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c
index 6f51f10b2b22759..36115e61c2c2152 100644
--- a/Modules/_elementtree.c
+++ b/Modules/_elementtree.c
@@ -2482,14 +2482,6 @@ typedef struct {
PyObject *pi_factory;
/* element tracing */
- PyObject *events_append; /* the append method of the list of events, or
NULL */
- PyObject *start_event_obj; /* event objects (NULL to ignore) */
- PyObject *end_event_obj;
- PyObject *start_ns_event_obj;
- PyObject *end_ns_event_obj;
- PyObject *comment_event_obj;
- PyObject *pi_event_obj;
-
char insert_comments;
char insert_pis;
elementtreestate *state;
@@ -2523,10 +2515,6 @@ treebuilder_new(PyTypeObject *type, PyObject *args,
PyObject *kwds)
}
t->index = 0;
- t->events_append = NULL;
- t->start_event_obj = t->end_event_obj = NULL;
- t->start_ns_event_obj = t->end_ns_event_obj = NULL;
- t->comment_event_obj = t->pi_event_obj = NULL;
t->insert_comments = t->insert_pis = 0;
t->state = get_elementtree_state_by_type(type);
}
@@ -2609,13 +2597,6 @@ treebuilder_gc_traverse(PyObject *op, visitproc visit,
void *arg)
{
TreeBuilderObject *self = _TreeBuilder_CAST(op);
Py_VISIT(Py_TYPE(self));
- Py_VISIT(self->pi_event_obj);
- Py_VISIT(self->comment_event_obj);
- Py_VISIT(self->end_ns_event_obj);
- Py_VISIT(self->start_ns_event_obj);
- Py_VISIT(self->end_event_obj);
- Py_VISIT(self->start_event_obj);
- Py_VISIT(self->events_append);
Py_VISIT(self->root);
Py_VISIT(self->this);
Py_VISIT(self->last);
@@ -2632,13 +2613,6 @@ static int
treebuilder_gc_clear(PyObject *op)
{
TreeBuilderObject *self = _TreeBuilder_CAST(op);
- Py_CLEAR(self->pi_event_obj);
- Py_CLEAR(self->comment_event_obj);
- Py_CLEAR(self->end_ns_event_obj);
- Py_CLEAR(self->start_ns_event_obj);
- Py_CLEAR(self->end_event_obj);
- Py_CLEAR(self->start_event_obj);
- Py_CLEAR(self->events_append);
Py_CLEAR(self->stack);
Py_CLEAR(self->data);
Py_CLEAR(self->last);
@@ -2808,24 +2782,6 @@ treebuilder_add_subelement(elementtreestate *st,
PyObject *element,
}
}
-LOCAL(int)
-treebuilder_append_event(TreeBuilderObject *self, PyObject *action,
- PyObject *node)
-{
- if (action != NULL) {
- PyObject *res;
- PyObject *event = _PyTuple_FromPair(action, node);
- if (event == NULL)
- return -1;
- res = PyObject_CallOneArg(self->events_append, event);
- Py_DECREF(event);
- if (res == NULL)
- return -1;
- Py_DECREF(res);
- }
- return 0;
-}
-
/* -------------------------------------------------------------------- */
/* handlers */
@@ -2891,9 +2847,6 @@ treebuilder_handle_start(TreeBuilderObject* self,
PyObject* tag,
Py_SETREF(self->this, Py_NewRef(node));
Py_SETREF(self->last, Py_NewRef(node));
- if (treebuilder_append_event(self, self->start_event_obj, node) < 0)
- goto error;
-
return node;
error:
@@ -2954,11 +2907,6 @@ treebuilder_handle_end(TreeBuilderObject* self,
PyObject* tag)
Py_DECREF(last);
Py_XDECREF(last_for_tail);
- if (treebuilder_append_event(self, self->end_event_obj, self->last) < 0) {
- Py_DECREF(this);
- return NULL;
- }
-
return this;
}
@@ -2988,11 +2936,6 @@ treebuilder_handle_comment(TreeBuilderObject* self,
PyObject* text)
comment = Py_NewRef(text);
}
- if (self->events_append && self->comment_event_obj) {
- if (treebuilder_append_event(self, self->comment_event_obj, comment) <
0)
- goto error;
- }
-
return comment;
error:
@@ -3031,11 +2974,6 @@ treebuilder_handle_pi(TreeBuilderObject* self, PyObject*
target, PyObject* text)
}
}
- if (self->events_append && self->pi_event_obj) {
- if (treebuilder_append_event(self, self->pi_event_obj, pi) < 0)
- goto error;
- }
-
return pi;
error:
@@ -3043,39 +2981,6 @@ treebuilder_handle_pi(TreeBuilderObject* self, PyObject*
target, PyObject* text)
return NULL;
}
-LOCAL(PyObject*)
-treebuilder_handle_start_ns(TreeBuilderObject* self, PyObject* prefix,
PyObject* uri)
-{
- PyObject* parcel;
-
- if (self->events_append && self->start_ns_event_obj) {
- parcel = _PyTuple_FromPair(prefix, uri);
- if (!parcel) {
- return NULL;
- }
-
- if (treebuilder_append_event(self, self->start_ns_event_obj, parcel) <
0) {
- Py_DECREF(parcel);
- return NULL;
- }
- Py_DECREF(parcel);
- }
-
- Py_RETURN_NONE;
-}
-
-LOCAL(PyObject*)
-treebuilder_handle_end_ns(TreeBuilderObject* self, PyObject* prefix)
-{
- if (self->events_append && self->end_ns_event_obj) {
- if (treebuilder_append_event(self, self->end_ns_event_obj, prefix) <
0) {
- return NULL;
- }
- }
-
- Py_RETURN_NONE;
-}
-
/* -------------------------------------------------------------------- */
/* methods (in alphabetical order) */
@@ -3222,6 +3127,15 @@ typedef struct {
PyObject *handle_start_ns;
PyObject *handle_end_ns;
+
+ /* event reporting for the pull API */
+ PyObject *events_append; /* the append method of the list of events */
+ PyObject *start_event_obj; /* event objects (NULL to ignore) */
+ PyObject *end_event_obj;
+ PyObject *start_ns_event_obj;
+ PyObject *end_ns_event_obj;
+ PyObject *comment_event_obj;
+ PyObject *pi_event_obj;
PyObject *handle_start;
PyObject *handle_data;
PyObject *handle_end;
@@ -3411,6 +3325,26 @@ expat_default_handler(void *op, const XML_Char *data_in,
int data_len)
Py_DECREF(key);
}
+/* Append (action, node) to the list of events of the pull parser. */
+LOCAL(int)
+xmlparser_append_event(XMLParserObject *self, PyObject *action, PyObject *node)
+{
+ if (self->events_append == NULL || action == NULL || node == NULL) {
+ return 0;
+ }
+ PyObject *event = _PyTuple_FromPair(action, node);
+ if (event == NULL) {
+ return -1;
+ }
+ PyObject *res = PyObject_CallOneArg(self->events_append, event);
+ Py_DECREF(event);
+ if (res == NULL) {
+ return -1;
+ }
+ Py_DECREF(res);
+ return 0;
+}
+
static void
expat_start_handler(void *op, const XML_Char *tag_in,
const XML_Char **attrib_in)
@@ -3486,7 +3420,10 @@ expat_start_handler(void *op, const XML_Char *tag_in,
Py_DECREF(tag);
Py_XDECREF(attrib);
- Py_XDECREF(res);
+ if (res != NULL) {
+ (void)xmlparser_append_event(self, self->start_event_obj, res);
+ Py_DECREF(res);
+ }
}
static void
@@ -3543,7 +3480,10 @@ expat_end_handler(void *op, const XML_Char *tag_in)
}
}
- Py_XDECREF(res);
+ if (res != NULL) {
+ (void)xmlparser_append_event(self, self->end_event_obj, res);
+ Py_DECREF(res);
+ }
}
static void
@@ -3563,42 +3503,34 @@ expat_start_ns_handler(void *op, const XML_Char
*prefix_in,
if (!prefix_in)
prefix_in = "";
- elementtreestate *st = self->state;
- if (TreeBuilder_CheckExact(st, self->target)) {
- /* shortcut - TreeBuilder does not actually implement .start_ns() */
- TreeBuilderObject *target = (TreeBuilderObject*) self->target;
-
- if (target->events_append && target->start_ns_event_obj) {
- prefix = PyUnicode_DecodeUTF8(prefix_in, strlen(prefix_in),
"strict");
- if (!prefix)
- return;
- uri = PyUnicode_DecodeUTF8(uri_in, strlen(uri_in), "strict");
- if (!uri) {
- Py_DECREF(prefix);
- return;
- }
+ if (self->handle_start_ns == NULL && self->start_ns_event_obj == NULL) {
+ return;
+ }
- res = treebuilder_handle_start_ns(target, prefix, uri);
- Py_DECREF(uri);
- Py_DECREF(prefix);
- }
- } else if (self->handle_start_ns) {
- prefix = PyUnicode_DecodeUTF8(prefix_in, strlen(prefix_in), "strict");
- if (!prefix)
- return;
- uri = PyUnicode_DecodeUTF8(uri_in, strlen(uri_in), "strict");
- if (!uri) {
- Py_DECREF(prefix);
- return;
- }
+ prefix = PyUnicode_DecodeUTF8(prefix_in, strlen(prefix_in), "strict");
+ if (!prefix)
+ return;
+ uri = PyUnicode_DecodeUTF8(uri_in, strlen(uri_in), "strict");
+ if (!uri) {
+ Py_DECREF(prefix);
+ return;
+ }
+ if (self->handle_start_ns) {
PyObject *args[2] = {prefix, uri};
res = PyObject_Vectorcall(self->handle_start_ns, args, 2, NULL);
- Py_DECREF(uri);
- Py_DECREF(prefix);
}
+ else {
+ /* the target does not implement .start_ns(), report the pair */
+ res = _PyTuple_FromPair(prefix, uri);
+ }
+ Py_DECREF(uri);
+ Py_DECREF(prefix);
- Py_XDECREF(res);
+ if (res != NULL) {
+ (void)xmlparser_append_event(self, self->start_ns_event_obj, res);
+ Py_DECREF(res);
+ }
}
static void
@@ -3614,15 +3546,7 @@ expat_end_ns_handler(void *op, const XML_Char *prefix_in)
if (!prefix_in)
prefix_in = "";
- elementtreestate *st = self->state;
- if (TreeBuilder_CheckExact(st, self->target)) {
- /* shortcut - TreeBuilder does not actually implement .end_ns() */
- TreeBuilderObject *target = (TreeBuilderObject*) self->target;
-
- if (target->events_append && target->end_ns_event_obj) {
- res = treebuilder_handle_end_ns(target, Py_None);
- }
- } else if (self->handle_end_ns) {
+ if (self->handle_end_ns) {
prefix = PyUnicode_DecodeUTF8(prefix_in, strlen(prefix_in), "strict");
if (!prefix)
return;
@@ -3630,8 +3554,15 @@ expat_end_ns_handler(void *op, const XML_Char *prefix_in)
res = PyObject_CallOneArg(self->handle_end_ns, prefix);
Py_DECREF(prefix);
}
+ else if (self->end_ns_event_obj) {
+ /* the target does not implement .end_ns() */
+ res = Py_NewRef(Py_None);
+ }
- Py_XDECREF(res);
+ if (res != NULL) {
+ (void)xmlparser_append_event(self, self->end_ns_event_obj, res);
+ Py_DECREF(res);
+ }
}
static void
@@ -3654,16 +3585,22 @@ expat_comment_handler(void *op, const XML_Char
*comment_in)
return; /* parser will look for errors */
res = treebuilder_handle_comment(target, comment);
- Py_XDECREF(res);
Py_DECREF(comment);
+ if (res != NULL) {
+ (void)xmlparser_append_event(self, self->comment_event_obj, res);
+ Py_DECREF(res);
+ }
} else if (self->handle_comment) {
comment = PyUnicode_DecodeUTF8(comment_in, strlen(comment_in),
"strict");
if (!comment)
return;
res = PyObject_CallOneArg(self->handle_comment, comment);
- Py_XDECREF(res);
Py_DECREF(comment);
+ if (res != NULL) {
+ (void)xmlparser_append_event(self, self->comment_event_obj, res);
+ Py_DECREF(res);
+ }
}
}
@@ -3743,7 +3680,7 @@ expat_pi_handler(void *op, const XML_Char *target_in,
/* shortcut */
TreeBuilderObject *target = (TreeBuilderObject*) self->target;
- if ((target->events_append && target->pi_event_obj) ||
target->insert_pis) {
+ if (self->pi_event_obj || target->insert_pis) {
pi_target = PyUnicode_DecodeUTF8(target_in, strlen(target_in),
"strict");
if (!pi_target)
goto error;
@@ -3751,9 +3688,12 @@ expat_pi_handler(void *op, const XML_Char *target_in,
if (!data)
goto error;
res = treebuilder_handle_pi(target, pi_target, data);
- Py_XDECREF(res);
Py_DECREF(data);
Py_DECREF(pi_target);
+ if (res != NULL) {
+ (void)xmlparser_append_event(self, self->pi_event_obj, res);
+ Py_DECREF(res);
+ }
}
} else if (self->handle_pi) {
pi_target = PyUnicode_DecodeUTF8(target_in, strlen(target_in),
"strict");
@@ -3765,9 +3705,12 @@ expat_pi_handler(void *op, const XML_Char *target_in,
PyObject *args[2] = {pi_target, data};
res = PyObject_Vectorcall(self->handle_pi, args, 2, NULL);
- Py_XDECREF(res);
Py_DECREF(data);
Py_DECREF(pi_target);
+ if (res != NULL) {
+ (void)xmlparser_append_event(self, self->pi_event_obj, res);
+ Py_DECREF(res);
+ }
}
return;
@@ -3790,6 +3733,10 @@ xmlparser_new(PyTypeObject *type, PyObject *args,
PyObject *kwds)
self->handle_start = self->handle_data = self->handle_end = NULL;
self->handle_comment = self->handle_pi = self->handle_close = NULL;
self->handle_doctype = NULL;
+ self->events_append = NULL;
+ self->start_event_obj = self->end_event_obj = NULL;
+ self->start_ns_event_obj = self->end_ns_event_obj = NULL;
+ self->comment_event_obj = self->pi_event_obj = NULL;
self->elementtree_module = PyType_GetModuleByDef(type,
&elementtreemodule);
assert(self->elementtree_module != NULL);
Py_INCREF(self->elementtree_module);
@@ -3965,6 +3912,13 @@ xmlparser_gc_traverse(PyObject *op, visitproc visit,
void *arg)
Py_VISIT(self->handle_start_ns);
Py_VISIT(self->handle_end_ns);
Py_VISIT(self->handle_doctype);
+ Py_VISIT(self->events_append);
+ Py_VISIT(self->start_event_obj);
+ Py_VISIT(self->end_event_obj);
+ Py_VISIT(self->start_ns_event_obj);
+ Py_VISIT(self->end_ns_event_obj);
+ Py_VISIT(self->comment_event_obj);
+ Py_VISIT(self->pi_event_obj);
Py_VISIT(self->target);
Py_VISIT(self->entity);
@@ -3994,6 +3948,13 @@ xmlparser_gc_clear(PyObject *op)
Py_CLEAR(self->handle_start_ns);
Py_CLEAR(self->handle_end_ns);
Py_CLEAR(self->handle_doctype);
+ Py_CLEAR(self->events_append);
+ Py_CLEAR(self->start_event_obj);
+ Py_CLEAR(self->end_event_obj);
+ Py_CLEAR(self->start_ns_event_obj);
+ Py_CLEAR(self->end_ns_event_obj);
+ Py_CLEAR(self->comment_event_obj);
+ Py_CLEAR(self->pi_event_obj);
Py_CLEAR(self->target);
Py_CLEAR(self->entity);
@@ -4287,40 +4248,28 @@ _elementtree_XMLParser__setevents_impl(XMLParserObject
*self,
{
/* activate element event reporting */
Py_ssize_t i;
- TreeBuilderObject *target;
PyObject *events_append, *events_seq;
if (!_check_xmlparser(self)) {
return NULL;
}
elementtreestate *st = self->state;
- if (!TreeBuilder_CheckExact(st, self->target)) {
- PyErr_SetString(
- PyExc_TypeError,
- "event handling only supported for ElementTree.TreeBuilder "
- "targets"
- );
- return NULL;
- }
-
- target = (TreeBuilderObject*) self->target;
-
events_append = PyObject_GetAttrString(events_queue, "append");
if (events_append == NULL)
return NULL;
- Py_XSETREF(target->events_append, events_append);
+ Py_XSETREF(self->events_append, events_append);
/* clear out existing events */
- Py_CLEAR(target->start_event_obj);
- Py_CLEAR(target->end_event_obj);
- Py_CLEAR(target->start_ns_event_obj);
- Py_CLEAR(target->end_ns_event_obj);
- Py_CLEAR(target->comment_event_obj);
- Py_CLEAR(target->pi_event_obj);
+ Py_CLEAR(self->start_event_obj);
+ Py_CLEAR(self->end_event_obj);
+ Py_CLEAR(self->start_ns_event_obj);
+ Py_CLEAR(self->end_ns_event_obj);
+ Py_CLEAR(self->comment_event_obj);
+ Py_CLEAR(self->pi_event_obj);
if (events_to_report == Py_None) {
/* default is "end" only */
- target->end_event_obj = PyUnicode_FromString("end");
+ self->end_event_obj = PyUnicode_FromString("end");
Py_RETURN_NONE;
}
@@ -4339,32 +4288,53 @@ _elementtree_XMLParser__setevents_impl(XMLParserObject
*self,
Py_DECREF(events_seq);
return NULL;
}
+
+ /* the target must implement the method of the event,
+ except for the namespace events */
+ PyObject *handler = Py_None;
+ if (strcmp(event_name, "start") == 0) {
+ handler = self->handle_start;
+ } else if (strcmp(event_name, "end") == 0) {
+ handler = self->handle_end;
+ } else if (strcmp(event_name, "comment") == 0) {
+ handler = self->handle_comment;
+ } else if (strcmp(event_name, "pi") == 0) {
+ handler = self->handle_pi;
+ }
+ if (handler == NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "the target does not support %R events",
+ event_name_obj);
+ Py_DECREF(events_seq);
+ return NULL;
+ }
+
if (strcmp(event_name, "start") == 0) {
- Py_XSETREF(target->start_event_obj, Py_NewRef(event_name_obj));
+ Py_XSETREF(self->start_event_obj, Py_NewRef(event_name_obj));
} else if (strcmp(event_name, "end") == 0) {
- Py_XSETREF(target->end_event_obj, Py_NewRef(event_name_obj));
+ Py_XSETREF(self->end_event_obj, Py_NewRef(event_name_obj));
} else if (strcmp(event_name, "start-ns") == 0) {
- Py_XSETREF(target->start_ns_event_obj, Py_NewRef(event_name_obj));
+ Py_XSETREF(self->start_ns_event_obj, Py_NewRef(event_name_obj));
EXPAT(st, SetNamespaceDeclHandler)(
self->parser,
(XML_StartNamespaceDeclHandler) expat_start_ns_handler,
(XML_EndNamespaceDeclHandler) expat_end_ns_handler
);
} else if (strcmp(event_name, "end-ns") == 0) {
- Py_XSETREF(target->end_ns_event_obj, Py_NewRef(event_name_obj));
+ Py_XSETREF(self->end_ns_event_obj, Py_NewRef(event_name_obj));
EXPAT(st, SetNamespaceDeclHandler)(
self->parser,
(XML_StartNamespaceDeclHandler) expat_start_ns_handler,
(XML_EndNamespaceDeclHandler) expat_end_ns_handler
);
} else if (strcmp(event_name, "comment") == 0) {
- Py_XSETREF(target->comment_event_obj, Py_NewRef(event_name_obj));
+ Py_XSETREF(self->comment_event_obj, Py_NewRef(event_name_obj));
EXPAT(st, SetCommentHandler)(
self->parser,
(XML_CommentHandler) expat_comment_handler
);
} else if (strcmp(event_name, "pi") == 0) {
- Py_XSETREF(target->pi_event_obj, Py_NewRef(event_name_obj));
+ Py_XSETREF(self->pi_event_obj, Py_NewRef(event_name_obj));
EXPAT(st, SetProcessingInstructionHandler)(
self->parser,
(XML_ProcessingInstructionHandler) expat_pi_handler
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]