Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 77 additions & 21 deletions Doc/library/xml.dom.pulldom.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@

**Source code:** :source:`Lib/xml/dom/pulldom.py`

.. The module was written by Paul Prescod and added in Python 2.0.
It is not based on any specification: the implementation is the only
reference. The Java Streaming API for XML (StAX, JSR 173) is based on
it, among other pull parsers.

--------------

The :mod:`xml.dom.pulldom` module provides a "pull parser" which can also be
Expand Down Expand Up @@ -50,19 +55,49 @@ Example::
doc.expandNode(node)
print(node.toxml())

``event`` is a constant and can be one of:
``event`` is one of the following constants,
and ``node`` is the node which the event is about.
The nodes implement the :mod:`xml.dom` interfaces;
they are created by the DOM implementation given to :class:`PullDOM`,
which is :mod:`xml.dom.minidom` by default.


.. data:: START_DOCUMENT
END_DOCUMENT

The start and the end of the document.
*node* is the :class:`~xml.dom.Document`.


.. data:: START_ELEMENT
END_ELEMENT

The start tag and the end tag of an element.
*node* is the :class:`~xml.dom.Element`.


.. data:: CHARACTERS

Character data.
*node* is the :class:`~xml.dom.Text` node.


* :data:`START_ELEMENT`
* :data:`END_ELEMENT`
* :data:`COMMENT`
* :data:`START_DOCUMENT`
* :data:`END_DOCUMENT`
* :data:`CHARACTERS`
* :data:`PROCESSING_INSTRUCTION`
* :data:`IGNORABLE_WHITESPACE`
.. data:: IGNORABLE_WHITESPACE

``node`` is an object of type :class:`xml.dom.minidom.Document`,
:class:`xml.dom.minidom.Element` or :class:`xml.dom.minidom.Text`.
White space in element content, as declared in the DTD.
*node* is the :class:`~xml.dom.Text` node.


.. data:: COMMENT

A comment.
*node* is the :class:`~xml.dom.Comment` node.


.. data:: PROCESSING_INSTRUCTION

A processing instruction.
*node* is the :class:`~xml.dom.ProcessingInstruction` node.

Since the document is treated as a "flat" stream of events, the document "tree"
is implicitly traversed and the desired elements are found regardless of their
Expand All @@ -76,12 +111,19 @@ and switch to DOM-related processing.

.. class:: PullDOM(documentFactory=None)

Subclass of :class:`xml.sax.handler.ContentHandler`.
Subclass of :class:`xml.sax.handler.ContentHandler` which turns SAX events
into the events of the pull parser.
The nodes are created, but they are not added to the tree,
unless :meth:`~DOMEventStream.expandNode` is called.
*documentFactory*, if given, is a DOM implementation used to create
the document; by default the implementation of :mod:`xml.dom.minidom`
is used.


.. class:: SAX2DOM(documentFactory=None)

Subclass of :class:`xml.sax.handler.ContentHandler`.
Subclass of :class:`PullDOM` which also adds every created node
to the tree, so that the complete document is built.


.. function:: parse(stream_or_string, parser=None, bufsize=None)
Expand All @@ -97,7 +139,9 @@ If you have XML in a string, you can use the :func:`parseString` function instea

.. function:: parseString(string, parser=None)

Return a :class:`DOMEventStream` that represents the (Unicode) *string*.
Return a :class:`DOMEventStream` that represents the *string*.
*string* must be a :class:`str` instance;
to parse bytes, pass a binary file object to :func:`parse`.

.. data:: default_bufsize

Expand All @@ -113,18 +157,21 @@ DOMEventStream Objects

.. class:: DOMEventStream(stream, parser, bufsize)

Produce the events for the data read from the file object *stream*
by the :class:`~xml.sax.xmlreader.XMLReader` *parser*.
The data is read by *bufsize* bytes, or characters for a text stream,
at a time.

.. versionchanged:: 3.11
Support for :meth:`~object.__getitem__` method has been removed.

.. method:: getEvent()

Return a tuple containing *event* and the current *node* as
:class:`xml.dom.minidom.Document` if event equals :data:`START_DOCUMENT`,
:class:`xml.dom.minidom.Element` if event equals :data:`START_ELEMENT` or
:data:`END_ELEMENT` or :class:`xml.dom.minidom.Text` if event equals
:data:`CHARACTERS`.
Return the next ``(event, node)`` tuple,
or ``None`` at the end of the document.
See above for the events and the corresponding nodes.
The current node does not contain information about its children, unless
:func:`expandNode` is called.
:meth:`expandNode` is called.

.. method:: expandNode(node)

Expand All @@ -142,4 +189,13 @@ DOMEventStream Objects
# Following statement prints node with all its children '<p>Some text <div>and more</div></p>'
print(node.toxml())

.. method:: DOMEventStream.reset()
.. method:: reset()

Discard the events which are not read yet
and prepare the object for parsing a new document.


.. method:: clear()

Release the parser and the document.
The stream is not closed, and the object can no longer be used.
8 changes: 0 additions & 8 deletions Doc/library/xml.dom.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,6 @@ The Document Object Model is being defined by the W3C in stages, or "levels" in
their terminology. The Python mapping of the API is substantially based on the
DOM Level 2 recommendation.

.. What if your needs are somewhere between SAX and the DOM? Perhaps
you cannot afford to load the entire tree in memory but you find the
SAX model somewhat cumbersome and low-level. There is also a module
called xml.dom.pulldom that allows you to build trees of only the
parts of a document that you need structured access to. It also has
features that allow you to find your way around the DOM.
See http://www.prescod.net/python/pulldom

DOM applications typically start by parsing some XML into a DOM. How this is
accomplished is not covered at all by DOM Level 1, and Level 2 provides only
limited improvements: There is a :class:`DOMImplementation` object class which
Expand Down
1 change: 0 additions & 1 deletion Doc/tools/.nitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ Doc/library/urllib.parse.rst
Doc/library/urllib.request.rst
Doc/library/wsgiref.rst
Doc/library/xml.dom.minidom.rst
Doc/library/xml.dom.pulldom.rst
Doc/library/xml.sax.reader.rst
Doc/library/xml.sax.rst
Doc/library/xmlrpc.client.rst
Expand Down
16 changes: 15 additions & 1 deletion Lib/xml/dom/pulldom.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Support for building partial DOM trees from SAX events."""

import xml.sax
import xml.sax.handler

Expand All @@ -11,6 +13,10 @@
CHARACTERS = "CHARACTERS"

class PullDOM(xml.sax.ContentHandler):
"""Content handler which turns SAX events into pull parser events.

The nodes are created, but they are not added to the tree."""

_locator = None
document = None

Expand Down Expand Up @@ -202,6 +208,8 @@ def fatalError(self, exception):
raise exception

class DOMEventStream:
"""Stream of the pull parser events."""

def __init__(self, stream, parser, bufsize):
self.stream = stream
self.parser = parser
Expand All @@ -211,6 +219,7 @@ def __init__(self, stream, parser, bufsize):
self.reset()

def reset(self):
"""Discard unread events and prepare for parsing a new document."""
self.pulldom = PullDOM()
# This content handler relies on namespace support
self.parser.setFeature(xml.sax.handler.feature_namespaces, 1)
Expand All @@ -226,6 +235,7 @@ def __iter__(self):
return self

def expandNode(self, node):
"""Expand all children of the node into the node."""
event = self.getEvent()
parents = [node]
while event:
Expand All @@ -241,6 +251,7 @@ def expandNode(self, node):
event = self.getEvent()

def getEvent(self):
"""Return the next (event, node) tuple, or None at the end."""
# use IncrementalParser interface, so we get the desired
# pull effect
if not self.pulldom.firstEvent[1]:
Expand Down Expand Up @@ -274,13 +285,14 @@ def _emit(self):
return rc

def clear(self):
"""clear(): Explicitly release parsing objects"""
"""Release the parser and the document."""
self.pulldom.clear()
del self.pulldom
self.parser = None
self.stream = None

class SAX2DOM(PullDOM):
"""PullDOM which also adds every created node to the tree."""

def startElementNS(self, name, tagName , attrs):
PullDOM.startElementNS(self, name, tagName, attrs)
Expand Down Expand Up @@ -316,6 +328,7 @@ def characters(self, chars):
default_bufsize = (2 ** 14) - 20

def parse(stream_or_string, parser=None, bufsize=None):
"""Return a DOMEventStream for the given file name or file object."""
if bufsize is None:
bufsize = default_bufsize
if isinstance(stream_or_string, str):
Expand All @@ -327,6 +340,7 @@ def parse(stream_or_string, parser=None, bufsize=None):
return DOMEventStream(stream, parser, bufsize)

def parseString(string, parser=None):
"""Return a DOMEventStream for the given string."""
from io import StringIO

bufsize = len(string)
Expand Down
Loading