[gRPC] Update the code generator for Python to produce typed handlers (#8326)

* Move `namer.h` and `idl_namer.h` to `include/codegen` so they can be reused from `grpc` dirqectory.

* [gRPC] Update the Python generator to produce typed handlers and Python stubs if requested.

* [gRPC] Document the newly added compiler flags.
This commit is contained in:
Anton Bobukh
2024-06-18 16:02:57 -07:00
committed by GitHub
parent dafd2f1f29
commit fb9afbafc7
22 changed files with 1177 additions and 665 deletions

View File

@@ -27,6 +27,7 @@ ${test_dir}/../flatc -p -o ${gen_code_path} -I include_test monster_test.fbs --g
${test_dir}/../flatc -p -o ${gen_code_path} -I include_test monster_extra.fbs --gen-object-api --python-typing --gen-compare
${test_dir}/../flatc -p -o ${gen_code_path} -I include_test arrays_test.fbs --gen-object-api --python-typing
${test_dir}/../flatc -p -o ${gen_code_path} -I include_test nested_union_test.fbs --gen-object-api --python-typing
${test_dir}/../flatc -p -o ${gen_code_path} -I include_test service_test.fbs --grpc --grpc-python-typed-handlers --python-typing --no-python-gen-numpy --gen-onefile
# Syntax: run_tests <interpreter> <benchmark vtable dedupes>
# <benchmark read count> <benchmark build count>

11
tests/service_test.fbs Normal file
View File

@@ -0,0 +1,11 @@
namespace example;
table HelloRequest {}
table HelloResponse {}
rpc_service HelloService {
Hello(HelloRequest):HelloResponse;
StreamClient(HelloRequest):HelloResponse (streaming: "client");
StreamServer(HelloRequest):HelloResponse (streaming: "server");
Stream(HelloRequest):HelloResponse (streaming: "bidi");
}

View File

@@ -0,0 +1,58 @@
# automatically generated by the FlatBuffers compiler, do not modify
# namespace: example
import flatbuffers
from typing import Any
class HelloRequest(object):
__slots__ = ['_tab']
@classmethod
def GetRootAs(cls, buf, offset: int = 0):
n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset)
x = HelloRequest()
x.Init(buf, n + offset)
return x
@classmethod
def GetRootAsHelloRequest(cls, buf, offset=0):
"""This method is deprecated. Please switch to GetRootAs."""
return cls.GetRootAs(buf, offset)
# HelloRequest
def Init(self, buf: bytes, pos: int):
self._tab = flatbuffers.table.Table(buf, pos)
def HelloRequestStart(builder: flatbuffers.Builder):
builder.StartObject(0)
def HelloRequestEnd(builder: flatbuffers.Builder) -> int:
return builder.EndObject()
class HelloResponse(object):
__slots__ = ['_tab']
@classmethod
def GetRootAs(cls, buf, offset: int = 0):
n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset)
x = HelloResponse()
x.Init(buf, n + offset)
return x
@classmethod
def GetRootAsHelloResponse(cls, buf, offset=0):
"""This method is deprecated. Please switch to GetRootAs."""
return cls.GetRootAs(buf, offset)
# HelloResponse
def Init(self, buf: bytes, pos: int):
self._tab = flatbuffers.table.Table(buf, pos)
def HelloResponseStart(builder: flatbuffers.Builder):
builder.StartObject(0)
def HelloResponseEnd(builder: flatbuffers.Builder) -> int:
return builder.EndObject()

View File

@@ -0,0 +1,26 @@
from __future__ import annotations
import flatbuffers
import flatbuffers
import typing
uoffset: typing.TypeAlias = flatbuffers.number_types.UOffsetTFlags.py_type
class HelloRequest(object):
@classmethod
def GetRootAs(cls, buf: bytes, offset: int) -> HelloRequest: ...
@classmethod
def GetRootAsHelloRequest(cls, buf: bytes, offset: int) -> HelloRequest: ...
def Init(self, buf: bytes, pos: int) -> None: ...
def HelloRequestStart(builder: flatbuffers.Builder) -> None: ...
def HelloRequestEnd(builder: flatbuffers.Builder) -> uoffset: ...
class HelloResponse(object):
@classmethod
def GetRootAs(cls, buf: bytes, offset: int) -> HelloResponse: ...
@classmethod
def GetRootAsHelloResponse(cls, buf: bytes, offset: int) -> HelloResponse: ...
def Init(self, buf: bytes, pos: int) -> None: ...
def HelloResponseStart(builder: flatbuffers.Builder) -> None: ...
def HelloResponseEnd(builder: flatbuffers.Builder) -> uoffset: ...

View File

@@ -0,0 +1,97 @@
# Generated by the gRPC FlatBuffers compiler. DO NOT EDIT!
import flatbuffers
import grpc
from service_test_generated import HelloRequest, HelloResponse
def _serialize_to_bytes(table):
buf = table._tab.Bytes
n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, 0)
if table._tab.Pos != n:
raise ValueError('must be a top-level table')
return bytes(buf)
class HelloServiceStub(object):
'''Interface exported by the server.'''
def __init__(self, channel):
'''Constructor.
Args:
channel: A grpc.Channel.
'''
self.Hello = channel.unary_unary(
method='/example.HelloService/Hello',
request_serializer=_serialize_to_bytes,
response_deserializer=HelloResponse.GetRootAs)
self.StreamClient = channel.stream_unary(
method='/example.HelloService/StreamClient',
request_serializer=_serialize_to_bytes,
response_deserializer=HelloResponse.GetRootAs)
self.StreamServer = channel.unary_stream(
method='/example.HelloService/StreamServer',
request_serializer=_serialize_to_bytes,
response_deserializer=HelloResponse.GetRootAs)
self.Stream = channel.stream_stream(
method='/example.HelloService/Stream',
request_serializer=_serialize_to_bytes,
response_deserializer=HelloResponse.GetRootAs)
class HelloServiceServicer(object):
'''Interface exported by the server.'''
def Hello(self, request, context):
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def StreamClient(self, request_iterator, context):
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def StreamServer(self, request, context):
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def Stream(self, request_iterator, context):
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_HelloServiceServicer_to_server(servicer, server):
rpc_method_handlers = {
'Hello': grpc.unary_unary_rpc_method_handler(
servicer.Hello,
request_deserializer=HelloRequest.GetRootAs,
response_serializer=_serialize_to_bytes),
'StreamClient': grpc.stream_unary_rpc_method_handler(
servicer.StreamClient,
request_deserializer=HelloRequest.GetRootAs,
response_serializer=_serialize_to_bytes),
'StreamServer': grpc.unary_stream_rpc_method_handler(
servicer.StreamServer,
request_deserializer=HelloRequest.GetRootAs,
response_serializer=_serialize_to_bytes),
'Stream': grpc.stream_stream_rpc_method_handler(
servicer.Stream,
request_deserializer=HelloRequest.GetRootAs,
response_serializer=_serialize_to_bytes),
}
generic_handler = grpc.method_handlers_generic_handler(
'example.HelloService', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))

View File

@@ -0,0 +1,27 @@
# Generated by the gRPC FlatBuffers compiler. DO NOT EDIT!
from __future__ import annotations
import grpc
import typing
from service_test_generated import HelloRequest, HelloResponse
class HelloServiceStub(object):
def __init__(self, channel: grpc.Channel) -> None: ...
def Hello(self, request: HelloRequest) -> HelloResponse: ...
def StreamClient(self, request_iterator: typing.Iterator[HelloRequest]) -> HelloResponse: ...
def StreamServer(self, request: HelloRequest) -> typing.Iterator[HelloResponse]: ...
def Stream(self, request_iterator: typing.Iterator[HelloRequest]) -> typing.Iterator[HelloResponse]: ...
class HelloServiceServicer(object):
def Hello(self, request: HelloRequest, context: grpc.ServicerContext) -> HelloResponse: ...
def StreamClient(self, request_iterator: typing.Iterator[HelloRequest], context: grpc.ServicerContext) -> HelloResponse: ...
def StreamServer(self, request: HelloRequest, context: grpc.ServicerContext) -> typing.Iterator[HelloResponse]: ...
def Stream(self, request_iterator: typing.Iterator[HelloRequest], context: grpc.ServicerContext) -> typing.Iterator[HelloResponse]: ...
def add_HelloServiceServicer_to_server(servicer: HelloServiceServicer, server: grpc.Server) -> None: ...