diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst index ec272a076c902b..923ad1060c6dd1 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 3262acd87d6d49..f99cb93b1a1c1d 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 f9ff8c4c3541ed..ba734103fcd6bb 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 bed8c27df5a384..a49a6a4cbcd739 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 00000000000000..cd769c804aab6c --- /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 18bbbb618c2b18..7dbca929249334 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