Merge pull request #24755 from lidizheng/aio-examples

Polish and add AsyncIO examples
pull/22334/head^2
Lidi Zheng 4 years ago committed by GitHub
commit befc7a7d4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 40
      examples/python/auth/BUILD.bazel
  2. 4
      examples/python/auth/_credentials.py
  3. 101
      examples/python/auth/async_customized_auth_client.py
  4. 103
      examples/python/auth/async_customized_auth_server.py
  5. 11
      examples/python/auth/customized_auth_client.py
  6. 11
      examples/python/auth/customized_auth_server.py
  7. 1
      examples/python/auth/helloworld.proto
  8. 20
      examples/python/auth/test/_auth_example_test.py
  9. 5
      examples/python/helloworld/async_greeter_client.py
  10. 43
      examples/python/helloworld/async_greeter_client_with_options.py
  11. 14
      examples/python/helloworld/async_greeter_server.py
  12. 49
      examples/python/helloworld/async_greeter_server_with_reflection.py
  13. 7
      setup.cfg

@ -33,12 +33,12 @@ py_binary(
name = "customized_auth_client", name = "customized_auth_client",
testonly = 1, testonly = 1,
srcs = ["customized_auth_client.py"], srcs = ["customized_auth_client.py"],
data = ["helloworld.proto"],
python_version = "PY3", python_version = "PY3",
deps = [ deps = [
":_credentials", ":_credentials",
"//examples:helloworld_py_pb2",
"//examples:helloworld_py_pb2_grpc",
"//src/python/grpcio/grpc:grpcio", "//src/python/grpcio/grpc:grpcio",
"//tools/distrib/python/grpcio_tools:grpc_tools",
], ],
) )
@ -46,12 +46,40 @@ py_binary(
name = "customized_auth_server", name = "customized_auth_server",
testonly = 1, testonly = 1,
srcs = ["customized_auth_server.py"], srcs = ["customized_auth_server.py"],
data = ["helloworld.proto"],
python_version = "PY3", python_version = "PY3",
deps = [ deps = [
":_credentials", ":_credentials",
"//examples:helloworld_py_pb2",
"//examples:helloworld_py_pb2_grpc",
"//src/python/grpcio/grpc:grpcio", "//src/python/grpcio/grpc:grpcio",
"//tools/distrib/python/grpcio_tools:grpc_tools",
],
)
py_binary(
name = "async_customized_auth_client",
testonly = 1,
srcs = ["async_customized_auth_client.py"],
data = ["helloworld.proto"],
imports = ["."],
python_version = "PY3",
deps = [
":_credentials",
"//src/python/grpcio/grpc:grpcio",
"//tools/distrib/python/grpcio_tools:grpc_tools",
],
)
py_binary(
name = "async_customized_auth_server",
testonly = 1,
srcs = ["async_customized_auth_server.py"],
data = ["helloworld.proto"],
imports = ["."],
python_version = "PY3",
deps = [
":_credentials",
"//src/python/grpcio/grpc:grpcio",
"//tools/distrib/python/grpcio_tools:grpc_tools",
], ],
) )
@ -61,9 +89,11 @@ py_test(
python_version = "PY3", python_version = "PY3",
deps = [ deps = [
":_credentials", ":_credentials",
":async_customized_auth_client",
":async_customized_auth_server",
":customized_auth_client", ":customized_auth_client",
":customized_auth_server", ":customized_auth_server",
"//examples:helloworld_py_pb2",
"//src/python/grpcio/grpc:grpcio", "//src/python/grpcio/grpc:grpcio",
"//tools/distrib/python/grpcio_tools:grpc_tools",
], ],
) )

@ -13,10 +13,6 @@
# limitations under the License. # limitations under the License.
"""Loading SSL credentials for gRPC Python authentication example.""" """Loading SSL credentials for gRPC Python authentication example."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os import os

@ -0,0 +1,101 @@
# Copyright 2020 The 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.
"""Client of the Python AsyncIO example of customizing authentication mechanism."""
import argparse
import asyncio
import logging
import grpc
import _credentials
helloworld_pb2, helloworld_pb2_grpc = grpc.protos_and_services(
"helloworld.proto")
_LOGGER = logging.getLogger(__name__)
_LOGGER.setLevel(logging.INFO)
_SERVER_ADDR_TEMPLATE = 'localhost:%d'
_SIGNATURE_HEADER_KEY = 'x-signature'
class AuthGateway(grpc.AuthMetadataPlugin):
def __call__(self, context: grpc.AuthMetadataContext,
callback: grpc.AuthMetadataPluginCallback) -> None:
"""Implements authentication by passing metadata to a callback.
Implementations of this method must not block.
Args:
context: An AuthMetadataContext providing information on the RPC that
the plugin is being called to authenticate.
callback: An AuthMetadataPluginCallback to be invoked either
synchronously or asynchronously.
"""
# Example AuthMetadataContext object:
# AuthMetadataContext(
# service_url=u'https://localhost:50051/helloworld.Greeter',
# method_name=u'SayHello')
signature = context.method_name[::-1]
callback(((_SIGNATURE_HEADER_KEY, signature),), None)
def create_client_channel(addr: str) -> grpc.aio.Channel:
# Call credential object will be invoked for every single RPC
call_credentials = grpc.metadata_call_credentials(AuthGateway(),
name='auth gateway')
# Channel credential will be valid for the entire channel
channel_credential = grpc.ssl_channel_credentials(
_credentials.ROOT_CERTIFICATE)
# Combining channel credentials and call credentials together
composite_credentials = grpc.composite_channel_credentials(
channel_credential,
call_credentials,
)
channel = grpc.aio.secure_channel(addr, composite_credentials)
return channel
async def send_rpc(channel: grpc.aio.Channel) -> helloworld_pb2.HelloReply:
stub = helloworld_pb2_grpc.GreeterStub(channel)
request = helloworld_pb2.HelloRequest(name='you')
try:
response = await stub.SayHello(request)
except grpc.RpcError as rpc_error:
_LOGGER.error('Received error: %s', rpc_error)
return rpc_error
else:
_LOGGER.info('Received message: %s', response)
return response
async def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument('--port',
nargs='?',
type=int,
default=50051,
help='the address of server')
args = parser.parse_args()
channel = create_client_channel(_SERVER_ADDR_TEMPLATE % args.port)
await send_rpc(channel)
await channel.close()
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
asyncio.run(main())

@ -0,0 +1,103 @@
# Copyright 2020 The 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.
"""Server of the Python AsyncIO example of customizing authentication mechanism."""
import argparse
import asyncio
import logging
from typing import Awaitable, Callable, Tuple
import grpc
import _credentials
helloworld_pb2, helloworld_pb2_grpc = grpc.protos_and_services(
"helloworld.proto")
_LOGGER = logging.getLogger(__name__)
_LOGGER.setLevel(logging.INFO)
_LISTEN_ADDRESS_TEMPLATE = 'localhost:%d'
_SIGNATURE_HEADER_KEY = 'x-signature'
class SignatureValidationInterceptor(grpc.aio.ServerInterceptor):
def __init__(self):
def abort(ignored_request, context: grpc.aio.ServicerContext) -> None:
context.abort(grpc.StatusCode.UNAUTHENTICATED, 'Invalid signature')
self._abort_handler = grpc.unary_unary_rpc_method_handler(abort)
async def intercept_service(
self, continuation: Callable[[grpc.HandlerCallDetails], Awaitable[
grpc.RpcMethodHandler]],
handler_call_details: grpc.HandlerCallDetails
) -> grpc.RpcMethodHandler:
# Example HandlerCallDetails object:
# _HandlerCallDetails(
# method=u'/helloworld.Greeter/SayHello',
# invocation_metadata=...)
method_name = handler_call_details.method.split('/')[-1]
expected_metadata = (_SIGNATURE_HEADER_KEY, method_name[::-1])
if expected_metadata in handler_call_details.invocation_metadata:
return await continuation(handler_call_details)
else:
return self._abort_handler
class SimpleGreeter(helloworld_pb2_grpc.GreeterServicer):
async def SayHello(self, request: helloworld_pb2.HelloRequest,
unused_context) -> helloworld_pb2.HelloReply:
return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)
async def run_server(port: int) -> Tuple[grpc.aio.Server, int]:
# Bind interceptor to server
server = grpc.aio.server(interceptors=(SignatureValidationInterceptor(),))
helloworld_pb2_grpc.add_GreeterServicer_to_server(SimpleGreeter(), server)
# Loading credentials
server_credentials = grpc.ssl_server_credentials(((
_credentials.SERVER_CERTIFICATE_KEY,
_credentials.SERVER_CERTIFICATE,
),))
# Pass down credentials
port = server.add_secure_port(_LISTEN_ADDRESS_TEMPLATE % port,
server_credentials)
await server.start()
return server, port
async def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument('--port',
nargs='?',
type=int,
default=50051,
help='the listening port')
args = parser.parse_args()
server, port = await run_server(args.port)
logging.info('Server is listening at port :%d', port)
await server.wait_for_termination()
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
asyncio.run(main())

@ -13,18 +13,15 @@
# limitations under the License. # limitations under the License.
"""Client of the Python example of customizing authentication mechanism.""" """Client of the Python example of customizing authentication mechanism."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import argparse import argparse
import contextlib import contextlib
import logging import logging
import grpc import grpc
from examples import helloworld_pb2 import _credentials
from examples import helloworld_pb2_grpc
from examples.python.auth import _credentials helloworld_pb2, helloworld_pb2_grpc = grpc.protos_and_services(
"helloworld.proto")
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
_LOGGER.setLevel(logging.INFO) _LOGGER.setLevel(logging.INFO)

@ -13,19 +13,16 @@
# limitations under the License. # limitations under the License.
"""Server of the Python example of customizing authentication mechanism.""" """Server of the Python example of customizing authentication mechanism."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import argparse import argparse
import contextlib import contextlib
import logging import logging
from concurrent import futures from concurrent import futures
import grpc import grpc
from examples import helloworld_pb2 import _credentials
from examples import helloworld_pb2_grpc
from examples.python.auth import _credentials helloworld_pb2, helloworld_pb2_grpc = grpc.protos_and_services(
"helloworld.proto")
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
_LOGGER.setLevel(logging.INFO) _LOGGER.setLevel(logging.INFO)

@ -0,0 +1 @@
../../protos/helloworld.proto

@ -13,16 +13,15 @@
# limitations under the License. # limitations under the License.
"""Test for gRPC Python authentication example.""" """Test for gRPC Python authentication example."""
from __future__ import absolute_import import asyncio
from __future__ import division
from __future__ import print_function
import unittest import unittest
import grpc import grpc
from examples.python.auth import _credentials from examples.python.auth import _credentials
from examples.python.auth import customized_auth_client from examples.python.auth import customized_auth_client
from examples.python.auth import customized_auth_server from examples.python.auth import customized_auth_server
from examples.python.auth import async_customized_auth_client
from examples.python.auth import async_customized_auth_server
_SERVER_ADDR_TEMPLATE = 'localhost:%d' _SERVER_ADDR_TEMPLATE = 'localhost:%d'
@ -51,6 +50,19 @@ class AuthExampleTest(unittest.TestCase):
resp = customized_auth_client.send_rpc(channel) resp = customized_auth_client.send_rpc(channel)
self.assertEqual(resp.code(), grpc.StatusCode.UNAUTHENTICATED) self.assertEqual(resp.code(), grpc.StatusCode.UNAUTHENTICATED)
def test_successful_call_asyncio(self):
async def test_body():
server, port = await async_customized_auth_server.run_server(0)
channel = async_customized_auth_client.create_client_channel(
_SERVER_ADDR_TEMPLATE % port)
await async_customized_auth_client.send_rpc(channel)
await channel.close()
await server.stop(0)
# No unhandled exception raised, test passed!
asyncio.get_event_loop().run_until_complete(test_body())
if __name__ == '__main__': if __name__ == '__main__':
unittest.main(verbosity=2) unittest.main(verbosity=2)

@ -21,10 +21,7 @@ import helloworld_pb2
import helloworld_pb2_grpc import helloworld_pb2_grpc
async def run(): async def run() -> None:
# NOTE(gRPC Python Team): .close() is possible on a channel and should be
# used in circumstances in which the with statement does not fit the needs
# of the code.
async with grpc.aio.insecure_channel('localhost:50051') as channel: async with grpc.aio.insecure_channel('localhost:50051') as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel) stub = helloworld_pb2_grpc.GreeterStub(channel)
response = await stub.SayHello(helloworld_pb2.HelloRequest(name='you')) response = await stub.SayHello(helloworld_pb2.HelloRequest(name='you'))

@ -0,0 +1,43 @@
# Copyright 2020 The 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.
"""gRPC Python AsyncIO helloworld.Greeter client with channel options and timeout parameters."""
import asyncio
import logging
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
# For more channel options, please see https://grpc.io/grpc/core/group__grpc__arg__keys.html
CHANNEL_OPTIONS = [('grpc.lb_policy_name', 'pick_first'),
('grpc.enable_retries', 0),
('grpc.keepalive_timeout_ms', 10000)]
async def run() -> None:
async with grpc.aio.insecure_channel(target='localhost:50051',
options=CHANNEL_OPTIONS) as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
# Timeout in seconds.
# Please refer gRPC Python documents for more detail. https://grpc.io/grpc/python/grpc.html
response = await stub.SayHello(helloworld_pb2.HelloRequest(name='you'),
timeout=10)
print("Greeter client received: " + response.message)
if __name__ == '__main__':
logging.basicConfig()
asyncio.run(run())

@ -23,18 +23,26 @@ import helloworld_pb2_grpc
class Greeter(helloworld_pb2_grpc.GreeterServicer): class Greeter(helloworld_pb2_grpc.GreeterServicer):
async def SayHello(self, request, context): async def SayHello(self, request: helloworld_pb2.HelloRequest,
context: grpc.aio.ServicerContext
) -> helloworld_pb2.HelloReply:
return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name) return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)
async def serve(): async def serve() -> None:
server = grpc.aio.server() server = grpc.aio.server()
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server) helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
listen_addr = '[::]:50051' listen_addr = '[::]:50051'
server.add_insecure_port(listen_addr) server.add_insecure_port(listen_addr)
logging.info("Starting server on %s", listen_addr) logging.info("Starting server on %s", listen_addr)
await server.start() await server.start()
await server.wait_for_termination() try:
await server.wait_for_termination()
except KeyboardInterrupt:
# Shuts down the server with 0 seconds of grace period. During the
# grace period, the server won't accept new connections and allow
# existing RPCs to continue within the grace period.
await server.stop(0)
if __name__ == '__main__': if __name__ == '__main__':

@ -0,0 +1,49 @@
# Copyright 2020 The 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 reflection-enabled version of gRPC AsyncIO helloworld.Greeter server."""
import asyncio
import logging
import grpc
from grpc_reflection.v1alpha import reflection
import helloworld_pb2
import helloworld_pb2_grpc
class Greeter(helloworld_pb2_grpc.GreeterServicer):
async def SayHello(self, request: helloworld_pb2.HelloRequest,
context: grpc.aio.ServicerContext
) -> helloworld_pb2.HelloReply:
return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)
async def serve() -> None:
server = grpc.aio.server()
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
SERVICE_NAMES = (
helloworld_pb2.DESCRIPTOR.services_by_name['Greeter'].full_name,
reflection.SERVICE_NAME,
)
reflection.enable_server_reflection(SERVICE_NAMES, server)
server.add_insecure_port('[::]:50051')
await server.start()
await server.wait_for_termination()
if __name__ == '__main__':
logging.basicConfig()
asyncio.run(serve())

@ -19,12 +19,17 @@ based_on_style = google
[metadata] [metadata]
license_files = LICENSE license_files = LICENSE
# NOTE(lidiz) Adding examples one by one due to pytype aggressive errer:
# ninja: error: build.ninja:178: multiple rules generate helloworld_pb2.pyi [-w dupbuild=err]
[pytype] [pytype]
inputs = inputs =
src/python/grpcio/grpc/experimental src/python/grpcio/grpc/experimental
src/python/grpcio_tests/tests_aio src/python/grpcio_tests/tests_aio
examples/python/auth
examples/python/helloworld
# NOTE(lidiz) # NOTE(lidiz)
# import-error: C extension triggers import-error. # import-error: C extension triggers import-error.
# module-attr: pytype cannot understand the namespace packages by Google. # module-attr: pytype cannot understand the namespace packages by Google.
disable = "import-error,module-attr" # attribute-error: Data classes in grpc module doesn't specify attributes.
disable = "import-error,module-attr,attribute-error"

Loading…
Cancel
Save