safexml 🛡️
High-performance, secure, modern XML parser and drop-in defusedxml replacement built in Rust.
Why safexml?
Python's standard library documentation explicitly warns:
"The XML modules in the Python standard library are not secure against maliciously constructed data. Use
defusedxml."
However, defusedxml was created in 2013 and has been stagnant (last released in March 2021), holding back over 94,000+ dependent repositories. It relies on Python-level monkeypatching of legacy CPython pyexpat handlers, retains Python GIL locks during parsing, and lacks modern Python 3.12+ features, typing, and standard ElementTree functions.
safexml is engineered from scratch in Rust using quick-xml and PyO3 to solve these problems:
- Drop-in Compatibility: 100% compatible with
defusedxml.ElementTreeand standardxml.etree.ElementTree. - True GIL Release: Detaches the Python GIL during XML tokenization and validation via Rust threads (
py.detach). - Blazing Fast: Parses, validates, and builds native elements orders of magnitude faster than pure Python / expat wrappers.
- Modern Python Only: Requires Python >= 3.12 exclusively (zero legacy Python 2 or 3.7–3.11 baggage).
- Strict Security by Default:
- Defeats Billion Laughs (exponential entity expansion).
- Defeats Quadratic Blowup entity attacks.
- Defeats XML External Entity (XXE) attacks (
SYSTEMandPUBLICURIs). - Defeats Cyclic Entity references.
- Defeats DTD Injections and parameter entities.
- Enforces configurable Nesting Depth Limits (default 1,000) to protect against C call stack exhaustion.
- Enforces configurable Text and Entity Size Limits to eliminate memory exhaustion attacks.
safexml vs polyxml
| Feature | polyxml |
safexml |
|---|---|---|
| Model | Data-Binding / Serde (schema-driven) | Untyped DOM Tree |
| Input / Output | XML $\leftrightarrow$ Python Typed Models (Dataclasses, Pydantic) | XML $\leftrightarrow$ ElementTree.Element |
| Use Case | APIs, microservices, typed business logic | Drop-in for ElementTree & defusedxml (SAML, Office docs, RSS, SVG) |
Upstream defusedxml Issues Resolved
safexml directly fixes open issues and pain points reported against tiran/defusedxml:
- #112, #84: Stagnant project maintenance. Active development under the
polyxmlorganization. - #105: Exception formatting crashed when printed by
richtracebacks due to missingargs. Fixed with proper standard exception hierarchy. - #104: Complete lack of PEP 561 typing.
safexmlships withpy.typedand full type annotations. - #87: Missing
xml.etree.ElementTree.indent(). Full support provided. - #80: Missing
Elementclass export. Re-exported directly. - #79: Missing
register_namespace()helper. Fully implemented. - #78:
fromstring()rejectedparser=keyword argument. Standard signature parity supported. - #76, #77, #88:
defuse_stdlib()monkeypatching brokeopenpyxlandxmlschema. Safe, non-destructive defuser implemented.
Installation
pip install safexml
Quickstart
Drop-in Replacement for defusedxml.ElementTree
import safexml.ElementTree as ET
# Parsing strings
root = ET.fromstring("<root><item id='1'>Safe XML</item></root>")
print(root.tag) # "root"
print(root[0].text) # "Safe XML"
# Pretty-printing (resolves tiran/defusedxml#87)
ET.indent(root)
print(ET.tostring(root, encoding="unicode"))
Catching Security Exceptions
import safexml.ElementTree as ET
from safexml.common import EntitiesForbidden, DTDForbidden, ExternalReferenceForbidden
# 1. Billion Laughs / Entity attack rejection
try:
ET.fromstring("""<!DOCTYPE bomb [
<!ENTITY a "1234567890">
<!ENTITY b "&a;&a;&a;&a;&a;&a;&a;&a;">
]><bomb>&a;</bomb>""")
except EntitiesForbidden as exc:
print(f"Blocked entity expansion: {exc.name}")
# 2. External DTD / XXE rejection
try:
ET.fromstring("""<!DOCTYPE root SYSTEM "http://attacker.com/evil.dtd"><root/>""")
except ExternalReferenceForbidden as exc:
print(f"Blocked external reference: {exc.sysid}")
# 3. Forbid any DTD
try:
ET.fromstring("<!DOCTYPE root><root/>", forbid_dtd=True)
except DTDForbidden as exc:
print(f"Blocked DTD: {exc.name}")
License
MIT License. Developed under the PolyXML organization.
Metadata
Release files for safexml 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 | |
|---|---|---|---|
| safexml-1.0.0.tar.gz | 27.7 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| safexml-1.0.0-cp312-abi3-win_amd64.whl | CPython 3.12 | abi3 | Windows x86-64 | Details |
| safexml-1.0.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.12 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| safexml-1.0.0-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.12 | abi3 | Linux glibc 2.17+ ARM64 | Details |
| safexml-1.0.0-cp312-abi3-macosx_11_0_arm64.whl | CPython 3.12 | abi3 | macOS 11.0+ ARM64 | Details |
| safexml-1.0.0-cp312-abi3-macosx_10_12_x86_64.whl | CPython 3.12 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 1.1 MB
Release files / safexml-1.0.0.tar.gz
| Download URL | safexml-1.0.0.tar.gz |
|---|---|
| Size | 27.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d635300a0a753b0190a7c539387c49eed4112fd76e952c9db7f3a8ff6934a4aa
|
|
BLAKE2b-256 checksum How to use checksums |
71dccaba6d7becb0c45a246606df211249e64604b2b41db3dc6f5dafa53daf14
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 11, 2026.
Transparency logRelease files / safexml-1.0.0-cp312-abi3-win_amd64.whl
| Download URL | safexml-1.0.0-cp312-abi3-win_amd64.whl |
|---|---|
| Size | 156.4 kB |
| Tags | CPython 3.12 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
bb3bc3d8187241a05bc79df9a82d859784dbea45029eab4d83eaf190f2fb79cf
|
|
BLAKE2b-256 checksum How to use checksums |
48088160d25a4cb9f9ffba7f2d75740e4ee25c0fdd365a0c6949854e32b660d1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 11, 2026.
Transparency logRelease files / safexml-1.0.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | safexml-1.0.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 239.3 kB |
| Tags | CPython 3.12 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
9386761aa24e7a02a7c543b2640f10baf213eebc279a1e75cd0705a01c39b040
|
|
BLAKE2b-256 checksum How to use checksums |
83ef0196d96dbcb2c94bae38c45f413794a927aaad1846ea735d851cb61fc92c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 11, 2026.
Transparency logRelease files / safexml-1.0.0-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | safexml-1.0.0-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 233.1 kB |
| Tags | CPython 3.12 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
946e9dcd2f9efaded7ceb600295cbc6a9ebd3c2e90d242b23179105786318e2a
|
|
BLAKE2b-256 checksum How to use checksums |
3ef77b42ba77efbc0533e8e43c14e5d7611f8f5d0f660a6d2deecd19c1ec7f24
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 11, 2026.
Transparency logRelease files / safexml-1.0.0-cp312-abi3-macosx_11_0_arm64.whl
| Download URL | safexml-1.0.0-cp312-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 225.8 kB |
| Tags | CPython 3.12 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
cc2ddb78b32c5d18d295e87ad1385a188df32f8ebc60ddc4a0fbba781140615f
|
|
BLAKE2b-256 checksum How to use checksums |
e272256ec8192b63fed2919f6e866118c448b143c53e635436f587096ab9df64
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 11, 2026.
Transparency logRelease files / safexml-1.0.0-cp312-abi3-macosx_10_12_x86_64.whl
| Download URL | safexml-1.0.0-cp312-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 233.3 kB |
| Tags | CPython 3.12 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
646ac3b276bbe4971db15233ddf1c7fca95cd86fab8c9b93c38d4ebd51cdcd22
|
|
BLAKE2b-256 checksum How to use checksums |
9aba161d2e447263b37b1bac6e20fdaab2697425574292aa0eb2a159558f949d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 11, 2026.
Transparency log