Skip to main content

FastAPI-style gRPC for Python

Project description

fastgrpcpy

FastAPI-style gRPC for Python.
Define services with type-annotated Python. Get the protobuf schema, generated stubs, and a running server — automatically.

Python License Status


Overview

fastgrpcpy treats your Python type hints as the source of truth for a gRPC service. The framework derives the .proto schema from your code, compiles the gRPC stubs, wires your handlers into the generated servicer, and runs the server — sync or async, with hot reload in development.

from dataclasses import dataclass
from fastgrpcpy import rpc, service

@dataclass
class User:
    id: int
    name: str

@service
class UserService:
    @rpc
    async def get_user(self, user_id: int) -> User:
        return User(id=user_id, name="FastgRPC")
fastgrpcpy dev main.py

Call it from any gRPC client. With server reflection enabled in dev mode, grpcurl works with no extra setup:

grpcurl -plaintext -d '{"user_id": 42}' \
  127.0.0.1:50051 fastgrpcpy.UserService/GetUser
# {"id": "42", "name": "Ashesh"}

That single command performs introspection, code generation, compilation, and serving in one step. No .proto files to write or maintain by hand.


Features

  • Zero-boilerplate service definition — decorate a class with @service, decorate methods with @rpc. fastgrpcpy handles the rest.
  • Hot reload in development, including transitive Python imports, powered by a Rust-backed file watcher.
  • Automatic protobuf generation from Python type hints, including nested dataclasses, Optional, list[T], dict[str, T], and AsyncIterator[T].
  • Wire compatibility guarantees — field numbers are pinned in a lock file (.fastgrpcpy.lock), preventing accidental wire-breaking changes.
  • Sync and async servers, auto-selected from your handler signatures (grpc.aio for async, grpc.server for sync).
  • All four streaming modes — unary-unary, unary-stream, stream-unary, stream-stream.
  • gRPC server reflection enabled in dev mode, so grpcurl, Postman, and BloomRPC work without any configuration.
  • Multi-file projects — split services, business logic, and models freely; reload follows imports.

Installation

Requires Python 3.11 or later.

pip install fastgrpcpy

For local development:

pip install -e '.[dev]'

Command-line interface

Command Purpose
fastgrpcpy dev <file> Start the development server with hot reload and reflection
fastgrpcpy run <file> Start the production server (no reload, no reflection)
fastgrpcpy proto <file> Print the generated .proto schema to stdout
fastgrpcpy compile <file> Write the .proto and compiled stubs to a directory

fastgrpcpy dev

The development server selects the appropriate gRPC stack based on your handler signatures:

  • Any async def handler → grpc.aio.server()
  • All def handlers → grpc.server(ThreadPoolExecutor)
  • Mixed sync and async → fails fast with a ValidationError
fastgrpcpy dev examples/complex/main.py
fastgrpcpy dev examples/complex/main.py --host 0.0.0.0 --port 50051

On every file save the framework executes the following pipeline:

  1. The Rust-backed watcher detects a .py change in the project tree.
  2. User modules are evicted from sys.modules so transitive imports reload cleanly.
  3. The entry-point file is re-imported, re-running the @service and @rpc decorators.
  4. The pipeline runs: inspector → field-number lock → validator → proto writer → protoc compiler.
  5. The previous server is gracefully stopped; a new one is built with the rewired handlers.

Reflection is enabled, so grpcurl -plaintext localhost:50051 list works out of the box.

fastgrpcpy run

Production server. No watcher, no reflection.

fastgrpcpy run main.py --host 0.0.0.0 --port 50051

fastgrpcpy proto

Print the generated .proto schema to stdout. Suitable for piping into a clients repository or copying into another build pipeline.

fastgrpcpy proto main.py
fastgrpcpy proto main.py > schemas/user.proto
fastgrpcpy proto main.py --package myco.users

The command consults .fastgrpcpy.lock to keep field numbers stable across runs.

fastgrpcpy compile

Produce a complete client bundle: the .proto source plus pre-compiled _pb2.py and _pb2_grpc.py modules.

fastgrpcpy compile main.py --out ./client/
client/
├── fastgrpcpy.proto
├── fastgrpcpy_pb2.py
└── fastgrpcpy_pb2_grpc.py

The output directory can be checked in to your clients repository or published as its own package.


Watch mode

fastgrpcpy dev runs a continuous build-and-serve loop:

edit main.py / business.py / models.py
        ↓ (Rust file watcher fires)
purge user modules from sys.modules
        ↓
re-import main.py — decorators re-register services
        ↓
inspector → lock → validator → proto writer → protoc compile
        ↓
stop the running server, start a new one with rewired handlers

Behavior worth knowing:

  • Transitive imports reload. Editing business.py or models.py triggers a full rebuild. Without the explicit sys.modules purge, Python would serve cached versions.
  • Framework modules persist. fastgrpcpy, grpc, and google.protobuf are excluded from the purge to keep reload latency low; re-importing the generated grpc stubs every keystroke would be prohibitively slow.
  • .py files trigger reload. Generated .proto and _pb2.py artifacts are filtered out by the watcher to prevent reload loops.
  • Reload errors are isolated. A failed rebuild (syntax error, validation failure, etc.) does not kill the running server; the previous server keeps serving traffic and the error is logged.

Project layout

A typical fastgrpcpy service:

my_service/
├── main.py                    # @service classes
├── business.py                # business logic, imported by main.py
├── models.py                  # dataclasses, imported by both
├── .fastgrpcpy/                 # gitignored, ephemeral build artifacts
│   ├── fastgrpcpy.proto         # generated schema, refreshed every reload
│   ├── fastgrpcpy_pb2.py        # compiled message classes
│   └── fastgrpcpy_pb2_grpc.py   # compiled servicer base + stub
└── .fastgrpcpy.lock             # committed; tracks protobuf field numbers

Everything inside .fastgrpcpy/ is regenerated on every fastgrpcpy dev reload. To export the schema for client teams, use fastgrpcpy proto (stdout) or fastgrpcpy compile --out <dir> (writes a clean bundle wherever you choose).

Field-number stability and .fastgrpcpy.lock

Protobuf identifies fields by number, not by name. If field numbers shift between releases — for example because a new field was inserted in the middle of a dataclass — every existing client will silently misinterpret the wire payload.

.fastgrpcpy.lock is the authoritative record of every field number ever assigned. New fields receive the next available number; removed fields are tombstoned and never reused.

[User]
id = 1
name = 2

[User.removed]
legacy_email = 3   # tombstoned — number 3 is permanently retired

Commit .fastgrpcpy.lock and review changes in pull requests, the same way you would treat a lock file or a database migration history.


Examples

The examples/ directory contains progressively larger services:

Folder Demonstrates
hello_world/ Minimal async service
async_basic/ Async handler with await asyncio.sleep
sync/ Sync handler running on the grpc thread-pool server
complex/ Multi-file project: main.py, business.py, models.py, two services, nested dataclasses
streaming/ Server, client, and bidirectional streaming
middleware/ Interceptor pattern (logging example)
fastgrpcpy dev examples/complex/main.py --port 50065

grpcurl -plaintext 127.0.0.1:50065 list
grpcurl -plaintext -d '{"user_id": 1}' \
  127.0.0.1:50065 fastgrpcpy.UserService/GetUser

Architecture

src/fastgrpcpy/
├── decorators.py        # @service, @rpc, the registry
├── app.py               # App configuration, interceptor registry
├── exceptions.py        # gRPC-status-mapped exception classes
├── codegen/
│   ├── ir.py            # ProtoFile / ProtoMessage / ProtoField (dataclasses)
│   ├── inspector.py     # Python types → IR
│   ├── lock.py          # field-number stability via .fastgrpcpy.lock
│   ├── validator.py     # IR and registry sanity checks
│   ├── proto_writer.py  # IR → .proto source string
│   └── compiler.py      # invokes grpcio-tools / protoc
├── server/
│   ├── runner.py        # build pipeline + lifecycle
│   ├── wiring.py        # servicer synthesis, sync/async detection
│   ├── converter.py     # dataclass ↔ protobuf recursive conversion
│   ├── watcher.py       # watchfiles-based hot reload
│   └── reflection.py    # gRPC server reflection setup
└── cli/
    └── main.py          # Typer CLI: dev, run, proto, compile

The codegen layer is a sequence of pure functions over an in-memory ProtoFile IR. The IR itself is never persisted; only the final .proto and .fastgrpcpy.lock are.


Development

pip install -e '.[dev]'
pytest                # unit + integration tests
ruff check .
mypy src

The test suite includes end-to-end integration tests that spin up a real gRPC server (both sync and async) and exercise it over the wire.


Project status

Alpha. The core pipeline — code generation, lock-file management, sync and async server runtimes, hot reload, multi-file projects — is implemented and covered by tests. Known gaps include TLS support, OpenTelemetry tracing, and HTTP/JSON transcoding.


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

fastgrpcpy-0.1.1.tar.gz (92.9 kB view details)

Uploaded Source

Built Distribution

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

fastgrpcpy-0.1.1-py3-none-any.whl (29.1 kB view details)

Uploaded Python 3

File details

Details for the file fastgrpcpy-0.1.1.tar.gz.

File metadata

  • Download URL: fastgrpcpy-0.1.1.tar.gz
  • Upload date:
  • Size: 92.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fastgrpcpy-0.1.1.tar.gz
Algorithm Hash digest
SHA256 a86c6ca9e45aa6d6f76fc10308c50c7fe6932279b59a0de0507c6a06beb572cf
MD5 79f0ba05446e68530b3fad78b323b8b7
BLAKE2b-256 5fbb87f3682c3e01bc5f21237cc2f856584c1c3ac9511141378e3e70504f2cff

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastgrpcpy-0.1.1.tar.gz:

Publisher: publish.yml on asheshvidyut/fastgrpc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fastgrpcpy-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: fastgrpcpy-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 29.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fastgrpcpy-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c8590752be54687c68b45a86676d3a3aa33600d694c7cb930067f4bbc08e1f4d
MD5 3d690f0bc68c68a342a4604f2dbd666f
BLAKE2b-256 7355d90a0cb5949daee54ca3293c46cbb136e9d8d93b618cb3fa0745703973e0

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastgrpcpy-0.1.1-py3-none-any.whl:

Publisher: publish.yml on asheshvidyut/fastgrpc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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