oppionated python sse server wrapping granian
Project description
[!WARNING] Under active development — May 2026
py_sse
minimal python sse server
A small framework for building Datastar apps over Server-Sent Events. Runs on Granian via RSGI, talks to SQLite through apsw, and pushes live DOM updates from the database with no client-side polling.
pip install py-sse
Hello
from py_sse import create_app, serve
app = create_app()
@app.get('/')
async def index(req):
return '<h1>hello</h1>'
if __name__ == '__main__':
serve(app)
Handlers return one of: str → 200 HTML, dict → 200 JSON, None → 204,
(url, status) → redirect/text, or an async generator → raw SSE stream.
Live feed
@app.stream is the core feature: register an SSE endpoint that re-renders
every time the database changes. Changes bridges SQLite's update hook to
asyncio, so any write() wakes every connected stream.
from py_sse import (create_app, create_db, migrate, query, write,
signals, Changes, serve)
db = create_db('chat.db')
migrate(db, "CREATE TABLE IF NOT EXISTS msgs (id INTEGER PRIMARY KEY, txt TEXT)")
def startup(loop):
global changes
changes = Changes(db, loop)
app = create_app(on_init=startup, on_del=lambda loop: changes.close())
@app.post('/say')
async def say(req):
s = await signals(req)
write(db, lambda c: c.execute("INSERT INTO msgs(txt) VALUES(?)", (s['text'],)))
@app.stream('/feed', on=lambda: changes)
def feed(req):
rows = query(db, "SELECT txt FROM msgs ORDER BY id DESC LIMIT 50")
return ''.join(f'<p>{txt}</p>' for (txt,) in rows)
Post to /say from any tab and every tab subscribed to /feed re-renders.
API
App — create_app(routes=None, *, on_init=None, on_del=None). Returns a
handle with .get/.post/.put/.patch/.delete(path), .mount(prefix, fn),
.before(fn, *, methods=None), and .stream(path, *, on). Paths support
{name} params. on_init(loop)/on_del(loop) run at startup/shutdown for
wiring shared state onto the right loop.
Requests — signals(req) reads Datastar signals (query param on GET, JSON
body otherwise). body(req) / body_stream(req) read the raw payload.
set_cookie(req, name, value, **opts) queues a Set-Cookie.
SSE events — patch_elements, patch_signals, remove_signals,
execute_script, redirect. @app.stream calls patch_elements for you.
DB — create_db(path) opens a WAL connection. migrate(conn, sql) applies
schema idempotently. query(conn, sql, bindings=(), *, limit=1000) returns
rows. write(conn, fn, *args) runs fn in a transaction. Changes(db, loop)
fans writes out to streams.
Static — static(app, url_prefix, directory) serves a file or directory
with HTTP range support and a directory-traversal guard.
Cookies — create_signer(secret) returns an HMAC-SHA256 signer with
.sign(value) / .unsign(signed, max_age=3600).
Serving — serve(app, *, host, port, **kwargs) runs in the foreground.
serve_background(app, host, port) returns a ServerState you stop with
stop_background(state); dev_alive(state) checks the port. Useful for
notebooks. start_tunnel(port) / stop_tunnel(t) expose a local server via
ngrok (requires ngrok and NGROK_AUTHTOKEN).
License
MIT
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.1.tar.gz.
File metadata
- Download URL: py_sse-0.12.1.tar.gz
- Upload date:
- Size: 11.9 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 |
c9ecc02b3b9c54bce44e1ff6a00b94dd95f872312559c3c6685f3fd27555809c
|
|
| MD5 |
1a0a8e5a1473eeb10fae3806e16cf5b9
|
|
| BLAKE2b-256 |
1977e019a7b12f6214203dad3cb9e482b6329698dea59190a1342d517cb2265e
|
File details
Details for the file py_sse-0.12.1-py3-none-any.whl.
File metadata
- Download URL: py_sse-0.12.1-py3-none-any.whl
- Upload date:
- Size: 14.3 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 |
d9b66e3322f4004daed454c15c4559ef1fdef47d4c7bf2119a58845a02bed7dc
|
|
| MD5 |
28ad44056cbed52a63dcefb475770132
|
|
| BLAKE2b-256 |
11b8578f5aa3efa08474435e9c994f02063467cea7ba7eb00831f1fe20930355
|