Python xmlreader: Control SAX Parsers

Published on: August 23, 2026
Reading time: 4 minutes
Close-up of a saxophone and sheet music stand, perfect for jazz lovers and musicians.

The xml.sax.xmlreader module defines the interfaces used by SAX parsers in Python. It describes how a parser receives input sources, sends events to handlers, configures features and properties, reports line and column positions, and supports incremental parsing.

Most applications create a parser with xml.sax.make_parser(), but understanding XMLReader, InputSource, Locator, and attribute objects provides better control over security, encoding, streams, and parser extensions.

The role of XMLReader

XMLReader is the base interface implemented by SAX parser drivers. A driver exposes a create_parser() function, and make_parser() uses it to create a reader. The reader processes XML and invokes registered handlers.

from xml.sax import make_parser
from xml.sax.handler import ContentHandler

class MyHandler(ContentHandler):
    def startElement(self, name, attrs):
        print("start:", name)

parser = make_parser()
parser.setContentHandler(MyHandler())
parser.parse("data.xml")

When parse() returns, the document has been fully processed. Use the incremental interface when chunks arrive over time or blocking behavior is not acceptable.

Configurable handlers

A reader can receive a ContentHandler, DTDHandler, EntityResolver, and ErrorHandler. Without a content handler, content events are discarded. Without an error handler, errors normally become exceptions and warnings may be printed.

The guide to Python xml.sax explains the main callbacks. Python saxutils provides generators and filters for the same event flow.

Control origins with InputSource

InputSource stores a public identifier, system identifier, encoding, byte stream, and character stream. It lets an application provide a source that is already open instead of allowing the reader to open a path or URL automatically.

from xml.sax import make_parser
from xml.sax.xmlreader import InputSource

source = InputSource()
source.setSystemId("local-import")
source.setEncoding("utf-8")
source.setByteStream(open("data.xml", "rb"))

parser = make_parser()
parser.setContentHandler(MyHandler())
parser.parse(source)

When a character stream exists, the parser ignores the byte stream and the encoding value on the input source. A byte stream takes precedence over opening the system identifier.

Byte streams and character streams

A byte stream allows the parser to inspect the XML encoding declaration unless an encoding is explicitly supplied. A character stream is already decoded, so the application becomes responsible for correct decoding.

Do not decode arbitrary XML bytes with errors="ignore" before parsing. Removed characters may change document meaning. The guide to Python codecs covers encodings and error policies.

Prevent automatic URL access

A string passed to parse() may represent a filename, path-like object, or system identifier. With external data, open the source yourself, enforce size and protocol restrictions, and provide a stream. This prevents a user-controlled reference from becoming an unrestricted network or file access.

Combine this with an EntityResolver that rejects external entities. XML input should not freely choose local files, remote hosts, or protocols.

Parser features

getFeature() and setFeature() read or change boolean SAX options. Common features include namespace processing, namespace prefixes, validation, and external entities. A parser may not recognize or support every feature.

from xml.sax import make_parser
from xml.sax.handler import feature_namespaces, feature_external_ges

parser = make_parser()
parser.setFeature(feature_namespaces, True)

try:
    parser.setFeature(feature_external_ges, False)
except Exception as exc:
    raise RuntimeError("Required XML protection is unavailable") from exc

Do not silently ignore a failed security configuration. If an essential protection cannot be guaranteed, reject the operation.

Parser properties

Properties carry more complex objects, including lexical handlers. getProperty() and setProperty() may raise SAXNotRecognizedException or SAXNotSupportedException. Configure properties before parsing begins.

Incremental parsing

IncrementalParser accepts chunks through feed(). After the final chunk, close() checks well-formedness conditions that can only be verified at the end and sends remaining events. Call reset() after close() before reusing the parser.

from xml.sax import make_parser

parser = make_parser()
parser.setContentHandler(MyHandler())

with open("large.xml", "rb") as file:
    while chunk := file.read(64 * 1024):
        parser.feed(chunk)

parser.close()

Do not mix parse() and feed() during the same operation. Do not call reset() while a document is being processed.

Limits still matter

Reading in chunks does not limit total size. Count input bytes, events, depth, accumulated text, and runtime. Stop before policy limits are exceeded.

MAX_BYTES = 20 * 1024 * 1024
received = 0

while chunk := origin.read(64 * 1024):
    received += len(chunk)
    if received > MAX_BYTES:
        raise ValueError("XML exceeds the size limit")
    parser.feed(chunk)

parser.close()

Locator for line and column

A Locator associates an event with the current document position. The parser provides it through setDocumentLocator(). Values are valid only during handler callbacks.

from xml.sax.handler import ContentHandler

class PositionedHandler(ContentHandler):
    def setDocumentLocator(self, locator):
        self.locator = locator

    def startElement(self, name, attrs):
        line = self.locator.getLineNumber()
        column = self.locator.getColumnNumber()
        print(name, line, column)

Copy location values during the event. Keeping the locator and consulting it later will report a different position.

AttributesImpl

AttributesImpl represents attributes passed to startElement(). It supports part of the mapping protocol plus methods such as getLength(), getNames(), getType(), and getValue().

A parser may reuse the object. Copy attributes when they must survive the callback.

def startElement(self, name, attrs):
    copied = dict(attrs.items())
    self.records.append((name, copied))

Namespace-aware attributes

With namespaces enabled, startElementNS() receives element names as (namespaceURI, localname) tuples. AttributesNSImpl works with the same form and can convert between qualified names and namespace pairs.

Do not rely only on textual prefixes. Prefixes may change while the namespace URI remains the same.

Error handling

An error handler receives warnings, recoverable errors, and fatal errors. Data pipelines should usually stop on structural errors and return a controlled diagnostic. Avoid logging the complete XML document or credentials.

A blocking entity resolver

from io import StringIO
from xml.sax.handler import EntityResolver
from xml.sax.xmlreader import InputSource

class BlockExternal(EntityResolver):
    def resolveEntity(self, publicId, systemId):
        source = InputSource()
        source.setCharacterStream(StringIO(""))
        return source

parser.setEntityResolver(BlockExternal())

Depending on the application, raising an exception may be safer than returning an empty source. The important rule is to avoid automatic external access.

Test strings, Path values, byte streams, character streams, declared and overridden encodings, namespaces, copied attributes, incremental input, truncated documents, external entities, unsupported features, byte limits, and locator positions.

Common mistakes

Frequent mistakes include trusting external system IDs, decoding character streams incorrectly, forgetting close(), reusing a parser without reset(), storing attribute objects without copying, enabling external entities, and assuming all parsers support the same features.

Conclusion

xml.sax.xmlreader defines the infrastructure behind SAX parsers: readers, input sources, handlers, features, properties, locators, and attribute interfaces. Understanding it gives applications stronger control over input, encoding, streaming, and security.

Open external sources with limits, keep external entities disabled, and configure the reader before parsing. Consult the official xmlreader documentation and Python’s XML security guidance.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    A developer typing code on a laptop with a Python book beside in an office.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python saxutils: XML Utility Functions

    Learn Python saxutils to escape XML, quote attributes, generate documents, build SAX filters, and avoid context mistakes.

    Ler mais

    Tempo de leitura: 5 minutos
    23/08/2026
    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python pulldom: Partial DOM for XML

    Learn Python pulldom to process XML events, expand only selected subtrees, and reduce memory use with safer limits.

    Ler mais

    Tempo de leitura: 5 minutos
    23/08/2026
    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python xml.sax: Process XML Events

    Learn Python xml.sax to process XML as events with low memory use, namespaces, handlers, limits, and secure entity handling.

    Ler mais

    Tempo de leitura: 4 minutos
    22/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python minidom: Manipulate XML with DOM

    Learn Python xml.dom.minidom to read, navigate, create, and serialize XML with DOM nodes, namespaces, memory control, and security.

    Ler mais

    Tempo de leitura: 5 minutos
    22/08/2026
    A solitary tree with sunlight filtering through the leaves on a grassy hill under a blue sky.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    ElementTree: Read and Modify XML in Python

    Learn Python ElementTree to read, search, modify, and generate XML with namespaces, incremental parsing, limits, and security.

    Ler mais

    Tempo de leitura: 5 minutos
    22/08/2026
    Close-up of hands typing on a laptop keyboard, Python book in sight, coding in progress.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python poplib: Read Email with POP3

    Learn Python poplib to access POP3 with TLS, list and download messages, use UIDL, enforce limits, and avoid accidental deletion.

    Ler mais

    Tempo de leitura: 5 minutos
    22/08/2026