pygrpc-native
A gRPC server for Python that does the HTTP/2 and gRPC work in Rust, outside the GIL.
Your handlers stay in Python.
When a Python gRPC server runs short of CPU, the usual answers are more server processes or a rewrite in another language. pygrpc-native moves the gRPC work itself (HTTP/2, flow control, deadlines, cancellation) into Rust, outside the GIL, and leaves your servicers, interceptors and generated code as they are.
- Switching is one line.
pygrpc_native.server()andpygrpc_native.aio.server()take the same arguments as grpcio's servers. Clients don't change. - It behaves like grpcio. Status codes, metadata, deadlines,
cancellation, interceptors and
ServicerContextwork the same way. The few differences are listed below, and anything else is a bug. - Nothing stops working. Methods it can't serve itself, such as bidirectional streaming or server reflection, are passed to a grpcio server running behind it.
- It's faster under load. With an empty handler it answers about 1.5x as many calls per second as grpcio from one client thread, and about 5x from 32. See Benchmarks.
Quick start
pip install pygrpc-native
Wheels are available for Linux (x86_64, aarch64) and macOS (arm64, x86_64), for CPython 3.10 and later.
Change the line that creates the server:
from concurrent import futures
-import grpc
+import pygrpc_native
-server = grpc.server(futures.ThreadPoolExecutor(16))
+server = pygrpc_native.server(futures.ThreadPoolExecutor(16))
greeter_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
server.add_insecure_port("[::]:50051")
server.start()
server.wait_for_termination()
For grpc.aio:
+import pygrpc_native.aio
+
-server = grpc.aio.server()
+server = pygrpc_native.aio.server()
greeter_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
server.add_insecure_port("[::]:50051")
await server.start()
await server.wait_for_termination()
To build from source:
pip install maturin
maturin develop --release
How it works
Every call reaches Python as an event: an id, the method, the request bytes and who sent it.
grpc client ──HTTP/2──▶ tokio threads (no GIL)
│ path → method id
▼
event queue ──▶ recv_batch() in Python
◀── respond(call_id, bytes) from any thread
Python picks up calls in batches, so a burst costs one trip across the GIL
instead of one per call. server() and aio.server() are built on top of
this and run your handlers the way grpcio does.
What runs natively
| Native | grpcio fallback | |
|---|---|---|
| Unary and server-streaming methods | ✓ | |
| Client-streaming and bidirectional methods | ✓ | |
Code generated by any grpcio-tools, and handlers added with add_generic_rpc_handlers |
✓ | |
| Server reflection | ✓ | |
| Interceptors | ✓ | ✓ |
The fallback is a grpcio server listening on a Unix socket behind this one.
Calls that go there work but aren't any faster, and peer() in them returns
the socket. The native server listens on a single insecure port.
Server options
| Options | |
|---|---|
| Same as grpcio | grpc.max_receive_message_length, grpc.max_send_message_length, grpc.keepalive_time_ms, grpc.keepalive_timeout_ms, grpc.keepalive_permit_without_calls, grpc.max_concurrent_streams, grpc.max_metadata_size, grpc.absolute_max_metadata_size, grpc.so_reuseport, maximum_concurrent_rpcs |
| Raise an error | grpc.max_connection_age_ms, grpc.max_connection_age_grace_ms, grpc.max_connection_idle_ms, grpc.max_allowed_incoming_connections, response compression, TLS |
| Ignored | Everything else. grpcio also ignores the many options that only clients read. This includes the limits on client pings, which are accepted but not enforced, and grpc.http2.max_frame_size, which grpc-core's flow control overrides anyway. |
As in grpcio, a value that isn't an integer is ignored.
Known differences from grpcio
- If a unary handler calls
send_initial_metadata()and then fails, the metadata is sent along with the status, so the client sees it as trailing metadata. - If a client doesn't answer a keepalive ping, the connection is closed after
grpc.keepalive_timeout_ms, as gRPC's keepalive docs describe. grpcio waits forgrpc.http2.ping_timeout_msinstead, which is a minute by default.
Notes on aio.server()
- Handlers run on the event loop that called
start(), including uvloop. abort()raisesgrpc.aio.AbortError. If the client cancels or the deadline passes, the handler getsasyncio.CancelledError, as in grpc.aio.- Streaming handlers can yield from an async generator or call
context.write(). - Handlers that aren't coroutines run in
migration_thread_poolwith grpcio's sync context, whereabort()returns instead of raising. - The fallback, the options and the differences above are the same as for the sync server.
Low-level API
NativeGrpcFront is the layer under server(). You receive each call as an
event and answer it by id:
import threading
from pygrpc_native import EVENT_CALL, NativeGrpcFront
front = NativeGrpcFront("0.0.0.0:50051", [("/echo.Echo/Say", False)])
front.start()
def serve():
while (events := front.recv_batch(64)) is not None:
for kind, call_id, method_id, payload, client_key in events:
if kind == EVENT_CALL:
front.respond(call_id, payload)
threading.Thread(target=serve).start()
Streaming, call details, response metadata, bearer tokens and metrics are covered in docs/low-level-api.md.
Benchmarks
An empty unary handler on an Apple M4 Pro, called by grpcio clients on the same machine. Calls per second, median of three runs:
| Server | 1 client thread | 32 client threads (8 processes x 4) |
|---|---|---|
grpc.server() |
7,739 | 10,955 |
grpc.aio.server() |
4,804 | 10,322 |
pygrpc_native.server() |
11,428 | 57,419 |
pygrpc_native.aio.server() |
9,444 | 58,073 |
| Low-level API | 12,835 | 63,413 |
On a 40-vCPU Linux container, the low-level API answered about 12x as many calls as grpcio at 32 threads. The benchmark README has all the numbers and explains how they were measured.
Limitations
NativeGrpcFrontonly serves unary and server-streaming methods. Other kinds can go through the fallback server, andserver()andaio.server()send them there for you.- There's no TLS yet.
NativeGrpcFrontchecks bearer tokens itself, whileserver()andaio.server()run grpcio interceptors. - As with grpc-core, binding a Unix socket path replaces any socket file already there.
SO_REUSEPORTis set only whengrpc.so_reuseportis 1. grpcio sets it by default, so binding a port that another server already uses works with grpcio but fails here.- Each connection allows up to 1024 concurrent streams unless
grpc.max_concurrent_streamssays otherwise. A connection that hasn't finished the HTTP/2 preface within 120 s is dropped. maximum_concurrent_rpcscounts a call to a method on the fallback server only while its handler runs, while grpcio also counts calls waiting for a worker. Withserver(), such a call over the limit is refused when a worker picks it up rather than as it arrives.- A method that only generic handlers know is looked up when it is first
called, so their
service()runs once more for that call. A method none of them knows is looked up on each call before it goes to the fallback server. - Calls sent back to back on one connection can reach handlers out of order.
gRPC doesn't guarantee an order across calls, though grpcio usually keeps
it.
NativeGrpcFront(ordered=True)keeps it. - A call whose request metadata is over the limit (16 KiB unless set) is
refused. Between
grpc.max_metadata_sizeand that limit, grpcio also refuses some calls at random. This server doesn't.
Compatibility
pygrpc-native follows semantic versioning, and the low-level API also stays compatible within 0.x:
- The 0.1 names, signatures and event tuples of
NativeGrpcFrontwon't change or go away.tests/test_api.pypins them. New parameters are only added after the existing ones, with a default. - The keys that
metrics()returns won't be renamed or removed within 0.x, though new ones may be added. server()andaio.server()follow grpcio's API. Any difference from grpcio's behavior is a bug unless this README lists it.
Changes are listed in CHANGELOG.md.
Contributing
See CONTRIBUTING.md.
Every commit needs a DCO sign-off (git commit -s).
License
Apache-2.0
Metadata
Release files for pygrpc-native 0.7.1
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 |
|---|---|---|---|---|
| pygrpc_native-0.7.1.tar.gz | 225.8 kB | With provenance | Details for pygrpc_native-0.7.1.tar.gz |
Built distributions (wheels)
| File | Actions Reset | |||
|---|---|---|---|---|
| pygrpc_native-0.7.1-cp310-abi3-manylinux_2_28_x86_64.whl | CPython 3.10 | abi3 | Linux glibc 2.28+ x86-64 | Details for pygrpc_native-0.7.1-cp310-abi3-manylinux_2_28_x86_64.whl |
| pygrpc_native-0.7.1-cp310-abi3-manylinux_2_28_aarch64.whl | CPython 3.10 | abi3 | Linux glibc 2.28+ ARM64 | Details for pygrpc_native-0.7.1-cp310-abi3-manylinux_2_28_aarch64.whl |
| pygrpc_native-0.7.1-cp310-abi3-macosx_11_0_arm64.whl | CPython 3.10 | abi3 | macOS 11.0+ ARM64 | Details for pygrpc_native-0.7.1-cp310-abi3-macosx_11_0_arm64.whl |
| pygrpc_native-0.7.1-cp310-abi3-macosx_10_12_x86_64.whl | CPython 3.10 | abi3 | macOS 10.12+ x86-64 | Details for pygrpc_native-0.7.1-cp310-abi3-macosx_10_12_x86_64.whl |
Total release size: 4.7 MB
Release files / pygrpc_native-0.7.1.tar.gz
| Download URL | pygrpc_native-0.7.1.tar.gz |
|---|---|
| Size | 225.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f1b9b4a66386310bf1d8e5d1ed0cdc2f4f866f8e4c311d9edbd85b2ce8ca9b21
|
|
BLAKE2b-256 checksum How to use checksums |
ffeb440c1c039549ae719a335ed6b0d8d3cd54618106a719d1d026d2ea65716e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.
Transparency logRelease files / pygrpc_native-0.7.1-cp310-abi3-manylinux_2_28_x86_64.whl
| Download URL | pygrpc_native-0.7.1-cp310-abi3-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 1.2 MB |
| Tags | CPython 3.10 Linux glibc 2.28+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
d158deff3abb29386adbc1121467c5df8cbf3e27767175b329f63ec82582aa09
|
|
BLAKE2b-256 checksum How to use checksums |
e6dd30a313c468e722f7363b884e38725687b9b8ed6aae229c189debf1402130
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.
Transparency logRelease files / pygrpc_native-0.7.1-cp310-abi3-manylinux_2_28_aarch64.whl
| Download URL | pygrpc_native-0.7.1-cp310-abi3-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 1.2 MB |
| Tags | CPython 3.10 Linux glibc 2.28+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
9dacefc05f4a8cb2b3587a05fe66bb46727d0904a78fa5b9fd20660ef4a5955c
|
|
BLAKE2b-256 checksum How to use checksums |
61a1dd6bfebe745d724c9ecfa3df4ae8d91370871d3b7036d2c37a95710fc0be
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.
Transparency logRelease files / pygrpc_native-0.7.1-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | pygrpc_native-0.7.1-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.1 MB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
570ffcc20e3f1ec7141ee00d4088829783011749ffa3474a88d63efe85565b55
|
|
BLAKE2b-256 checksum How to use checksums |
32dfde10b5a6e74cb3d17ccfed22c3127940c00203ae62df9366544c3fbe1427
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.
Transparency logRelease files / pygrpc_native-0.7.1-cp310-abi3-macosx_10_12_x86_64.whl
| Download URL | pygrpc_native-0.7.1-cp310-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 1.1 MB |
| Tags | CPython 3.10 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
cba41529309b4a44ba9b3d9ab9d7e040fd2f810958825a5fbe125127bdd5300c
|
|
BLAKE2b-256 checksum How to use checksums |
a5148f5cb672a5abb651872e4c3f4ea05a225a6a76a4fcae1ffd793ff80900d4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.
Transparency logSecurity for pygrpc-native 0.7.1
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.
Full provenance
All 5 files in this release have been uploaded with provenance metadata.
Attestation for pygrpc_native-0.7.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.
Transparency logA 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.