WebSocket utilities for HawkAPI — rooms, broadcasting, Redis pub/sub, auth integration
Project description
hawkapi-websockets
WebSocket utilities for HawkAPI. Connection manager with rooms + broadcasting, optional Redis pub/sub backplane for multi-process fan-out, and a heartbeat monitor.
Install
pip install hawkapi-websockets # local-only (single-process)
pip install 'hawkapi-websockets[redis]' # + Redis backplane
Quickstart
from hawkapi import Depends, HawkAPI, WebSocket
from hawkapi_websockets import ConnectionManager, get_manager, init_websockets
app = HawkAPI()
init_websockets(app)
@app.websocket("/ws/{room}")
async def ws_room(websocket: WebSocket, room: str, m: ConnectionManager = Depends(get_manager)):
await websocket.accept()
conn = await m.connect(websocket, rooms=[room])
try:
await m.broadcast_json({"event": "joined", "id": conn.id}, room=room)
async for msg in websocket.iter_text():
await m.broadcast_text(msg, room=room, exclude=[conn.id])
finally:
await m.disconnect(conn.id)
await m.broadcast_json({"event": "left", "id": conn.id}, room=room)
Broadcasting
await m.broadcast_text("hi") # everyone
await m.broadcast_json({"event": "x"}, room="lobby") # one room
await m.broadcast_text("hi", exclude=[conn.id]) # skip the sender
await m.send_to(connection_id, {"private": True}) # direct
Failed sends auto-drop the broken connection, so a misbehaving client never blocks the broadcast.
Redis backplane
When you run multiple worker processes, broadcasts must travel across them. Plug in the Redis backplane and every publish() is fanned out to every replica's ConnectionManager.
from hawkapi_websockets import RedisBackplane, init_websockets
backplane = RedisBackplane(url="redis://localhost:6379/0", channel="hawkapi:ws")
m = init_websockets(app, backplane=backplane)
# Anywhere in your code:
await backplane.publish({"kind": "json", "room": "lobby", "payload": {"event": "tick"}})
The backplane subscribes during app.on_startup and shuts down with the app.
Heartbeat
from hawkapi_websockets import HeartbeatConfig, HeartbeatMonitor
monitor = HeartbeatMonitor(manager=m, config=HeartbeatConfig(interval_seconds=30, timeout_seconds=90))
monitor.start() # background task pings every interval, drops stale connections
@app.websocket("/ws")
async def ws(websocket):
await websocket.accept()
conn = await m.connect(websocket)
monitor.touch(conn.id)
try:
async for msg in websocket.iter_text():
monitor.touch(conn.id) # client liveness signal
...
finally:
await m.disconnect(conn.id)
Testing
The manager works with any object implementing WebSocketLike (send_text/send_bytes/close), so you can test broadcast logic without a real WebSocket connection.
class FakeWS:
def __init__(self): self.sent: list[str] = []
async def send_text(self, data): self.sent.append(data)
async def send_bytes(self, data): ...
async def close(self, code=1000): ...
async def test_chat_broadcast():
m = ConnectionManager()
a, b = FakeWS(), FakeWS()
await m.connect(a, connection_id="alice", rooms=["lobby"])
await m.connect(b, connection_id="bob", rooms=["lobby"])
await m.broadcast_text("hi", room="lobby", exclude=["alice"])
assert b.sent == ["hi"]
Development
git clone https://github.com/ashimov/hawkapi-websockets.git
cd hawkapi-websockets
uv sync --extra dev
uv run pytest -q
uv run ruff check . && uv run ruff format --check .
uv run pyright src/
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 hawkapi_websockets-0.2.1.tar.gz.
File metadata
- Download URL: hawkapi_websockets-0.2.1.tar.gz
- Upload date:
- Size: 22.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
44b02dc1deb2d9ce0d0fa1fd2e058b18f52ec897bb66036323800459b26cb57c
|
|
| MD5 |
10809d235daeb4e3bdcfbaeb9c47a0c3
|
|
| BLAKE2b-256 |
997275610d035dc71cc49950fd6e8b42a12cdde7037cc38ee1edfa4b7f732f61
|
Provenance
The following attestation bundles were made for hawkapi_websockets-0.2.1.tar.gz:
Publisher:
release.yml on ashimov/hawkapi-websockets
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hawkapi_websockets-0.2.1.tar.gz -
Subject digest:
44b02dc1deb2d9ce0d0fa1fd2e058b18f52ec897bb66036323800459b26cb57c - Sigstore transparency entry: 1553633765
- Sigstore integration time:
-
Permalink:
ashimov/hawkapi-websockets@d0c6d9a3f70bc40519fd87db71f63579c4bee3ef -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/ashimov
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d0c6d9a3f70bc40519fd87db71f63579c4bee3ef -
Trigger Event:
release
-
Statement type:
File details
Details for the file hawkapi_websockets-0.2.1-py3-none-any.whl.
File metadata
- Download URL: hawkapi_websockets-0.2.1-py3-none-any.whl
- Upload date:
- Size: 11.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6608a59e99198609b4032d0471685f6c8663a01460a6dbe54599ce099fa589df
|
|
| MD5 |
e2d799d2f2c964da9b6f82e798d4468f
|
|
| BLAKE2b-256 |
e32d2390dd61d7f65967f1df93e1def657783c07560854395f9df2ef0c92f1db
|
Provenance
The following attestation bundles were made for hawkapi_websockets-0.2.1-py3-none-any.whl:
Publisher:
release.yml on ashimov/hawkapi-websockets
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hawkapi_websockets-0.2.1-py3-none-any.whl -
Subject digest:
6608a59e99198609b4032d0471685f6c8663a01460a6dbe54599ce099fa589df - Sigstore transparency entry: 1553633773
- Sigstore integration time:
-
Permalink:
ashimov/hawkapi-websockets@d0c6d9a3f70bc40519fd87db71f63579c4bee3ef -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/ashimov
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d0c6d9a3f70bc40519fd87db71f63579c4bee3ef -
Trigger Event:
release
-
Statement type: