Skip to content

v1.3.0

Choose a tag to compare

@chaokunyang chaokunyang released this 25 Jun 03:33

Highlights

  • Python gRPC code generation now defaults to the grpc.aio AsyncIO API, while synchronous grpcio output remains available through --grpc-python-mode=sync.
  • Dart joins the generated gRPC service surface: foryc --dart_out=... --grpc now emits package:grpc clients, service bases, method descriptors, and Fory-backed payload serialization.
  • Compiler gRPC documentation was refined across languages, including clearer guidance for generated service dependencies and transport behavior.
  • Runtime hardening continued with remote schema metadata limits and Java aligned-varint/type-checker fixes.

Python Async gRPC Mode

Python gRPC generation now targets AsyncIO by default. Generated companions use
grpc.aio: servicer bases expose async def methods, stubs are used with
grpc.aio.Channel instances, and streaming RPCs use async iterables. This keeps
the generated code aligned with modern Python async services while preserving
the same Fory-backed request and response encoding used by the existing gRPC
support.

Generate the default async companion with:

foryc service.fdl --python_out=./generated/python --grpc

For a simple unary service, the generated async server shape is:

import asyncio

import grpc.aio

import demo_greeter
import demo_greeter_grpc


class Greeter(demo_greeter_grpc.GreeterServicer):
    async def say_hello(self, request, context):
        return demo_greeter.HelloReply(reply=f"Hello, {request.name}")


async def serve():
    server = grpc.aio.server()
    demo_greeter_grpc.add_servicer(Greeter(), server)
    server.add_insecure_port("[::]:50051")
    await server.start()
    await server.wait_for_termination()


asyncio.run(serve())

Clients use a grpc.aio channel and await generated stub methods:

import grpc
import grpc.aio

import demo_greeter
import demo_greeter_grpc


credentials = grpc.ssl_channel_credentials()
async with grpc.aio.secure_channel("api.example.com:443", credentials) as channel:
    stub = demo_greeter_grpc.GreeterStub(channel)
    reply = await stub.say_hello(demo_greeter.HelloRequest(name="Fory"))

Existing synchronous applications can still request sync companions explicitly:

foryc service.fdl --python_out=./generated/python --grpc --grpc-python-mode=sync

In sync mode the generated public names and <module>_grpc.py filename stay the
same, but applications use grpc.server(...), standard grpc.Channel
instances, and regular def servicer methods.

Dart gRPC Code Generation

Fory 1.3.0 adds Dart gRPC service generation for schemas with service
definitions. Service definitions can come from Fory IDL, protobuf IDL, or
FlatBuffers rpc_service definitions. The generated code uses normal
grpc-dart APIs for clients, service bases, method descriptors, call options,
deadlines, cancellations, metadata, and status codes, while each request and
response object is serialized with Fory instead of protobuf message bytes.

Add grpc and build_runner alongside the Fory package in the Dart
application:

dependencies:
  fory: ^1.3.0
  grpc: ^4.0.0

dev_dependencies:
  build_runner: ^2.4.0

Generate Dart models and the gRPC companion with:

foryc service.fdl --dart_out=./lib/generated --grpc
dart run build_runner build --delete-conflicting-outputs

For a demo.greeter package, the generator emits the model file, the
build_runner serializer part, and a <stem>_grpc.dart companion with
GreeterServiceBase and GreeterClient. The generated client and service base
install the schema's Fory module automatically on first use, so service
implementations do not need a separate manual registration step for the
generated message types.

A unary Dart server uses grpc-dart's Server and the generated service base:

import 'dart:io';

import 'package:grpc/grpc.dart';
import 'demo/greeter/greeter.dart';
import 'demo/greeter/greeter_grpc.dart';

class GreeterService extends GreeterServiceBase {
  @override
  Future<HelloReply> sayHello(ServiceCall call, HelloRequest request) async {
    return HelloReply()..reply = 'Hello, ${request.name}';
  }
}

Future<void> main() async {
  final server = Server.create(services: [GreeterService()]);
  await server.serve(address: InternetAddress.loopbackIPv4, port: 50051);
}

Generated Dart clients use standard ClientChannel values and return the
grpc-dart call types:

import 'package:grpc/grpc.dart';
import 'demo/greeter/greeter.dart';
import 'demo/greeter/greeter_grpc.dart';

final channel = ClientChannel(
  'localhost',
  port: 50051,
  options: const ChannelOptions(credentials: ChannelCredentials.insecure()),
);
final client = GreeterClient(channel);

final reply = await client.sayHello(HelloRequest()..name = 'Fory');
await channel.shutdown();

Dart generation covers unary, server-streaming, client-streaming, and
bidirectional streaming RPC shapes following grpc-dart conventions.

Features

Bug Fix

Other Improvements

New Contributors

Full Changelog: v1.2.0...v1.3.0