Skip to main content

XML parser with streaming iterator interface

Project description

xml_iterator

Streaming XML for Python. Primary goal: defeat the infinite depth attack on large dumps (FIRDS-like shape) where useful records sit under outer elements that stay open until late/EOF — a tree/DOM consumer either waits for those outers or holds the whole open tree.

<root>                              ← opens near start
  <Payload>
    <RefData>                       ← open for almost the whole file
      <FinInstrm> ... </FinInstrm>  ← record 1 complete (outers still open)
      <FinInstrm> ... </FinInstrm>  ← record 2
      ...
    </RefData>                      ← closes near EOF
  </Payload>
</root>

Protection is streaming under open wrappers plus user discard / early stop — not max_depth on a full-document dict. Process each record on its end event, drop it, and break after K records. Memory stays bounded only if finished work is discarded.

Threat model / landscape: backlog/docs/streaming-memory-model-and-landscape.md.

Benchmarks

Release builds only (make develop / make build). Debug extensions are ~9× slower.

Numbers below: 2026-07-17, machine bleepblop. Raw JSON: benchmark_data/benchmark_results.json. Narrative / before–after: PERF_2026-07-17.md.

xml_iterator.xml_to_dict vs xmltodict.parse

Same output shape (attributes included; namespace prefixes stripped). Full-file dict rebuild — for modest documents / parity, not multi-GB FIRDS-as-one-tree.

Synthetic (attrs, identical results):

Elements Size xml_iterator.xml_to_dict xmltodict.parse Speedup
500 0.2 MB 0.003s 0.019s 7.0×
2,000 0.7 MB 0.011s 0.062s 5.6×
5,000 1.8 MB 0.030s 0.169s 5.7×

SwissProt (110 MB): xml_iterator.xml_to_dict 3.0s vs xmltodict.parse 13.2s (4.5×).

ESMA FIRDS (~441 MB full dict): xml_iterator.xml_to_dict 72s vs xmltodict.parse 45s (slower — full tree still loses at this scale; use streaming).

Streaming / other APIs (SwissProt 110 MB)

Approach Time
xml_iterator.iter_xml full drain (8.0M events) 2.6s
xml_iterator.iter_xml(..., attributes=True) (10.2M events) 3.6s
stdlib ET.iterparse (start+end, elem.clear()) 6.6s
xml_iterator.get_edge_counts (aggregation in Rust) 1.3s

Reproduce: make benchmark (synthetic), make benchmark-real (SwissProt), make benchmark-firds / make benchmark-all. Needs uv pip install -e ".[bench]".

Usage

from xml_iterator.xml_iterator import iter_xml
from xml_iterator.core import xml_to_dict  # xml_iterator.xml_to_dict

# Streaming: records under open wrappers (FIRDS-like)
records = 0
for count, event, value in iter_xml('file.xml'):
    if event == 'end' and value == 'FinInstrm':
        records += 1
        # handle; discard — do not accumulate under open parents
        if records >= 1000:
            break

# Full document dict — modest files / xmltodict parity only
data = xml_to_dict('small.xml')

Also: get_edge_counts(path), opt-in attrs via iter_xml(path, attributes=True). Example: examples/firds_shape_stream.py. Sample event dump: examples/simple.xml + examples/example_xml_iter.py.

Limits: file paths only (no pipes); namespace prefixes stripped; full-file xml_iterator.xml_to_dict is not the multi-GB path.

When to use something else: stdlib ET.iterparse + clear(), bigxml, or xmltodict item_depth callbacks — see landscape doc above.

Develop

make develop          # release extension (default)
make develop-debug    # debug build (~9× slower)
pytest                # after: uv pip install -e ".[test]"

Changelog: CHANGELOG.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

xml_iterator-0.2.0-cp37-abi3-win_amd64.whl (331.1 kB view details)

Uploaded CPython 3.7+Windows x86-64

xml_iterator-0.2.0-cp37-abi3-manylinux_2_34_x86_64.whl (476.7 kB view details)

Uploaded CPython 3.7+manylinux: glibc 2.34+ x86-64

File details

Details for the file xml_iterator-0.2.0-cp37-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for xml_iterator-0.2.0-cp37-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 2418ccdd711d23c4ca2ab82ed708f5bf1956654594c5a8ef6f2e2fbac37e35f8
MD5 87c8549dd9a4f5480bf09cfa26152b0b
BLAKE2b-256 da54583ba863cc7e358fc00357d88ec312f2857217b8ef9671a48c442df497d8

See more details on using hashes here.

File details

Details for the file xml_iterator-0.2.0-cp37-abi3-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for xml_iterator-0.2.0-cp37-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 fbaf13412d71e146da268284d8d6bcc4c397c675f799af1c98392c9108c719d5
MD5 d37490a6c91fd4dee8e01c23bd6d523d
BLAKE2b-256 9a8156c698e68c066bf303f4bf89eeae1220b3ad6aedfb028fd0226356dbc942

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page