Bwsgi
Ultra-lightweight, zero-dependency Python WSGI framework. Flask-style routing. Django-style auth. Built on the Python Standard Library.
Why Bwsgi?
- Zero dependencies — pure Python stdlib
- ~5-8 MB idle RAM — runs happily on 512MB hosts like Serv00
- Flask-style routing — decorator-based, familiar syntax
- Secure auth built in — PBKDF2 password hashing + DB-backed sessions
- WSGI native — works with Passenger, Gunicorn, uWSGI
Install
pip install bwsgi
Quick Start
from bwsgi import Bwsgi, Response, login_required
app = Bwsgi(db_path="app.db")
@app.route("/")
def home(req):
return Response.html("<h1>Hello from Bwsgi</h1>")
@app.route("/dashboard")
@login_required
def dashboard(req):
return Response.html(f"<h1>Hi {req.user['username']}</h1>")
if __name__ == "__main__":
app.run()
Routing and responses
Routes accept named path parameters and one or more HTTP methods:
@app.route("/users/<username>", methods=("GET",))
def user(req, username):
return Response.json({"username": username})
profile_url = app.url_for("user", username="Jane Doe")
Path values generated by url_for are URL-encoded. Requests to a known path
with an unsupported method receive 405 Method Not Allowed and an Allow
header. Static files are served from static/ at /static/; files are
streamed in chunks and paths are checked against the configured static root.
Request provides query, headers, cookies, body, form(), and json().
Malformed JSON request bodies receive 400 Bad Request.
Response provides text(), html(), json(), redirect(), and file().
JSON request validation
Use Request.validate_json() to require a JSON object with specific required
fields and Python types. Malformed JSON and invalid values return a JSON
400 Bad Request response:
@app.route("/api/items", methods=("POST",))
def create_item(req):
item = req.validate_json({"name": str, "quantity": int})
return Response.json(item, "201 Created")
Sessions and CSRF
req.session is a server-side dictionary persisted in SQLite and identified by
a signed, HTTP-only cookie. Set a stable secret key when creating the app so
session cookies survive process restarts:
app = Bwsgi(db_path="app.db", secret_key="load-this-from-your-environment")
Protect state-changing routes with @csrf_protect. Render req.csrf_token()
into a hidden form field named _csrf_token, or send it in the
X-Csrf-Token header for JSON requests:
@app.route("/submit", methods=("POST",))
@csrf_protect
def submit(req):
req.session["submitted"] = True
return Response.json({"ok": True})
Safe methods (GET, HEAD, OPTIONS, and TRACE) do not require a token.
Keep state-changing actions on non-safe methods and use CSRF protection for
browser-session routes.
Templates, middleware, and errors
Templates are UTF-8 files under templates/ by default. {{ name }} values
are HTML-escaped automatically; this intentionally supports variable
substitution only, not template logic:
<h1>Hello, {{ username }}</h1>
return app.render_template("hello.html", username="Ada")
Register standard WSGI middleware with a factory that accepts and returns a WSGI callable:
def request_id_middleware(next_app):
def middleware(environ, start_response):
environ["HTTP_X_REQUEST_ID"] = "example"
return next_app(environ, start_response)
return middleware
app.add_middleware(request_id_middleware)
Error handlers may be registered for HTTP status codes or exception classes:
@app.errorhandler(ValueError)
def invalid_value(req):
return Response.json({"error": "invalid value"}, "400 Bad Request")
License
MIT
Metadata
Release files for bwsgi 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bwsgi-0.1.1.tar.gz | 14.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bwsgi-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 26.5 kB
Release files / bwsgi-0.1.1.tar.gz
| Download URL | bwsgi-0.1.1.tar.gz |
|---|---|
| Size | 14.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
07b036e8629d2f2d2685ff268c7b252ee1d780fbeefd6e7c5889a7b81640673a
|
|
BLAKE2b-256 checksum How to use checksums |
88692278f6f80297c7cb699b5a778fef4d9b60c7315d0c49636291046333d471
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / bwsgi-0.1.1-py3-none-any.whl
| Download URL | bwsgi-0.1.1-py3-none-any.whl |
|---|---|
| Size | 11.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
76312541205332873d9b6de8f6545cb25da8e8076c4fbe9efacc9748004e504b
|
|
BLAKE2b-256 checksum How to use checksums |
ca39a48a75e4712a9b6af52a4a6ec7337cd6572d097ead2d5ae945647148d4be
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|