oppionated python sse server wrapping granian
Project description
[!WARNING] Under active development — May 2026
py_sse
A minimal SSE web framework for Python 3.13+. Real-time hypermedia over HTTP/1.1 with brotli-compressed Server-Sent Events.
One OS thread per connection. Topic-scoped pub/sub via hierarchical subjects. No async/await. Pure stdlib + brotli + apsw.
Install
uv add py-sse
Hello world
from py_sse import serve, html
def get_root(req):
return html("<h1>hello, world</h1>")
serve([("GET", "/", get_root)], host="0.0.0.0", port=8000)
Real-time updates
from py_sse import serve, html, Changes, Database, stream_handler
db = Database("app.db", schema="CREATE TABLE IF NOT EXISTS msg (id INTEGER PRIMARY KEY, text TEXT)")
def post_msg(req):
db.execute("INSERT INTO msg (text) VALUES (?)", (req["body"].decode(),))
db.changes.notify("msg")
return ("", [], b"")
@stream_handler(
resource_id_fn=lambda req: "msgs",
subscribe_to=lambda req: "msg",
)
def get_msgs(req):
rows = db.all("SELECT id, text FROM msg ORDER BY id DESC")
body = "<ul>" + "".join(f"<li>{t}</li>" for _, t in rows) + "</ul>"
return html(f"<html><body>{body}</body></html>")
serve(
[("GET", "/", get_msgs), ("POST", "/msg", post_msg)],
changes=db.changes,
)
Design
- Pages are database snapshots. Every render is the truth as of query time.
- Fat morphs. SSE frames send the whole page; idiomorph patches the DOM; brotli compresses across frames.
- Topic-scoped pub/sub.
changes.notify("game.5.score")wakes only viewers waiting on matching patterns. - Graceful degradation. Past the live cap, viewers get one-shot
HTML with
data-on-intervalfor polling. - One thread per connection. The thread IS the subscription. When it dies, the subscription dies. No registry.
License
MIT
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 Distribution
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 py_sse-0.12.0.tar.gz.
File metadata
- Download URL: py_sse-0.12.0.tar.gz
- Upload date:
- Size: 11.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux ARM","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f3b1678c2438b099e01cd1e8c54f9e131540d5f5447507428e7f683684dfc10
|
|
| MD5 |
6d832241dd1a9f90cb04cdb6176b085e
|
|
| BLAKE2b-256 |
2e85757260330e6d76b3612815c6625f37c154df27e44126a765c60172ea07e1
|
File details
Details for the file py_sse-0.12.0-py3-none-any.whl.
File metadata
- Download URL: py_sse-0.12.0-py3-none-any.whl
- Upload date:
- Size: 13.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux ARM","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cc7e215e847237d93e8b95178f0478db1e2c45ab1caa1764e0291036d662115f
|
|
| MD5 |
18519afdef7954ef302f342a9b15ad5b
|
|
| BLAKE2b-256 |
149a41022eb54dbf79b8dae103f858482b0b0db845d5b862c0016a96af10f733
|