Safely parse untrusted XML using only the Python standard library.
purexml is a zero-dependency, drop-in replacement for defusedxml.
It hardens Python's standard-library XML parsers against the known attack classes —
entity-expansion bombs, external-entity resolution (XXE), and external-DTD retrieval —
and hands back the same standard xml.etree / minidom / SAX objects your code already
expects. Migrating is a literal find-and-replace: s/defusedxml/purexml/.
- Zero runtime dependencies. Pure standard library — drop a third-party dependency with no loss of protection (same parses succeed, same attacks blocked).
- A real drop-in. Mirrors
defusedxml's API and defaults across the surface the ecosystem actually imports, validated oracle-gated againstdefusedxmlitself. - Maintained.
defusedxmlhas been frozen since 2021 — the XML threat landscape hasn't. purexml tracks the moving libexpat / CPython mitigations and can report what your runtime is actually protected against. - Opt-in hardening. Structural-DoS caps and a posture report that
defusedxmllacks — off by default, so the drop-in promise is never broken.
Deciding whether to switch? See why switch from
defusedxml— the full case, and the honest boundaries (when not to).
pip install purexmlThen the only change to your code is the import path:
# before: from defusedxml.ElementTree import fromstring
from purexml.ElementTree import fromstring
root = fromstring(untrusted_xml) # raises on bomb / XXE / external DTD; returns a standard ElementDefaults match defusedxml exactly (forbid_entities=True, forbid_external=True,
forbid_dtd=False), so nothing else changes.
Runnable examples for every surface:
examples/· Module-by-module migration guide:docs/MIGRATING.md
Drop-in replacements for the defusedxml modules the ecosystem imports:
| purexml module | replaces | surface |
|---|---|---|
purexml.ElementTree |
defusedxml.ElementTree |
fromstring, parse, iterparse, XML, XMLParser, tostring, forbid_* |
purexml.minidom |
defusedxml.minidom |
parse, parseString |
purexml.sax / .expatreader |
defusedxml.sax |
make_parser, parse, parseString |
purexml.xmlrpc |
defusedxml.xmlrpc |
monkey_patch, unmonkey_patch |
purexml.common |
defusedxml.common |
exception catch-site aliases |
defusedxml.pulldom is deferred (low measured demand); defusedxml.lxml is excluded by
the zero-dependency contract (it wraps the third-party lxml).
Bounded protections defusedxml never had — default-off, so the strict mirror is
unchanged until you ask:
import purexml
from purexml import fromstring, RECOMMENDED_LIMITS
# Bound structural DoS (deep nesting / attribute floods / giant documents):
root = fromstring(untrusted, limits=RECOMMENDED_LIMITS) # raises LimitExceeded past the caps
# See what THIS runtime is actually protected against (read-only; no parse):
print(purexml.security_report())There's also a posture CLI — python -m purexml (--json for machine output, or
--check --min-expat X.Y.Z as an opt-in CI gate).
v1.0.0 — the public contract is frozen and binding. The defusedxml-mirror surface
won't move under you without a 2.0 (see PUBLIC_CONTRACT.md).
- Runs on CPython 3.10–3.13, zero runtime dependencies.
- Correctness is oracle-gated against
defusedxmlevery release — C14N + event-stream equivalence over a real corpus, an adversarial attack battery, and differential fuzzing (seedocs/EQUIVALENCE.md). - The contract freeze was ratified with the anchor consumer (file-observer) — see
docs/v1.0.0_RFC_Specification.md. The opt-in defense-in-depth (Limits,security_report()) stays provisional (it tracks the moving libexpat threat landscape). - On PyPI:
pip install purexml— zero runtime dependencies. - License: MIT.
docs/vs-defusedxml.md— why switch fromdefusedxml(and when not to)examples/— runnable, copy-paste examples for every surfacedocs/MIGRATING.md— thes/defusedxml/purexml/migration guideCOMPATIBILITY.md— drop-in compatibility contract + exception edge casesLIMITATIONS.md— what purexml deliberately does not doSECURITY.md— security policy and how to report issuesPUBLIC_CONTRACT.md— consumer stability commitments (binding as of 1.0)CHANGELOG.md— release notes ·HISTORY.md— full per-release recordSTACK.md— language, runtime, dependencies ·CONVENTIONS.md— project conventions
MIT — purexml is give-it-away, zero-dependency infrastructure: pure open source, maximum reuse.
Built by russalo — more at blog.russalo.com.