As a user, to use the new implementation of gRCP in the asynchronous version, I will compile by passing a specific flag:
$ python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. proto/echo.proto --aiogrpc
This compiles the stubs, generated with the asynchronous code.
Example of the code generated by the asynchronous version:
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
import grpc
from grpc.aio import AioChannel, AioServer
from proto import echo_pb2 as proto_dot_echo__pb2
class EchoStub(object):
# missing associated documentation comment in .proto file
pass
def __init__(self, channel: AioChannel):
"""Constructor.
Args:
channel: A grpc.aio.AioChannel.
"""
self.Hi = channel.unary_unary(
'/echo.Echo/Hi',
request_serializer=proto_dot_echo__pb2.EchoRequest.SerializeToString,
response_deserializer=proto_dot_echo__pb2.EchoReply.FromString,
)
class EchoServicer(object):
# missing associated documentation comment in .proto file
pass
async def Hi(self, request, context):
# missing associated documentation comment in .proto file
pass
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_EchoServicer_to_server(servicer, server: AioServer):
rpc_method_handlers = {
'Hi': grpc.unary_unary_rpc_method_handler(
servicer.Hi,
request_deserializer=proto_dot_echo__pb2.EchoRequest.FromString,
response_serializer=proto_dot_echo__pb2.EchoReply.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'echo.Echo', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
Main changes that are introduced in this code (when the flag is passed):
- The channel is a different class now (it's an asynchronous channel)
- All callable objects returned by this channel, are asynchronous
(their callable methods are defined as
async def __call__(...)
) - The same for the server (also asynchronous, new type).
Code as it would be used from the client
import grpc.aio
channel = grpc.aio.insecure_channel("127.0.0.1:50051")
stub = echo_pb2_grpc.EchoStub(channel)
response = await stub.Hi(echo_pb2.EchoRequest(message="ping"))
Or, with an asynchronous context manager:
import grpc.aio
import echo_pb2_grpc
import echo_pb2
async with grpc.aio.insecure_channel("127.0.0.1:50051") as channel:
stub = echo_pb2_grpc.EchoStub(channel)
response = await stub.Hi(echo_pb2.EchoRequest(message="ping"))
The control over the version of the code (synchronous vs. asynchronous) is totally up to the user, by providing the --aiogrpc
flag at compilation time.
The API is used by the user explicitly, when determining from where to import the code (import grpc.aio
).
- Would it make sense to also generate the grpc stub code with an asynchronous name? Like
import echo_pb2_grpc_aio