Gravier
Small typed wrapper around granian's RSGI interface: a router with msgspec validation, app lifecycle, SSE helpers, OpenAPI generation, and test utilities.
pip install gravier
Quick start
import msgspec
from gravier import App, Body, Query, Response, Router, serve
class SearchQuery(msgspec.Struct):
q: str
limit: int = 10
class CreateItem(msgspec.Struct):
name: str
price: float
router = Router()
@router.get("/health")
async def health(scope, proto) -> dict:
return {"status": "ok"}
@router.get("/items/{item_id}")
async def get_item(scope, proto, item_id: str) -> dict:
return {"item_id": item_id}
@router.get("/search")
async def search(query: Query[SearchQuery]) -> dict:
return {"q": query.q, "limit": query.limit}
@router.post("/items")
async def create(req: Body[CreateItem]) -> dict: # 422 on invalid body
return {"name": req.name}
app = App(router, on_startup=[], on_shutdown=[])
Serve it (main.py above):
granian --interface rsgi main:app
or programmatically, with a clean error when the port is taken:
from gravier import serve
serve("main:app", port=8000)
Features
-
Router — static and
{param}routes,GET/POST/PUT/PATCH/DELETE,include(other, prefix=...), JSON errors for 404/405/422/500. -
Query[T]/Body[T]— msgspec-validated query strings and JSON bodies; pyright sees the concreteTon the parameter. Invalid input returns 422 automatically. -
Depends— inject sync callables, coroutines, or async generators (setup/teardown) into handlers. -
App— bundles a router with startup/shutdown hooks mapped onto granian's__rsgi_init__/__rsgi_del__worker lifecycle. -
SSE —
sse(event, data)formatting and ready-madeSSE_HEADERS; a handler returningNonekeeps the protocol to itself:from gravier import SSE_HEADERS, sse @router.post("/events") async def events(scope, proto) -> None: transport = proto.response_stream(200, SSE_HEADERS) await transport.send_bytes(sse("delta", {"text": "hello"}))
-
OpenAPI —
build_openapi(router, title=...)generates a 3.1 spec fromQuery/Body/return annotations;openapi_handler(router, title=...)serves it. -
Testing (
gravier.testing) —FakeScope/FakeProtofor direct handler tests (including captured streams), andASGIBridgeto run the router under any ASGI test client without booting granian:import niquests from gravier.testing import ASGIBridge with niquests.Session(app=ASGIBridge(router)) as client: assert client.get("http://test/health").json() == {"status": "ok"}
Development
uv sync --group dev
just ci # lint + type check + tests
Metadata
Release files for gravier 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| gravier-0.1.0.tar.gz | 11.8 kB | Without provenance | Details for gravier-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| gravier-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for gravier-0.1.0-py3-none-any.whl |
Total release size: 26.3 kB
Release files / gravier-0.1.0.tar.gz
| Download URL | gravier-0.1.0.tar.gz |
|---|---|
| Size | 11.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
180958d5962bcf2544be4ba86d3dbefe1ca29fa8bceb5158dd7605b7a075a8f1
|
|
BLAKE2b-256 checksum How to use checksums |
e4b8caf67ecfdce96242e1af865ea500e8ddad08d06becc6160e5668756de68d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / gravier-0.1.0-py3-none-any.whl
| Download URL | gravier-0.1.0-py3-none-any.whl |
|---|---|
| Size | 14.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5458a888cf1f8464e9bb2c3252c03742302b9e6abdeb197aff9d596e3fed9196
|
|
BLAKE2b-256 checksum How to use checksums |
b662856b141abf19985061a5ced485bd9cd78c9f0e6a29e3a4a2f438ece43e4e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for gravier 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.