zan
A Flask-compatible Python web framework powered by a Rust HTTP core.
from zan import Flask, jsonify, request
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, World!"
@app.route("/user/<int:uid>")
def user(uid):
return jsonify(uid=uid, name=f"user{uid}")
@app.route("/post", methods=["POST"])
def post():
return jsonify(echo=request.get_json())
if __name__ == "__main__":
app.run() # zan/0.1.0 — Rust HTTP server
Replace from flask import ... with from zan import ... and the rest of your code stays the same.
Install
zan provides pre-built wheels for Windows, macOS, and Linux. No Rust toolchain is required:
pip install zan
Optional template support:
pip install zan[templates]
To build from source, see Building from source below.
Building from source
You need the Rust toolchain (rustc 1.75+) and Python 3.8+:
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install maturin pytest jinja2
maturin develop --release
pytest tests/ -q
Features
- Drop-in Flask replacement — same
Flask,request,session,g,current_app,jsonify,url_for,abort,Blueprint,render_template, andsend_fileAPIs. - Rust HTTP core — multi-threaded Tokio server with keep-alive, chunked transfer, pipelining,
100-continue, and size/timeouts enforced in Rust. - Trie router — Werkzeug-compatible converters (
<int>,<float>,<path>,<uuid>,<any(a,b)>), strict-slash and merge-slash redirects, automaticHEAD/OPTIONShandling. - Static files served in Rust — no GIL contention; includes
Last-Modified/304, MIME inference, and path-traversal protection. - Sessions & flashes — signed cookie sessions (HMAC-SHA256, itsdangerous semantics) and
flash/get_flashed_messages. - Signals & hooks —
before_request,after_request,teardown_request,teardown_appcontext, context processors, and a full signal implementation. - Multiple apps & multi-core — run several apps in the same process (
start/stop) and scale beyond the GIL withrun(processes=N)plus a Rust TCP load balancer.
Installation & Build
You need the Rust toolchain (rustc 1.75+) and Python 3.8+:
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install maturin pytest jinja2
maturin develop --release
pytest tests/ -q
Templates are optional and require jinja2 (pip install jinja2). If jinja2 is not installed, render_template* will raise a clear error.
Flask Compatibility
API surface already implemented and tested (91 test cases):
| Category | Coverage |
|---|---|
| Application | Flask(import_name), route/add_url_rule, run, start/stop (non-blocking multi-instance), test_client, config, debug, secret_key, logger, cli, extensions, name |
| Routing | <string>, <int>, <float>, <path>, <uuid>, <any(a,b)> converters, methods, endpoint, strict-slash 308 redirect, merge-slashes redirect, automatic HEAD/OPTIONS, 405 + Allow |
| Request | request.args/form/values/json/data/get_json/headers/cookies/method/path/url/endpoint/view_args/blueprint/remote_addr/user_agent/authorization, multipart file uploads |
| Response | str/bytes/dict/list/Response/(body, status, headers) tuples, generators, make_response, jsonify (sorted keys + ensure_ascii), redirect, send_file, set_cookie/delete_cookie |
| Hooks | before_request, after_request, teardown_request, teardown_appcontext, context_processor |
| Errors | Full HTTPException family, abort, errorhandler (status codes and exception classes), debug traceback page |
| Context | request/session/g/current_app proxies, app_context/request_context/test_request_context, RuntimeError when accessed outside context |
| Session | Signed-cookie sessions (HMAC-SHA256, itsdangerous semantics), flash/get_flashed_messages |
| Blueprints | Blueprint, url_prefix, blueprint-level routes/hooks/error handlers, bp.endpoint naming |
| Templates | render_template, render_template_string, blueprint template folders, url_for/get_flashed_messages context injection |
| URL building | url_for (args, _anchor, _external, blueprint defaults) |
| Static files | /static/ served directly from Rust (Last-Modified/304, MIME inference, path-traversal protection) |
| Signals | Full signal support (uses blinker if available, otherwise a built-in compatible implementation) |
| Multi-instance | Multiple apps can start()/stop() in the same process, sharing the Rust runtime |
| Multi-core | run(processes=N) multi-process + Rust TCP load balancer (round-robin, X-Forwarded-For) |
Known differences (intentional or not yet implemented):
- No Werkzeug reloader — in debug mode you must restart manually after code changes (a warning is printed).
- Not WSGI-based — apps run on the built-in Rust server, not
werkzeug.servingor gunicorn. request.schemeis alwayshttp(TLS is on the roadmap).
Performance
Local comparison on Windows, Python 3.13, 8 keep-alive connections, pure Python view functions:
| Scenario | zan | Flask dev server | Speedup |
|---|---|---|---|
| Plain text | 3205 req/s | 291 req/s | 11.0x |
| JSON | 2941 req/s | 423 req/s | 6.9x |
| Route params | 3077 req/s | 419 req/s | 7.4x |
| POST JSON | 2375 req/s | 422 req/s | 5.6x |
TechEmpower-style benchmark (six canonical endpoints, single connection, Flask side served by waitress):
| Test | zan | Flask | Speedup |
|---|---|---|---|
| plaintext | 1,150 req/s | 199 req/s | 5.8x |
| json | 1,077 req/s | 232 req/s | 4.6x |
| db / queries / fortunes | — | — | 1.1–1.2x (SQLite-bound) |
Multi-core: run(processes=N) breaks through the GIL; CPU-bound views scale almost linearly (2 cores measured at ~1.9x).
To reproduce: python benchmarks/bench_keepalive.py (keep-alive), benchmarks/tfb/harness2.py (TechEmpower, methodology and limitations in benchmarks/tfb/results.md), and benchmarks/bench_multiprocess2.py (multi-core). Note that the Flask dev server (Werkzeug) does not enable keep-alive by default; both frameworks were measured with the same client and workload.
Architecture
┌─────────────────────────────────────────────────────┐
│ Your code (written exactly like Flask) │
├─────────────────────────────────────────────────────┤
│ zan Python layer (app/wrappers/ctx/session/...) │ ← compatibility: Flask API aligned one-to-one
├──────────────────────── PyO3 ───────────────────────┤
│ zan Rust core (_zan) │
│ • Process-wide shared Tokio runtime │
│ (workers = CPU cores, reused across instances) │
│ • Multi-threaded Tokio HTTP/1.1 server │
│ (keep-alive, chunked, pipelining, 100-continue, │
│ timeouts and size limits) │
│ • Trie router (Werkzeug converter semantics, │
│ static-first, strict/merge-slash redirects) │
│ • Static file serving (fully in Rust, no GIL) │
│ • Native Rust JSON serialization │
│ (output aligned with json.dumps) │
│ • TCP load balancer (multi-process mode, │
│ round-robin + X-Forwarded-For) │
│ • Fast error paths: 404/405/413/431 skip Python │
└─────────────────────────────────────────────────────┘
- Each request is parsed by a Tokio worker; after routing it crosses into Python via
spawn_blocking(releasing the GIL viaallow_threads), with full app/request context pushed so hooks, signals, and sessions behave like Flask. - When a view returns
str/bytes/dict/tuple, serialization happens in Rust;Responseobjects pass through FFI as a(status, headers, body)tuple via_fast(), with no intermediateenviron. - Uncaught exceptions fall back to the Python error chain (
errorhandler→HTTPException→ 500); debug mode renders a traceback page.
Project Structure
src/ Rust core
router.rs Trie router + converters
http.rs Connection/parsing/static files/dispatch
json.rs Native JSON serialization
pyapi.rs PyO3 Server class (shared runtime/lifecycle/load-balancer entry)
balancer.rs Multi-process TCP load balancer
zan/ Python compatibility layer (16 modules)
tests/ 91 test cases (compatibility 62 / features 18 / multi-method rules 3 / multi-instance & multi-core 8)
blog/ Full blog example (zan backend + React/shadcn frontend served in one process)
benchmarks/ Benchmarks: keep-alive comparison / multi-core scaling / TechEmpower standard (tfb/)
Documentation
Full documentation is currently available in Chinese under docs/:
- Index — overview, feature table, quick start
- Quick start — routes, requests, responses, templates, sessions, blueprints, error handling
- Routing — all converters, methods, strict-slash 308, 405, OPTIONS/HEAD
- Request object — all
requestattributes and methods - Response object — view return values,
jsonify,redirect,send_file, cookies - Context & hooks —
request/session/g/current_app, hooks, signals - Sessions & flashes — signed cookie internals, permanent sessions,
flash - Blueprints — registration,
url_prefix, blueprint hooks/static/template folders - Error handling —
HTTPExceptionfamily,abort,errorhandler - Debugging — debug page, reloader, colored output
- CLI —
python -m zan run/shell/routes - Config reference — all configuration keys
- Multi-instance & multi-core —
start/stop,processes=N, load balancer - Architecture — Rust core, PyO3 boundary, request lifecycle, performance data
- Testing —
test_client,test_request_context, pytest integration - FAQ — differences from Flask, deployment advice, performance tuning
English translations of the docs are welcome — see CONTRIBUTING.md.
Roadmap
- HTTPS/TLS (rustls) and HTTP/2
- WebSocket support
- More edge-case behavior for
url_forwithexternalandSERVER_NAME - CI matrix for platforms other than Windows and abi3 wheel publishing
Contributing
Contributions are welcome! Please read CONTRIBUTING.md for guidelines on bug reports, feature requests, and pull requests.
License
MIT © 2026 RaysunKR
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 zan-0.1.0.tar.gz.
File metadata
- Download URL: zan-0.1.0.tar.gz
- Upload date:
- Size: 395.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eebb12c9e7bb7d5d4acb545a7e628a3d2920ec5423675850f69bf34fd6bb8be3
|
|
| MD5 |
b851dd9d6c81c18a23361812cc25f991
|
|
| BLAKE2b-256 |
d7b4da5d191f11b710e277b2527f12f8bcc4e60ff8d79e0641605d1edbb9ed8b
|
Provenance
The following attestation bundles were made for zan-0.1.0.tar.gz:
Publisher:
publish.yml on RaysunKR/zan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zan-0.1.0.tar.gz -
Subject digest:
eebb12c9e7bb7d5d4acb545a7e628a3d2920ec5423675850f69bf34fd6bb8be3 - Sigstore transparency entry: 2585026024
- Sigstore integration time:
-
Permalink:
RaysunKR/zan@56732b39ead3e6eae134ee9fafee8488027dbd25 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/RaysunKR
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@56732b39ead3e6eae134ee9fafee8488027dbd25 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file zan-0.1.0-cp38-abi3-win_amd64.whl.
File metadata
- Download URL: zan-0.1.0-cp38-abi3-win_amd64.whl
- Upload date:
- Size: 529.6 kB
- Tags: CPython 3.8+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0b59c36affb0fa8124296137bf1a47963664672fe317bbe7d8831fa4699ff121
|
|
| MD5 |
61e7c52d75c62146419dde206dc41730
|
|
| BLAKE2b-256 |
49e220589e293ff9272414482564352c4ea4c528d741968fe25cabc4da642c51
|
Provenance
The following attestation bundles were made for zan-0.1.0-cp38-abi3-win_amd64.whl:
Publisher:
publish.yml on RaysunKR/zan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zan-0.1.0-cp38-abi3-win_amd64.whl -
Subject digest:
0b59c36affb0fa8124296137bf1a47963664672fe317bbe7d8831fa4699ff121 - Sigstore transparency entry: 2585026473
- Sigstore integration time:
-
Permalink:
RaysunKR/zan@56732b39ead3e6eae134ee9fafee8488027dbd25 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/RaysunKR
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@56732b39ead3e6eae134ee9fafee8488027dbd25 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file zan-0.1.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: zan-0.1.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 611.6 kB
- Tags: CPython 3.8+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d196b96b102fc3b0dd0c05cf589f4ecb01d0dd2131b3729090a7a62b994e24a1
|
|
| MD5 |
20c0f818198544eb22e94b637da0a75f
|
|
| BLAKE2b-256 |
0a3e40b01fe320d60af64f5f9d297aa8e2c34f5c177749db81442b0dbee8d64c
|
Provenance
The following attestation bundles were made for zan-0.1.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
publish.yml on RaysunKR/zan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zan-0.1.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
d196b96b102fc3b0dd0c05cf589f4ecb01d0dd2131b3729090a7a62b994e24a1 - Sigstore transparency entry: 2585026848
- Sigstore integration time:
-
Permalink:
RaysunKR/zan@56732b39ead3e6eae134ee9fafee8488027dbd25 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/RaysunKR
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@56732b39ead3e6eae134ee9fafee8488027dbd25 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file zan-0.1.0-cp38-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: zan-0.1.0-cp38-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 567.1 kB
- Tags: CPython 3.8+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ae853e1248362f2b71621120ab5d58ee09d094ad653197e7b1a47e234c71f11f
|
|
| MD5 |
2d7c3f05564c7899c1a7bf29495075e9
|
|
| BLAKE2b-256 |
07d86c2a595ca36cd6f004e568571e38b6a0a5a814524b64f751c005a33678cd
|
Provenance
The following attestation bundles were made for zan-0.1.0-cp38-abi3-macosx_11_0_arm64.whl:
Publisher:
publish.yml on RaysunKR/zan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zan-0.1.0-cp38-abi3-macosx_11_0_arm64.whl -
Subject digest:
ae853e1248362f2b71621120ab5d58ee09d094ad653197e7b1a47e234c71f11f - Sigstore transparency entry: 2585026116
- Sigstore integration time:
-
Permalink:
RaysunKR/zan@56732b39ead3e6eae134ee9fafee8488027dbd25 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/RaysunKR
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@56732b39ead3e6eae134ee9fafee8488027dbd25 -
Trigger Event:
workflow_dispatch
-
Statement type: