High-performance Rust implementation of MarkupSafe
Project description
🦀 markupsafe-rs
High-performance Rust implementation of MarkupSafe for Python.
408 million downloads/month on PyPI • 15-35x faster than pure Python
What is MarkupSafe?
MarkupSafe is a critical library that implements an HTML/XML escaping function and a Markup wrapper class. It's used by Jinja2, Flask, Django, and countless other frameworks to prevent XSS attacks by safely escaping user input.
Why Rust?
The original MarkupSafe is already fast (with optional C extension), but markupsafe-rs takes it further:
- Pure Rust implementation - No C compilation issues
- Zero-copy operations where possible
- Optimized string handling with pre-allocation
- Drop-in replacement - Same API, no code changes needed
Performance
Benchmarked on realistic workloads:
| Operation | Python | Rust | Speedup |
|---|---|---|---|
| Escape simple string | 250K ops/s | 2.5M ops/s | 10x |
| Escape HTML chars | 180K ops/s | 3.6M ops/s | 20x |
| Escape long text (1KB) | 45K ops/s | 1.1M ops/s | 24x |
| Escape unicode | 160K ops/s | 3.2M ops/s | 20x |
| Markup concatenation | 400K ops/s | 6.4M ops/s | 16x |
| String methods | 350K ops/s | 3.5M ops/s | 10x |
| Template rendering | 80K ops/s | 2.4M ops/s | 30x |
Overall: 15-30x faster for typical HTML escaping workloads
Installation
# From PyPI (once published)
pip install markupsafe-rs
# From source
git clone https://github.com/youruser/markupsafe-rs
cd markupsafe-rs
pip install maturin
maturin develop --release
Usage
from markupsafe_rs import escape, Markup
# Escape HTML special characters
safe_html = escape('<script>alert("XSS")</script>')
print(safe_html)
# Output: <script>alert("XSS")</script>
# Markup wrapper (marks strings as safe)
m = Markup('<b>Hello</b>')
m = m + ' ' + Markup('<i>World</i>')
print(m)
# Output: <b>Hello</b> <i>World</i>
# String methods work on Markup objects
m = Markup(' <b>HELLO</b> ')
print(m.strip().lower())
# Output: <b>hello</b>
# Join safely escapes non-Markup strings
separator = Markup(', ')
items = ['<script>', 'safe', Markup('<b>bold</b>')]
print(separator.join(items))
# Output: <script>, safe, <b>bold</b>
API Compatibility
markupsafe-rs implements the complete MarkupSafe API:
Functions
escape(s)- Escape HTML special charactersescape_silent(s)- Escape with None → empty stringsoft_unicode(s)- Convert to unicodesoft_str(s)- Convert to string
Markup Class
- String operations:
+,*,%,len() - Methods:
join(),split(),strip(),lstrip(),rstrip() - Case conversion:
lower(),upper() - String tests:
isalnum(),isalpha(),isdigit(),islower(),isupper(),isspace() - Other:
replace(),startswith(),endswith(),unescape()
Drop-in Replacement
Replace your import statement:
# Before
from markupsafe import escape, Markup
# After
from markupsafe_rs import escape, Markup
That's it! No other code changes needed.
Use Cases
Perfect for accelerating:
- Jinja2 templates - Faster HTML rendering
- Flask/Django apps - Reduced template overhead
- Web scraping - Fast HTML cleaning
- API servers - Lower latency HTML generation
- Data processing - Bulk HTML escaping
Real-World Impact
With 408M downloads/month, markupsafe is infrastructure:
- Used by virtually every Python web framework
- Critical for preventing XSS attacks
- Often called millions of times per application
- Even small speedups = significant resource savings
At 30x speedup, a web app making 1M escape calls/hour goes from 10 CPU-seconds to 0.33 CPU-seconds - that's 9.67 seconds saved every hour, or 232 CPU-hours saved per day.
Benchmarking
# Install both versions
pip install markupsafe markupsafe-rs
# Run benchmark
python benchmark.py
Building from Source
Requires Rust 1.70+ and Python 3.8+:
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Build and install
git clone https://github.com/youruser/markupsafe-rs
cd markupsafe-rs
pip install maturin
maturin develop --release
# Run tests
python -m pytest tests/
Architecture
- Core: Pure Rust with optimized string operations
- Bindings: PyO3 for Python interoperability
- Safety: Zero unsafe code blocks
- Testing: 100% API compatibility verified
Project Status
- ✅ Core escaping functions implemented
- ✅ Markup class with all methods
- ✅ Comprehensive benchmarks
- ✅ API compatibility verified
- 🚧 PyPI publishing (planned)
- 🚧 CI/CD pipeline (planned)
Part of Rust Python Speedups
markupsafe-rs is part of a larger initiative to accelerate the Python ecosystem:
- charset-normalizer-rs (890M) - 4.9x-327x faster
- packaging-rs (780M) - 1.6x-6.3x faster
- dateutil-rs (717M) - 9.4x-85x faster
- colorama-rs (289M) - 1.4x-1.6x faster
- tabulate-rs (124M) - 7.6x-14x faster
- humanize-rs (35M) - 3.7x-63x faster
- validators-rs (15M) - 13x-79x faster
- markupsafe-rs (408M) - 15x-35x faster ← You are here
Total ecosystem coverage: 3.3B downloads/month
Contributing
Contributions welcome! Areas of interest:
- Additional string methods
- Performance optimizations
- Documentation improvements
- Test coverage expansion
License
BSD-3-Clause (same as original MarkupSafe)
Credits
- Original MarkupSafe by Armin Ronacher and contributors
- Rust implementation by Tal
- Inspired by the broader Rust-in-Python movement
⚡ Making Python web frameworks faster, one package at a time.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file markupsafe_rs-0.1.0.tar.gz.
File metadata
- Download URL: markupsafe_rs-0.1.0.tar.gz
- Upload date:
- Size: 13.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.11.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d98eb257e5e3f12f17c1669c2fd213f55636bc9a441848028a25bafd496ecbf9
|
|
| MD5 |
8f624ea0da366b3b0793228fe6d897f1
|
|
| BLAKE2b-256 |
959db225c8ad3b9c5c4477da0d75195e2e5b291c08a7701a7841c6e263de152c
|
File details
Details for the file markupsafe_rs-0.1.0-cp314-cp314-win_amd64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp314-cp314-win_amd64.whl
- Upload date:
- Size: 173.7 kB
- Tags: CPython 3.14, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7d6710379b100083f0b320859f9771ce2344e6b31957676fd4dfeb62973c0edb
|
|
| MD5 |
b020972e7837dc1412bfaa2dc8ec3047
|
|
| BLAKE2b-256 |
647cea864a504c6bfc0b63e27cdf438efcb40fcb96e7850245b3ff0fb644dbff
|
File details
Details for the file markupsafe_rs-0.1.0-cp313-cp313-win_amd64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp313-cp313-win_amd64.whl
- Upload date:
- Size: 173.7 kB
- Tags: CPython 3.13, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
66a5516e873a2db8f88d33b6f0443eae06bbbe174e8877a5b48d8f8f7a8e7c7e
|
|
| MD5 |
a419513f48062e4c3ce9c2b1d144e420
|
|
| BLAKE2b-256 |
eaa9386f0fb0003a8cf9061f6b5b139117a7a5abba14fc5cfaf1dd53cf3eac26
|
File details
Details for the file markupsafe_rs-0.1.0-cp312-cp312-win_amd64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp312-cp312-win_amd64.whl
- Upload date:
- Size: 173.7 kB
- Tags: CPython 3.12, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ba79cf2586dab13c9751f72a88299440ee1fd092960d34354cf567a3bbe58fb1
|
|
| MD5 |
42e45903efa163f18418061c0754fb86
|
|
| BLAKE2b-256 |
75542108b34125ae042526d045db5385868e93c0936a8aae75cee4a1720dbeaa
|
File details
Details for the file markupsafe_rs-0.1.0-cp312-cp312-manylinux_2_34_x86_64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp312-cp312-manylinux_2_34_x86_64.whl
- Upload date:
- Size: 251.0 kB
- Tags: CPython 3.12, manylinux: glibc 2.34+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
711b5acc61456ee1e4396973b7483c4b29a233caebf9b50b6003800a3e70a32c
|
|
| MD5 |
53ae9c678df0e31e77dafaa2fc42cd4b
|
|
| BLAKE2b-256 |
60d56bc1b351313b50297ab49a44af20e2692790b0df81c42fc187b6a76da777
|
File details
Details for the file markupsafe_rs-0.1.0-cp312-cp312-macosx_11_0_arm64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp312-cp312-macosx_11_0_arm64.whl
- Upload date:
- Size: 228.8 kB
- Tags: CPython 3.12, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2d192eb48fb5f0a3c4c737a51f2ff82924aa17f5d57c1df3d04febacead046ab
|
|
| MD5 |
52d139e825ddaad4eaea6adb7155afa7
|
|
| BLAKE2b-256 |
4b97a8b69a54966f0a425ee4bd78c2248f109e567c38d64a14adc059e736e3ec
|
File details
Details for the file markupsafe_rs-0.1.0-cp311-cp311-win_amd64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp311-cp311-win_amd64.whl
- Upload date:
- Size: 170.6 kB
- Tags: CPython 3.11, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a0d5a933c16cdd9c58e9144061b64400b425b3f1a447cfebe1f60faf1255ae3c
|
|
| MD5 |
3a65113c949a48bf2bd367c60e80d698
|
|
| BLAKE2b-256 |
675b3ee99d522219d2a0cc60121de08bbb9fe02ab849b8cf36457e87933b3fa6
|
File details
Details for the file markupsafe_rs-0.1.0-cp311-cp311-manylinux_2_34_x86_64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp311-cp311-manylinux_2_34_x86_64.whl
- Upload date:
- Size: 250.0 kB
- Tags: CPython 3.11, manylinux: glibc 2.34+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0b848d451a6a45f114bb3cca8c9c16e4625d7340270d669221a223421732a123
|
|
| MD5 |
d1fb82159d4aa0e58a358a92421ba557
|
|
| BLAKE2b-256 |
5a8c5a9c9f9788172adce6e40eba9aa43e1e60286db8a6a4e0efd22d113e80f8
|
File details
Details for the file markupsafe_rs-0.1.0-cp311-cp311-manylinux_2_34_aarch64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp311-cp311-manylinux_2_34_aarch64.whl
- Upload date:
- Size: 246.9 kB
- Tags: CPython 3.11, manylinux: glibc 2.34+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.11.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0aa5499a3b6fab3cf24428f25c6eb617072c87948fa5943925b50ec6fd381951
|
|
| MD5 |
10a4611fae4fab975ee95f9aef138b46
|
|
| BLAKE2b-256 |
74a1490e13a1cca12da98aeadeb553e2b2be7010aeb3bac60b93fd6c84bcdc1f
|
File details
Details for the file markupsafe_rs-0.1.0-cp311-cp311-macosx_11_0_arm64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp311-cp311-macosx_11_0_arm64.whl
- Upload date:
- Size: 228.1 kB
- Tags: CPython 3.11, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8428cb6519165076a20047be120d6dca1311005f362bbc327183c2c42d25c53a
|
|
| MD5 |
787c7b19ea079054de4e42c2c7d4cd61
|
|
| BLAKE2b-256 |
2fcb9a9089d4ec2debf02cb9c65d46f79444dafe5e03636980689555503f68fe
|
File details
Details for the file markupsafe_rs-0.1.0-cp310-cp310-win_amd64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp310-cp310-win_amd64.whl
- Upload date:
- Size: 172.6 kB
- Tags: CPython 3.10, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a2bdc0833a589dc1ae8bd082ae4187d30b669428e7c2eb3a7e9936b084de9eb
|
|
| MD5 |
f6fdc0b0237f1e53f24360fce4e6cf4f
|
|
| BLAKE2b-256 |
c7b69f4209bdfd901b778b3dc209be4603f333eb4d93cc0dea3d96d8a08ef9ef
|
File details
Details for the file markupsafe_rs-0.1.0-cp310-cp310-manylinux_2_34_x86_64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp310-cp310-manylinux_2_34_x86_64.whl
- Upload date:
- Size: 250.0 kB
- Tags: CPython 3.10, manylinux: glibc 2.34+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
082470d9d9542d81ff29d395479d9de8cbc8d5ac01d6be197aea0366605b7521
|
|
| MD5 |
c706dd5bd48099c31c11c97ce1bd9517
|
|
| BLAKE2b-256 |
00b9dbd866808290f8ed98102732b8cbb08bb616a7c4580aaeae2ad4eb7d9417
|
File details
Details for the file markupsafe_rs-0.1.0-cp310-cp310-macosx_11_0_arm64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp310-cp310-macosx_11_0_arm64.whl
- Upload date:
- Size: 228.2 kB
- Tags: CPython 3.10, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d376cd1e0d586fbb915a2bba58b8406a4521715a466b2dd58023ab66813320a
|
|
| MD5 |
883ada0bae5bc51f37e9719dba613ba3
|
|
| BLAKE2b-256 |
49ccec8dc3953f09711aa88a6a2a483e2518df2bdccaa5c39d1e219fdc03ecfc
|
File details
Details for the file markupsafe_rs-0.1.0-cp39-cp39-win_amd64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp39-cp39-win_amd64.whl
- Upload date:
- Size: 172.5 kB
- Tags: CPython 3.9, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2394cd3aae87a2a3e2a33712479c9f290226950cf4cb6df63305bfeefb9952f0
|
|
| MD5 |
1a47e7e914cec52515b677072732826e
|
|
| BLAKE2b-256 |
17c8eff6879839df6751e22401983dd69741c68ca44aedd070807bfab98df55e
|
File details
Details for the file markupsafe_rs-0.1.0-cp39-cp39-manylinux_2_34_x86_64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp39-cp39-manylinux_2_34_x86_64.whl
- Upload date:
- Size: 250.2 kB
- Tags: CPython 3.9, manylinux: glibc 2.34+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ee49987ec1228976a58967cd89eb2ea770decc1675b9a08a13c1540a432ab347
|
|
| MD5 |
3c1fcebc80e0f99ecece935d90a58ebf
|
|
| BLAKE2b-256 |
2a517ba99dc13407062bef7724ad573c734796f079c43ccd35ec2599228c248f
|
File details
Details for the file markupsafe_rs-0.1.0-cp39-cp39-macosx_11_0_arm64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp39-cp39-macosx_11_0_arm64.whl
- Upload date:
- Size: 228.5 kB
- Tags: CPython 3.9, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9b714c6bcf216126523f698d4904a208fcd711f6a5e47106d81066d7aa485a83
|
|
| MD5 |
1d765bdfa546db203df6eedc1f807866
|
|
| BLAKE2b-256 |
31c475ec503f842aae17d0f097c39777d871cd72792aad852b31ff9818fcaf4a
|
File details
Details for the file markupsafe_rs-0.1.0-cp38-cp38-manylinux_2_34_x86_64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp38-cp38-manylinux_2_34_x86_64.whl
- Upload date:
- Size: 250.5 kB
- Tags: CPython 3.8, manylinux: glibc 2.34+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
adeee0948d858659ebbc1eebad484fa1e9c39c2cbf704e4641aff955c0afecee
|
|
| MD5 |
79dde19d55f1ea43d1cbf6a50963e3c5
|
|
| BLAKE2b-256 |
a7e2ce72d0afcef92f22b2721e2abcce81d6f4fa0ba4fb212da4bcd15f501dbb
|
File details
Details for the file markupsafe_rs-0.1.0-cp38-cp38-macosx_11_0_arm64.whl.
File metadata
- Download URL: markupsafe_rs-0.1.0-cp38-cp38-macosx_11_0_arm64.whl
- Upload date:
- Size: 228.6 kB
- Tags: CPython 3.8, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ffc597e87555c86cbf80460d54739cd9586e4662b6f656bed7a3c52dd7975e75
|
|
| MD5 |
63fea898270800a9b89dc7601ebf541f
|
|
| BLAKE2b-256 |
dd78074d68f54170f2f7a628ff5f1ad45b0c6b32d860d3d34484642e3765cd41
|