Skip to main content

pygrpc-native

A gRPC server for Python whose HTTP/2 and gRPC work runs in Rust, outside the GIL. Your handlers stay in Python.

When a Python gRPC server runs out of CPU, the usual fixes are running more server processes or rewriting the servicers in another language. pygrpc-native instead moves the gRPC work itself (framing, flow control, deadlines and cancellation) onto a tokio runtime outside the GIL, and leaves your servicers, interceptors and generated code alone, so Python spends its time on your code. pygrpc_native.server() and pygrpc_native.aio.server() take the same arguments as grpcio's servers, so switching is usually a one-line change, and nothing changes for clients.

Underneath, each 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

Calls reach Python in batches, so a burst takes one trip across the GIL rather than one per call.

Install

pip install pygrpc-native

Wheels are published for Linux (x86_64, aarch64) and macOS (arm64, x86_64) and work on CPython 3.10 and later. To build from a checkout:

pip install maturin
maturin develop --release

With grpcio servicers

pygrpc_native.server() takes the same arguments as grpc.server() and serves servicers from grpcio-tools generated code (1.64 or later) unchanged:

from concurrent import futures
import pygrpc_native

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()

Status codes, error messages, metadata, deadlines, cancellation and ServicerContext behave as they do in grpcio.

Unary and server-streaming methods are served natively on one insecure port. The rest, such as client-streaming and bidirectional methods, server reflection, or handlers added only with add_generic_rpc_handlers, as code generated by grpcio-tools before 1.64 does, go to a grpcio server on a Unix socket behind it. They work, but they aren't any faster, and peer() in them is the socket.

Interceptors run as in grpcio, on the methods served here and on the fallback server. Of the server options, the message size limits, the keepalive settings (grpc.keepalive_time_ms, grpc.keepalive_timeout_ms and grpc.keepalive_permit_without_calls), grpc.max_concurrent_streams and the metadata size limits (grpc.max_metadata_size and grpc.absolute_max_metadata_size) work as in grpcio. Options that ask for something this server can't do raise an error: closing connections by age or idleness (grpc.max_connection_age_ms, grpc.max_connection_age_grace_ms, grpc.max_connection_idle_ms), grpc.max_allowed_incoming_connections and response compression, as do maximum_concurrent_rpcs and TLS. Any other option is ignored, as grpcio ignores the many that only clients read. That covers 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. Values that aren't integers are ignored too, as in grpcio.

There are two known differences. If a unary handler calls send_initial_metadata() and then fails, the metadata goes out with the status, so the client sees it as trailing metadata. And a connection whose client doesn't answer a keepalive ping is closed after grpc.keepalive_timeout_ms, as gRPC's keepalive documentation describes, while grpcio waits for grpc.http2.ping_timeout_ms, a minute by default.

pygrpc_native.aio.server() does the same for grpc.aio.server():

import pygrpc_native.aio

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()

Handlers run on the event loop that called start(), uvloop included. abort() raises grpc.aio.AbortError, and a handler whose client cancels or whose deadline passes gets asyncio.CancelledError, as in grpc.aio. Streams can yield from an async generator or call context.write(), and handlers that aren't coroutines run in migration_thread_pool with grpcio's sync context, where abort() returns instead of raising. The fallback, the unsupported options and the known differences are the same as for the sync server.

Low-level API

NativeGrpcFront is the layer under server(). Each call reaches Python as an event that you answer 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.

Compatibility

pygrpc-native follows semantic versioning, and the low-level API stays compatible within 0.x as well. tests/test_api.py pins the 0.1 names, signatures and event tuples of NativeGrpcFront: they won't change or go away, and new parameters only come after the existing ones, with a default. The keys metrics() returns won't be renamed or removed within 0.x either, though new ones may be added. server() and aio.server() follow grpcio's API, and any difference from grpcio's behavior is a bug unless it is listed in this README. Changes are listed in CHANGELOG.md.

Limitations

  • NativeGrpcFront serves unary and server-streaming methods only; other kinds can go through the fallback server. server() and aio.server() send them there on their own.
  • No TLS yet. NativeGrpcFront checks bearer tokens itself; server() and aio.server() run grpcio interceptors instead.
  • Like grpc-core, binding a Unix socket path replaces any socket file already there.
  • The server sets SO_REUSEPORT only when grpc.so_reuseport is 1. grpcio sets it by default, so binding a port another server already uses succeeds with grpcio but fails here.
  • Per connection: at most 1024 concurrent streams unless grpc.max_concurrent_streams says otherwise. A connection that hasn't finished the HTTP/2 preface after 120 s is dropped.
  • A call with more request metadata than the limit (16 KiB unless set) is refused. grpcio also refuses some calls at random between grpc.max_metadata_size and that limit; this server doesn't.

Benchmarks

With an empty handler on an Apple M4 Pro, pygrpc_native.server() answers about 1.5x the calls per second of grpc.server() from one client thread and about 5x from 32. On a 40-vCPU Linux container the low-level API answered about 12x at 32 threads. See benchmarks for the numbers and how they were measured.

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.5.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pygrpc-native 0.5.1
File Size Uploaded Provenance Actions
pygrpc_native-0.5.1.tar.gz 199.6 kB With provenance Details for pygrpc_native-0.5.1.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for pygrpc-native 0.5.1
File Actions
pygrpc_native-0.5.1-cp310-abi3-manylinux_2_28_x86_64.whl CPython 3.10 abi3 Linux glibc 2.28+ x86-64 Details for pygrpc_native-0.5.1-cp310-abi3-manylinux_2_28_x86_64.whl
pygrpc_native-0.5.1-cp310-abi3-manylinux_2_28_aarch64.whl CPython 3.10 abi3 Linux glibc 2.28+ ARM64 Details for pygrpc_native-0.5.1-cp310-abi3-manylinux_2_28_aarch64.whl
pygrpc_native-0.5.1-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details for pygrpc_native-0.5.1-cp310-abi3-macosx_11_0_arm64.whl
pygrpc_native-0.5.1-cp310-abi3-macosx_10_12_x86_64.whl CPython 3.10 abi3 macOS 10.12+ x86-64 Details for pygrpc_native-0.5.1-cp310-abi3-macosx_10_12_x86_64.whl

Total release size: 4.6 MB

Release files / pygrpc_native-0.5.1.tar.gz

Download URL pygrpc_native-0.5.1.tar.gz
Size 199.6 kB
Tags Source
SHA-256 checksum
How to use checksums
8f36130ba446ec76e5a3dd5bcf9685c74e4b6247c3501aa461e2ff7a534420a2
BLAKE2b-256 checksum
How to use checksums
de2bd040cb168733ce9e3146647d9afae26f16c6bbee5d25c0ead6052e5de094
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 27, 2026.

Transparency log

Release files / pygrpc_native-0.5.1-cp310-abi3-manylinux_2_28_x86_64.whl

Download URL pygrpc_native-0.5.1-cp310-abi3-manylinux_2_28_x86_64.whl
Size 1.1 MB
Tags CPython 3.10 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
872a2db4b665bbadd90b5814b70190b3922083552717538548d7a7c42ff0a31c
BLAKE2b-256 checksum
How to use checksums
3842c774858b7e6e2ba74fed1e6de1121d392ef8cfdd0cb79e2b11af9b3a89df
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 27, 2026.

Transparency log

Release files / pygrpc_native-0.5.1-cp310-abi3-manylinux_2_28_aarch64.whl

Download URL pygrpc_native-0.5.1-cp310-abi3-manylinux_2_28_aarch64.whl
Size 1.1 MB
Tags CPython 3.10 Linux glibc 2.28+ ARM64 abi3
SHA-256 checksum
How to use checksums
7d07a2552563c6b9a43588c1ebcce53fc83efedd106f211de021e937208dbabd
BLAKE2b-256 checksum
How to use checksums
e0f30d82e15093b59a4109271acb98b5fcf6e6515747cd1e66095c97a692ce78
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 27, 2026.

Transparency log

Release files / pygrpc_native-0.5.1-cp310-abi3-macosx_11_0_arm64.whl

Download URL pygrpc_native-0.5.1-cp310-abi3-macosx_11_0_arm64.whl
Size 1.0 MB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
ffbfc3d339c618e8bf8b86c4bfe383ecaa78ba0a6691b6c2525a2353622fa2eb
BLAKE2b-256 checksum
How to use checksums
b462cca411356d5bf0d91756ed69b7fe50d30c30e76713c7bcef9d0205901da8
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 27, 2026.

Transparency log

Release files / pygrpc_native-0.5.1-cp310-abi3-macosx_10_12_x86_64.whl

Download URL pygrpc_native-0.5.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
a27ccd9795d27d68b1daf2ba337dfab6a1044ca6bfb7a43a0995a320b07ffe38
BLAKE2b-256 checksum
How to use checksums
40f7da7bbcff6ad4f90411e3e808a1a94a0cb3043a5232dc3bf7777e0578fdcf
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 27, 2026.

Transparency log

Security for pygrpc-native 0.5.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.

Select from 5 files

Attestation for pygrpc_native-0.5.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 27, 2026.

Transparency log

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.

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