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.

  • SSE — sse(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"}))
    
  • OpenAPI — build_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

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)

Source distribution for gravier 0.1.0
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)

Table of built distributions (wheels) for gravier 0.1.0
File Interpreter ABI Platform Actions
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.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

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