purexml
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/.
Why 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.
Install & migrate
Until purexml is published to PyPI, depend on it via a git or path reference. Then 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 Element
Defaults 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
What it covers
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).
Opt-in defense-in-depth
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).
Status
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). - Not yet published to PyPI, and the
purexmlname is not yet claimed — distribution is a separate, deliberate step. Depend on it via git/path until then. - License: MIT.
Documentation
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 (binds at 1.0)CHANGELOG.md— release notes ·HISTORY.md— full per-release recordSTACK.md— language, runtime, dependencies ·CONVENTIONS.md— project conventions
License
MIT — purexml is give-it-away, zero-dependency infrastructure: pure open
source, maximum reuse. Publishing to PyPI and claiming the purexml name remain a
separate, deliberate call.
Metadata
Release files for purexml 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| purexml-1.0.0.tar.gz | 69.7 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| purexml-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 105.7 kB
Release files / purexml-1.0.0.tar.gz
| Download URL | purexml-1.0.0.tar.gz |
|---|---|
| Size | 69.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bfd03bd7107dafe0868436246497b6cf5ea38180db0a65027f3fd04504afa254
|
|
BLAKE2b-256 checksum How to use checksums |
1e252fec55ef7bbeb2c92ee362d9b2309ddffd2dfa1887ab9d473aab065e6495
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 8, 2026.
Transparency logRelease files / purexml-1.0.0-py3-none-any.whl
| Download URL | purexml-1.0.0-py3-none-any.whl |
|---|---|
| Size | 36.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
19f5cf53b1e9e98ffb6ed4ad7c352933c9a370141778d544ea950670c25e19b2
|
|
BLAKE2b-256 checksum How to use checksums |
7175506c1ade0ac0f4cecbf4d716f62adf78e592788679d88294bfdb62e9829b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 8, 2026.
Transparency logSecurity for purexml 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for purexml-1.0.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 8, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.