title: gRPC Support sidebar_position: 13 id: grpc_support license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Fory can generate Python gRPC service companions for schemas that define services. The generated modules use grpcio for transport and use Fory to serialize request and response objects.
Use this mode when every RPC peer is generated from the same Fory IDL, protobuf IDL, or FlatBuffers IDL and you want gRPC transport semantics with Fory payload encoding. Use standard protobuf gRPC code generation when clients or tools must consume protobuf message bytes directly.
Generated Python companions currently target the synchronous grpcio API. Use regular def servicer methods, grpc.server(...), standard grpc.Channel instances, and Python iterators or generators for streaming RPCs. The generated stub accepts any channel configured by your application. The compiler does not generate grpc.aio stubs or service bases, so do not implement generated servicer methods as async def unless you add a custom adapter outside the generated companion. Python gRPC async support based on grpc.aio will be available in the next Fory release.
Install grpcio alongside pyfory. The generated companion imports grpc, but pyfory does not add gRPC as a hard dependency.
pip install pyfory grpcio
Service definitions can come from Fory IDL, protobuf IDL, or FlatBuffers rpc_service definitions. A Fory IDL service looks like this:
package demo.greeter; message HelloRequest { string name = 1; } message HelloReply { string reply = 1; } service Greeter { rpc SayHello (HelloRequest) returns (HelloReply); }
Generate Python model and gRPC companion code with --grpc:
foryc service.fdl --python_out=./generated/python --grpc
For this schema, the Python generator emits:
| File | Purpose |
|---|---|
demo_greeter.py | Fory dataclasses and registration helpers |
demo_greeter_grpc.py | grpcio stub, servicer base, and registrar |
The module name is derived from the Fory package by replacing dots with underscores. A schema with no package uses generated.py and generated_grpc.py.
Subclass the generated servicer and register it with a normal grpcio server. Generated Python method names use snake_case, while the gRPC wire path keeps the original IDL method name.
from concurrent import futures import grpc import demo_greeter import demo_greeter_grpc class Greeter(demo_greeter_grpc.GreeterServicer): def say_hello(self, request, context): return demo_greeter.HelloReply(reply=f"Hello, {request.name}") def serve(): server = grpc.server(futures.ThreadPoolExecutor(max_workers=8)) demo_greeter_grpc.add_servicer(Greeter(), server) server.add_insecure_port("[::]:50051") server.start() server.wait_for_termination() if __name__ == "__main__": serve()
Generated request and response types are serialized by the generated companion, so service implementations do not perform manual Fory registration.
Use the generated stub with a normal grpcio channel. Production clients usually pass a TLS/auth-configured channel:
import grpc import demo_greeter import demo_greeter_grpc def main(): credentials = grpc.ssl_channel_credentials() with grpc.secure_channel("api.example.com:443", credentials) as channel: stub = demo_greeter_grpc.GreeterStub(channel) reply = stub.say_hello(demo_greeter.HelloRequest(name="Fory")) print(reply.reply) if __name__ == "__main__": main()
For local tests and development, an insecure channel can be used explicitly:
# Test-only channel. Use a TLS/auth-configured grpc.Channel in production. with grpc.insecure_channel("localhost:50051") as channel: stub = demo_greeter_grpc.GreeterStub(channel)
grpcio still owns channel options, credentials, deadlines, metadata, retries, and interceptors.
Fory service definitions can use unary, server-streaming, client-streaming, and bidirectional streaming RPC shapes:
service Greeter { rpc SayHello (HelloRequest) returns (HelloReply); rpc LotsOfReplies (HelloRequest) returns (stream HelloReply); rpc LotsOfGreetings (stream HelloRequest) returns (HelloReply); rpc Chat (stream HelloRequest) returns (stream HelloReply); }
Generated Python code follows grpcio conventions:
| IDL shape | Servicer method shape | Stub method shape |
|---|---|---|
rpc A (Req) returns (Res) | returns one response object | returns one response object |
rpc A (Req) returns (stream Res) | yields response objects | returns an iterator of responses |
rpc A (stream Req) returns (Res) | consumes an iterator and returns a response | accepts an iterator of requests |
rpc A (stream Req) returns (stream Res) | consumes and yields iterators | accepts and returns iterators |
Servicer methods use snake_case names, while generated descriptors preserve the exact IDL service and method names for the gRPC path.
Server implementations can use Python iterators directly:
class Greeter(demo_greeter_grpc.GreeterServicer): def lots_of_replies(self, request, context): yield demo_greeter.HelloReply(reply=f"Hello, {request.name}") yield demo_greeter.HelloReply(reply=f"Welcome, {request.name}") def lots_of_greetings(self, request_iterator, context): names = [request.name for request in request_iterator] return demo_greeter.HelloReply(reply=", ".join(names)) def chat(self, request_iterator, context): for request in request_iterator: yield demo_greeter.HelloReply(reply=f"Hello, {request.name}")
Generated clients use the standard grpcio streaming call shapes:
credentials = grpc.ssl_channel_credentials() with grpc.secure_channel("api.example.com:443", credentials) as channel: stub = demo_greeter_grpc.GreeterStub(channel) for reply in stub.lots_of_replies( demo_greeter.HelloRequest(name="Fory") ): print(reply.reply) def greeting_requests(): yield demo_greeter.HelloRequest(name="Ada") yield demo_greeter.HelloRequest(name="Grace") summary = stub.lots_of_greetings(greeting_requests()) print(summary.reply) def chat_requests(): yield demo_greeter.HelloRequest(name="Fory") yield demo_greeter.HelloRequest(name="RPC") for reply in stub.chat(chat_requests()): print(reply.reply)
The generated service companion only supplies Fory serialization callbacks. Operational behavior remains standard grpcio behavior:
ModuleNotFoundError: No module named 'grpc'Install grpcio in the environment that runs the generated service module:
pip install grpcio
TypeError: Unsupported gRPC servicer typePass an instance of the generated servicer subclass to demo_greeter_grpc.add_servicer(...). If the schema contains multiple services, the generated registrar accepts only the matching generated servicer types.
UNIMPLEMENTEDConfirm that the generated servicer was registered with the server, and that the client and server were generated from the same package, service, and method names.
Fory gRPC companions do not use protobuf wire encoding for messages. Use a Fory-generated client for Fory-generated services, or provide a separate protobuf service endpoint for generic protobuf clients.