Skip to main content

A type-safe RPC bridge between Python and TypeScript. The function signature is the contract.

Project description

dyadpy (Python)

A type-safe RPC bridge between Python and TypeScript.

uv add dyadpy

This is the Python half of Dyadpy. It ships:

  • A thin ASGI framework (dyadpy.App) that uses your function signatures as the contract — no separate Pydantic models declared above the handler.
  • A type extractor that walks inspect.signature + typing.get_type_hints, normalizes through msgspec's native JSON Schema export, and produces a canonical IR.
  • A codegen that turns the IR into a single client.ts for your frontend.
  • A CLI (dyadpy dev, dyadpy build, dyadpy codegen, dyadpy init) that runs the whole loop in one process.

For the full story, the design rationale, and a side-by-side comparison vs. FastAPI + openapi-typescript / tRPC / Encore.ts / Connect-RPC, see the repo README.

30-second example

from dyadpy import App, stream, raises
from dataclasses import dataclass
import msgspec

app = App()

class CreatePost(msgspec.Struct):
    title: str
    body: str

class Post(msgspec.Struct):
    id: int
    title: str
    body: str

@dataclass
class PostNotFound(Exception):
    post_id: int

@app.post("/posts")
async def create_post(data: CreatePost) -> Post: ...

@app.get("/posts/{post_id}")
@raises(PostNotFound)
async def get_post(post_id: int) -> Post: ...

class Tick(msgspec.Struct, tag="tick"):
    seq: int

@app.get("/ticks")
async def ticks(count: int) -> stream[Tick]:
    for i in range(count):
        yield Tick(seq=i)

Run it:

dyadpy dev server.app:app

The watcher writes frontend/src/lib/dyadpy/client.ts automatically. Then in your frontend:

import { api } from "@/lib/dyadpy/client";

const post = await api.createPost({ data: { title: "hi", body: "world" } });

for await (const ev of api.ticks({ count: 10 })) {
  /* typed */
}

Primitives in this package

Primitive Purpose
App + @app.{get,post,put,patch,delete} Route decorators.
Annotated[T, Body / Query / Path / Header / Cookie / File / Form] Parameter location markers.
Annotated[list[T], Query()] Repeated query params (?tag=a&tag=b).
Bytes Raw request / response bodies. Skips the JSON envelope.
stream[T] Typed SSE — client gets AsyncIterable<T>.
@raises(E1, E2, …) Typed error union → Result<T, E1 | E2> on the TS side.
Context.set_status / set_header / set_cookie / after Shape the response without dropping to Starlette.
Depends(provider) DI in the FastAPI shape.
after(fn, …) Run a callback after the response is sent.
InMemoryBackend + TaskBackend Protocol Background jobs.
dyadpy.otel.instrument(app) One OpenTelemetry span per request (dyadpy[otel]).
dyadpy openapi / swift / kotlin (CLI) Emit OpenAPI 3.1, Swift, or Kotlin clients off the same IR.

Full reference: https://github.com/tamimbinhakim/dyadpy/blob/main/docs/reference.md

Optional extras

uv add 'dyadpy[pydantic]'  # Pydantic plugin (model_validate + model_json_schema)
uv add 'dyadpy[otel]'      # OpenTelemetry middleware
uv add 'dyadpy[all]'       # everything

Scope

Dyadpy ships at the wire level: RPC, typed streaming, typed errors, cancellation, file uploads, dependency injection. It does not ship vertical integrations — no LLM types, no React hooks in core, no chat-bot primitives. Those layers compose on top of the fundamentals and live in their own packages.

License

MIT

Project details


Download files

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

Source Distribution

dyadpy-0.1.0a0.tar.gz (66.7 kB view details)

Uploaded Source

Built Distribution

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

dyadpy-0.1.0a0-py3-none-any.whl (54.2 kB view details)

Uploaded Python 3

File details

Details for the file dyadpy-0.1.0a0.tar.gz.

File metadata

  • Download URL: dyadpy-0.1.0a0.tar.gz
  • Upload date:
  • Size: 66.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","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

Hashes for dyadpy-0.1.0a0.tar.gz
Algorithm Hash digest
SHA256 a105e59281b49552d3653cadec2bfe7c6ca5c1a1d0eff5a9c3e141382f62536a
MD5 3b0f39464ab18eb70833dbb7004391c6
BLAKE2b-256 73965711295e04dda5cf19701ac955f0367f453dd757dd217590d0d01bff4c54

See more details on using hashes here.

File details

Details for the file dyadpy-0.1.0a0-py3-none-any.whl.

File metadata

  • Download URL: dyadpy-0.1.0a0-py3-none-any.whl
  • Upload date:
  • Size: 54.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","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

Hashes for dyadpy-0.1.0a0-py3-none-any.whl
Algorithm Hash digest
SHA256 d170d4c12671c0d6cf20bce765f0c83f75a9e8cfbbb2ff28328a2d8e857701ec
MD5 ad2629a0f1790afc58689e8f9c0d624e
BLAKE2b-256 73535d1298ade9048c6a680ffb303c87e3ce9f9150b24aab274497bc1c263382

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page