Skip to main content

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 concrete T on 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.

  • SSEsse(event, data) formatting and ready-made SSE_HEADERS; a handler returning None keeps 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"}))
    
  • OpenAPIbuild_openapi(router, title=...) generates a 3.1 spec from Query/Body/return annotations; openapi_handler(router, title=...) serves it.

  • Testing (gravier.testing) — FakeScope/FakeProto for direct handler tests (including captured streams), and ASGIBridge to 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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gravier-0.1.0.tar.gz (11.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gravier-0.1.0-py3-none-any.whl (14.5 kB view details)

Uploaded Python 3

File details

Details for the file gravier-0.1.0.tar.gz.

File metadata

  • Download URL: gravier-0.1.0.tar.gz
  • Upload date:
  • Size: 11.8 kB
  • Tags: Source
  • Uploaded using 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}

File hashes

Hashes for gravier-0.1.0.tar.gz
Algorithm Hash digest
SHA256 180958d5962bcf2544be4ba86d3dbefe1ca29fa8bceb5158dd7605b7a075a8f1
MD5 26385b7fbbed53fb9c4df785cb5e13a5
BLAKE2b-256 e4b8caf67ecfdce96242e1af865ea500e8ddad08d06becc6160e5668756de68d

See more details on using hashes here.

File details

Details for the file gravier-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: gravier-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 14.5 kB
  • Tags: Python 3
  • Uploaded using 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}

File hashes

Hashes for gravier-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5458a888cf1f8464e9bb2c3252c03742302b9e6abdeb197aff9d596e3fed9196
MD5 5819ff9196eabb6d563ea17d836d3d2c
BLAKE2b-256 b662856b141abf19985061a5ced485bd9cd78c9f0e6a29e3a4a2f438ece43e4e

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page