diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst
index ec272a076c902bf..923ad1060c6dd1f 100644
--- a/Doc/library/xml.etree.elementtree.rst
+++ b/Doc/library/xml.etree.elementtree.rst
@@ -611,10 +611,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"``
@@ -622,11 +623,16 @@ 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;
+ 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.
+ 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.
@@ -658,6 +664,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)
@@ -1491,7 +1500,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
@@ -1502,6 +1511,18 @@ 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 ``"start-ns"`` and ``"end-ns"`` events are reported as before
+ if the target does not implement :meth:`!start_ns` and :meth:`!end_ns`.
+
+ .. versionchanged:: next
+ Added the *target* parameter.
+
.. method:: feed(data)
Feed the given data to the parser. *data* is a string
@@ -1534,9 +1555,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 3262acd87d6d49f..f99cb93b1a1c1dd 100644
--- a/Doc/whatsnew/3.16.rst
+++ b/Doc/whatsnew/3.16.rst
@@ -660,6 +660,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 f9ff8c4c3541eda..ba734103fcd6bb0 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"\n"
@@ -2067,6 +2104,58 @@ def __next__(self):
self._feed(parser, "bar")
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, "")
+ 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, "")
+ self.assert_event_tuples(parser, [
+ ('comment', ('comment', ' text ')),
+ ('pi', ('pi', 'pitarget')),
+ ])
+
+ 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, "")
+ 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, "")
+ 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 bed8c27df5a3845..a49a6a4cbcd7391 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",)
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 18bbbb618c2b18b..7dbca9292493341 100644
--- a/Modules/_elementtree.c
+++ b/Modules/_elementtree.c
@@ -2409,14 +2409,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;
@@ -2450,10 +2442,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);
}
@@ -2518,13 +2506,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);
@@ -2541,13 +2522,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);
@@ -2717,24 +2691,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 */
@@ -2800,9 +2756,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:
@@ -2873,11 +2826,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;
}
@@ -2907,11 +2855,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:
@@ -2950,11 +2893,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:
@@ -2962,39 +2900,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) */
@@ -3125,6 +3030,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;
@@ -3314,6 +3228,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)
@@ -3389,7 +3323,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
@@ -3446,7 +3383,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
@@ -3466,42 +3406,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
@@ -3517,15 +3449,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;
@@ -3533,8 +3457,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
@@ -3557,16 +3488,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);
+ }
}
}
@@ -3646,7 +3583,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;
@@ -3654,9 +3591,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");
@@ -3668,9 +3608,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;
@@ -3693,6 +3636,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);
@@ -3861,6 +3808,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);
@@ -3890,6 +3844,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);
@@ -4181,40 +4142,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;
}
@@ -4238,31 +4187,31 @@ _elementtree_XMLParser__setevents_impl(XMLParserObject *self,
}
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