This commit is contained in:
Yusuke Nishioka 2024-06-11 16:27:42 -07:00 committed by GitHub
commit 0af7031e67
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 355 additions and 0 deletions

View File

@ -68,6 +68,10 @@ ARTIFACTS += interceptors/headers/helloworld_pb2.py
ARTIFACTS += interceptors/headers/helloworld_pb2_grpc.py
ARTIFACTS += interceptors/headers/helloworld_pb2.pyi
ARTIFACTS += interceptors/streaming/hellostreamingworld_pb2.py
ARTIFACTS += interceptors/streaming/hellostreamingworld_pb2_grpc.py
ARTIFACTS += interceptors/streaming/hellostreamingworld_pb2.pyi
ARTIFACTS += metadata/helloworld_pb2.py
ARTIFACTS += metadata/helloworld_pb2_grpc.py
ARTIFACTS += metadata/helloworld_pb2.pyi
@ -131,6 +135,9 @@ interceptors/default_value/helloworld_pb2.py interceptors/default_value/hellowor
interceptors/headers/helloworld_pb2.py interceptors/headers/helloworld_pb2_grpc.py interceptors/headers/helloworld_pb2.pyi: ../protos/helloworld.proto
python3 -m grpc_tools.protoc --python_out=interceptors/headers --grpc_python_out=interceptors/headers --pyi_out=interceptors/headers -I ../protos ../protos/helloworld.proto
interceptors/streaming/hellostreamingworld_pb2.py interceptors/streaming/hellostreamingworld_pb2_grpc.py interceptors/streaming/hellostreamingworld_pb2.pyi: ../protos/hellostreamingworld.proto
python3 -m grpc_tools.protoc --python_out=interceptors/streaming --grpc_python_out=interceptors/streaming --pyi_out=interceptors/streaming -I ../protos ../protos/hellostreamingworld.proto
metadata/helloworld_pb2.py metadata/helloworld_pb2_grpc.py metadata/helloworld_pb2.pyi: ../protos/helloworld.proto
python3 -m grpc_tools.protoc --python_out=metadata --grpc_python_out=metadata --pyi_out=metadata -I ../protos ../protos/helloworld.proto

View File

@ -0,0 +1,62 @@
# gRPC Python Client/Server Streaming Interceptor Example
This example demonstrates the usage of streaming client/server interceptors.
## How to run this example
1. Start server: `python3 greeter_server.py`
1. Start client: `python3 greeter_client.py`
### Expected outcome
#### unary_stream
server
```
INFO:root:interceptor1: before RPC
INFO:root:interceptor2: before RPC
INFO:root:interceptor3: before RPC
INFO:root:SayHelloServerStreaming: received request 0 # ---------------
INFO:root:interceptor3: before response 0 returned # |
INFO:root:interceptor2: before response 0 returned # |
INFO:root:interceptor1: before response 0 returned # | 1st response
INFO:root:interceptor1: after response 0 returned # |
INFO:root:interceptor2: after response 0 returned # |
INFO:root:interceptor3: after response 0 returned # ---------------
INFO:root:SayHelloServerStreaming: received request 1 # ---------------
INFO:root:interceptor3: before response 1 returned # |
INFO:root:interceptor2: before response 1 returned # |
INFO:root:interceptor1: before response 1 returned # | 2nd response
INFO:root:interceptor1: after response 1 returned # |
INFO:root:interceptor2: after response 1 returned # |
INFO:root:interceptor3: after response 1 returned # ---------------
INFO:root:interceptor3: after RPC
INFO:root:interceptor2: after RPC
INFO:root:interceptor1: after RPC
```
client
```
INFO:root:interceptor1: before RPC
INFO:root:interceptor2: before RPC
INFO:root:interceptor3: before RPC
INFO:root:interceptor3: before response 0 returned # ---------------
INFO:root:interceptor2: before response 0 returned # |
INFO:root:interceptor1: before response 0 returned # |
INFO:root:Hello number 0, Alice! # | 1st response
INFO:root:interceptor1: after response 0 returned # |
INFO:root:interceptor2: after response 0 returned # |
INFO:root:interceptor3: after response 0 returned # ---------------
INFO:root:interceptor3: before response 1 returned # ---------------
INFO:root:interceptor2: before response 1 returned # |
INFO:root:interceptor1: before response 1 returned # |
INFO:root:Hello number 1, Alice! # | 2nd response
INFO:root:interceptor1: after response 1 returned # |
INFO:root:interceptor2: after response 1 returned # |
INFO:root:interceptor3: after response 1 returned # ---------------
INFO:root:interceptor3: after RPC
INFO:root:interceptor2: after RPC
INFO:root:interceptor1: after RPC
```

View File

@ -0,0 +1,66 @@
# Copyright 2023 gRPC authors.
#
# Licensed 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
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The Python implementation of the GRPC hellostreamingworld.MultiGreeter client."""
import logging
import grpc
import hellostreamingworld_pb2
import hellostreamingworld_pb2_grpc
def _wrap_response_iterator(interceptor_id, response_iterator):
for i, response in enumerate(response_iterator):
logging.info(f"{interceptor_id}: before response {i} returned")
yield response
logging.info(f"{interceptor_id}: after response {i} returned")
logging.info(f"{interceptor_id}: after RPC")
class UnaryStreamClientInterceptor(grpc.UnaryStreamClientInterceptor):
def __init__(self, interceptor_id):
self._interceptor_id = interceptor_id
def intercept_unary_stream(
self, continuation, client_call_details, request
):
logging.info(f"{self._interceptor_id}: before RPC")
response_iterator = continuation(client_call_details, request)
new_response_iterator = _wrap_response_iterator(
self._interceptor_id, response_iterator
)
return new_response_iterator
def run() -> None:
with grpc.insecure_channel(f"localhost:50051") as channel:
intercept_channel = grpc.intercept_channel(
channel,
UnaryStreamClientInterceptor("interceptor1"),
UnaryStreamClientInterceptor("interceptor2"),
UnaryStreamClientInterceptor("interceptor3"),
)
stub = hellostreamingworld_pb2_grpc.MultiGreeterStub(intercept_channel)
request = hellostreamingworld_pb2.HelloRequest(
name="Alice", num_greetings="2"
)
for response in stub.sayHello(request):
logging.info(response.message)
if __name__ == "__main__":
logging.basicConfig(level="INFO")
run()

View File

@ -0,0 +1,100 @@
# Copyright 2023 gRPC authors.
#
# Licensed 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
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The Python implementation of the GRPC hellostreamingworld.MultiGreeter server."""
import logging
from concurrent import futures
import grpc
import hellostreamingworld_pb2
import hellostreamingworld_pb2_grpc
def _wrap_handler(handler, behavior_wrapper):
factory = grpc.unary_stream_rpc_method_handler
behavior = handler.unary_stream
return factory(
behavior_wrapper(behavior),
request_deserializer=handler.request_deserializer,
response_serializer=handler.response_serializer,
)
def _wrap_request_iterator(interceptor_id, request_iterator):
for i, request in enumerate(request_iterator):
logging.info(f"{interceptor_id}: before request {i} sent")
yield request
logging.info(f"{interceptor_id}: after request {i} sent")
def _wrap_response_iterator(interceptor_id, response_iterator):
for i, response in enumerate(response_iterator):
logging.info(f"{interceptor_id}: before response {i} returned")
yield response
logging.info(f"{interceptor_id}: after response {i} returned")
logging.info(f"{interceptor_id}: after RPC")
class ServerInterceptor(grpc.ServerInterceptor):
def __init__(self, interceptor_id: str):
self._interceptor_id = interceptor_id
def intercept_service(self, continuation, handler_call_details):
def wrap_behavior(behavior):
def _unary_stream(request, context):
logging.info(f"{self._interceptor_id}: before RPC")
response_iterator = behavior(request, context)
new_response_iterator = _wrap_response_iterator(
self._interceptor_id, response_iterator
)
return new_response_iterator
return _unary_stream
handler = continuation(handler_call_details)
return _wrap_handler(handler, wrap_behavior)
class MultiGreeter(hellostreamingworld_pb2_grpc.MultiGreeterServicer):
def sayHello(self, request, context):
num_greetings = int(request.num_greetings)
for i in range(num_greetings):
logging.info(f"SayHelloServerStreaming: received request {i}")
yield hellostreamingworld_pb2.HelloReply(
message=f"Hello number {i}, {request.name}!"
)
def serve():
server = grpc.server(
futures.ThreadPoolExecutor(max_workers=1),
interceptors=[
ServerInterceptor("interceptor1"),
ServerInterceptor("interceptor2"),
ServerInterceptor("interceptor3"),
],
)
hellostreamingworld_pb2_grpc.add_MultiGreeterServicer_to_server(
MultiGreeter(), server
)
listen_addr = "[::]:50051"
server.add_insecure_port(listen_addr)
server.start()
server.wait_for_termination()
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
serve()

View File

@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: hellostreamingworld.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19hellostreamingworld.proto\x12\x13hellostreamingworld\"3\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x15\n\rnum_greetings\x18\x02 \x01(\t\"\x1d\n\nHelloReply\x12\x0f\n\x07message\x18\x01 \x01(\t2b\n\x0cMultiGreeter\x12R\n\x08sayHello\x12!.hellostreamingworld.HelloRequest\x1a\x1f.hellostreamingworld.HelloReply\"\x00\x30\x01\x42\x0f\n\x07\x65x.grpc\xa2\x02\x03HSWb\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'hellostreamingworld_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\007ex.grpc\242\002\003HSW'
_globals['_HELLOREQUEST']._serialized_start=50
_globals['_HELLOREQUEST']._serialized_end=101
_globals['_HELLOREPLY']._serialized_start=103
_globals['_HELLOREPLY']._serialized_end=132
_globals['_MULTIGREETER']._serialized_start=134
_globals['_MULTIGREETER']._serialized_end=232
# @@protoc_insertion_point(module_scope)

View File

@ -0,0 +1,19 @@
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from typing import ClassVar as _ClassVar, Optional as _Optional
DESCRIPTOR: _descriptor.FileDescriptor
class HelloRequest(_message.Message):
__slots__ = ["name", "num_greetings"]
NAME_FIELD_NUMBER: _ClassVar[int]
NUM_GREETINGS_FIELD_NUMBER: _ClassVar[int]
name: str
num_greetings: str
def __init__(self, name: _Optional[str] = ..., num_greetings: _Optional[str] = ...) -> None: ...
class HelloReply(_message.Message):
__slots__ = ["message"]
MESSAGE_FIELD_NUMBER: _ClassVar[int]
message: str
def __init__(self, message: _Optional[str] = ...) -> None: ...

View File

@ -0,0 +1,70 @@
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
import hellostreamingworld_pb2 as hellostreamingworld__pb2
class MultiGreeterStub(object):
"""The greeting service definition.
"""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.sayHello = channel.unary_stream(
'/hellostreamingworld.MultiGreeter/sayHello',
request_serializer=hellostreamingworld__pb2.HelloRequest.SerializeToString,
response_deserializer=hellostreamingworld__pb2.HelloReply.FromString,
)
class MultiGreeterServicer(object):
"""The greeting service definition.
"""
def sayHello(self, request, context):
"""Sends multiple greetings
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_MultiGreeterServicer_to_server(servicer, server):
rpc_method_handlers = {
'sayHello': grpc.unary_stream_rpc_method_handler(
servicer.sayHello,
request_deserializer=hellostreamingworld__pb2.HelloRequest.FromString,
response_serializer=hellostreamingworld__pb2.HelloReply.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'hellostreamingworld.MultiGreeter', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
# This class is part of an EXPERIMENTAL API.
class MultiGreeter(object):
"""The greeting service definition.
"""
@staticmethod
def sayHello(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_stream(request, target, '/hellostreamingworld.MultiGreeter/sayHello',
hellostreamingworld__pb2.HelloRequest.SerializeToString,
hellostreamingworld__pb2.HelloReply.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)