Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
grpc_helpers_async.py337 linesDownload Raw Back to api_core
1# Copyright 2020 Google LLC2#3# Licensed under the Apache License, Version 2.0 (the "License");4# you may not use this file except in compliance with the License.5# You may obtain a copy of the License at6#7#     http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an "AS IS" BASIS,11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12# See the License for the specific language governing permissions and13# limitations under the License.14 15"""AsyncIO helpers for :mod:`grpc` supporting 3.7+.16 17Please combine more detailed docstring in grpc_helpers.py to use following18functions. This module is implementing the same surface with AsyncIO semantics.19"""20 21import asyncio22import functools23 24from typing import AsyncGenerator, Generic, Iterator, Optional, TypeVar25 26import grpc27from grpc import aio28 29from google.api_core import exceptions, grpc_helpers30 31# denotes the proto response type for grpc calls32P = TypeVar("P")33 34# NOTE(lidiz) Alternatively, we can hack "__getattribute__" to perform35# automatic patching for us. But that means the overhead of creating an36# extra Python function spreads to every single send and receive.37 38 39class _WrappedCall(aio.Call):40    def __init__(self):41        self._call = None42 43    def with_call(self, call):44        """Supplies the call object separately to keep __init__ clean."""45        self._call = call46        return self47 48    async def initial_metadata(self):49        return await self._call.initial_metadata()50 51    async def trailing_metadata(self):52        return await self._call.trailing_metadata()53 54    async def code(self):55        return await self._call.code()56 57    async def details(self):58        return await self._call.details()59 60    def cancelled(self):61        return self._call.cancelled()62 63    def done(self):64        return self._call.done()65 66    def time_remaining(self):67        return self._call.time_remaining()68 69    def cancel(self):70        return self._call.cancel()71 72    def add_done_callback(self, callback):73        self._call.add_done_callback(callback)74 75    async def wait_for_connection(self):76        try:77            await self._call.wait_for_connection()78        except grpc.RpcError as rpc_error:79            raise exceptions.from_grpc_error(rpc_error) from rpc_error80 81 82class _WrappedUnaryResponseMixin(Generic[P], _WrappedCall):83    def __await__(self) -> Iterator[P]:84        try:85            response = yield from self._call.__await__()86            return response87        except grpc.RpcError as rpc_error:88            raise exceptions.from_grpc_error(rpc_error) from rpc_error89 90 91class _WrappedStreamResponseMixin(Generic[P], _WrappedCall):92    def __init__(self):93        self._wrapped_async_generator = None94 95    async def read(self) -> P:96        try:97            return await self._call.read()98        except grpc.RpcError as rpc_error:99            raise exceptions.from_grpc_error(rpc_error) from rpc_error100 101    async def _wrapped_aiter(self) -> AsyncGenerator[P, None]:102        try:103            # NOTE(lidiz) coverage doesn't understand the exception raised from104            # __anext__ method. It is covered by test case:105            #     test_wrap_stream_errors_aiter_non_rpc_error106            async for response in self._call:  # pragma: no branch107                yield response108        except grpc.RpcError as rpc_error:109            raise exceptions.from_grpc_error(rpc_error) from rpc_error110 111    def __aiter__(self) -> AsyncGenerator[P, None]:112        if not self._wrapped_async_generator:113            self._wrapped_async_generator = self._wrapped_aiter()114        return self._wrapped_async_generator115 116 117class _WrappedStreamRequestMixin(_WrappedCall):118    async def write(self, request):119        try:120            await self._call.write(request)121        except grpc.RpcError as rpc_error:122            raise exceptions.from_grpc_error(rpc_error) from rpc_error123 124    async def done_writing(self):125        try:126            await self._call.done_writing()127        except grpc.RpcError as rpc_error:128            raise exceptions.from_grpc_error(rpc_error) from rpc_error129 130 131# NOTE(lidiz) Implementing each individual class separately, so we don't132# expose any API that should not be seen. E.g., __aiter__ in unary-unary133# RPC, or __await__ in stream-stream RPC.134class _WrappedUnaryUnaryCall(_WrappedUnaryResponseMixin[P], aio.UnaryUnaryCall):135    """Wrapped UnaryUnaryCall to map exceptions."""136 137 138class _WrappedUnaryStreamCall(_WrappedStreamResponseMixin[P], aio.UnaryStreamCall):139    """Wrapped UnaryStreamCall to map exceptions."""140 141 142class _WrappedStreamUnaryCall(143    _WrappedUnaryResponseMixin[P], _WrappedStreamRequestMixin, aio.StreamUnaryCall144):145    """Wrapped StreamUnaryCall to map exceptions."""146 147 148class _WrappedStreamStreamCall(149    _WrappedStreamRequestMixin, _WrappedStreamResponseMixin[P], aio.StreamStreamCall150):151    """Wrapped StreamStreamCall to map exceptions."""152 153 154# public type alias denoting the return type of async streaming gapic calls155GrpcAsyncStream = _WrappedStreamResponseMixin[P]156# public type alias denoting the return type of unary gapic calls157AwaitableGrpcCall = _WrappedUnaryResponseMixin[P]158 159 160def _wrap_unary_errors(callable_):161    """Map errors for Unary-Unary async callables."""162    grpc_helpers._patch_callable_name(callable_)163 164    @functools.wraps(callable_)165    def error_remapped_callable(*args, **kwargs):166        call = callable_(*args, **kwargs)167        return _WrappedUnaryUnaryCall().with_call(call)168 169    return error_remapped_callable170 171 172def _wrap_stream_errors(callable_):173    """Map errors for streaming RPC async callables."""174    grpc_helpers._patch_callable_name(callable_)175 176    @functools.wraps(callable_)177    async def error_remapped_callable(*args, **kwargs):178        call = callable_(*args, **kwargs)179 180        if isinstance(call, aio.UnaryStreamCall):181            call = _WrappedUnaryStreamCall().with_call(call)182        elif isinstance(call, aio.StreamUnaryCall):183            call = _WrappedStreamUnaryCall().with_call(call)184        elif isinstance(call, aio.StreamStreamCall):185            call = _WrappedStreamStreamCall().with_call(call)186        else:187            raise TypeError("Unexpected type of call %s" % type(call))188 189        await call.wait_for_connection()190        return call191 192    return error_remapped_callable193 194 195def wrap_errors(callable_):196    """Wrap a gRPC async callable and map :class:`grpc.RpcErrors` to197    friendly error classes.198 199    Errors raised by the gRPC callable are mapped to the appropriate200    :class:`google.api_core.exceptions.GoogleAPICallError` subclasses. The201    original `grpc.RpcError` (which is usually also a `grpc.Call`) is202    available from the ``response`` property on the mapped exception. This203    is useful for extracting metadata from the original error.204 205    Args:206        callable_ (Callable): A gRPC callable.207 208    Returns: Callable: The wrapped gRPC callable.209    """210    if isinstance(callable_, aio.UnaryUnaryMultiCallable):211        return _wrap_unary_errors(callable_)212    else:213        return _wrap_stream_errors(callable_)214 215 216def create_channel(217    target,218    credentials=None,219    scopes=None,220    ssl_credentials=None,221    credentials_file=None,222    quota_project_id=None,223    default_scopes=None,224    default_host=None,225    compression=None,226    attempt_direct_path: Optional[bool] = False,227    **kwargs228):229    """Create an AsyncIO secure channel with credentials.230 231    Args:232        target (str): The target service address in the format 'hostname:port'.233        credentials (google.auth.credentials.Credentials): The credentials. If234            not specified, then this function will attempt to ascertain the235            credentials from the environment using :func:`google.auth.default`.236        scopes (Sequence[str]): A optional list of scopes needed for this237            service. These are only used when credentials are not specified and238            are passed to :func:`google.auth.default`.239        ssl_credentials (grpc.ChannelCredentials): Optional SSL channel240            credentials. This can be used to specify different certificates.241        credentials_file (str): A file with credentials that can be loaded with242            :func:`google.auth.load_credentials_from_file`. This argument is243            mutually exclusive with credentials.244        quota_project_id (str): An optional project to use for billing and quota.245        default_scopes (Sequence[str]): Default scopes passed by a Google client246            library. Use 'scopes' for user-defined scopes.247        default_host (str): The default endpoint. e.g., "pubsub.googleapis.com".248        compression (grpc.Compression): An optional value indicating the249            compression method to be used over the lifetime of the channel.250        attempt_direct_path (Optional[bool]): If set, Direct Path will be attempted251            when the request is made. Direct Path is only available within a Google252            Compute Engine (GCE) environment and provides a proxyless connection253            which increases the available throughput, reduces latency, and increases254            reliability. Note:255 256            - This argument should only be set in a GCE environment and for Services257              that are known to support Direct Path.258            - If this argument is set outside of GCE, then this request will fail259              unless the back-end service happens to have configured fall-back to DNS.260            - If the request causes a `ServiceUnavailable` response, it is recommended261              that the client repeat the request with `attempt_direct_path` set to262              `False` as the Service may not support Direct Path.263            - Using `ssl_credentials` with `attempt_direct_path` set to `True` will264              result in `ValueError` as this combination  is not yet supported.265 266        kwargs: Additional key-word args passed to :func:`aio.secure_channel`.267 268    Returns:269        aio.Channel: The created channel.270 271    Raises:272        google.api_core.DuplicateCredentialArgs: If both a credentials object and credentials_file are passed.273        ValueError: If `ssl_credentials` is set and `attempt_direct_path` is set to `True`.274    """275 276    # If `ssl_credentials` is set and `attempt_direct_path` is set to `True`,277    # raise ValueError as this is not yet supported.278    # See https://github.com/googleapis/python-api-core/issues/590279    if ssl_credentials and attempt_direct_path:280        raise ValueError("Using ssl_credentials with Direct Path is not supported")281 282    composite_credentials = grpc_helpers._create_composite_credentials(283        credentials=credentials,284        credentials_file=credentials_file,285        scopes=scopes,286        default_scopes=default_scopes,287        ssl_credentials=ssl_credentials,288        quota_project_id=quota_project_id,289        default_host=default_host,290    )291 292    if attempt_direct_path:293        target = grpc_helpers._modify_target_for_direct_path(target)294 295    return aio.secure_channel(296        target, composite_credentials, compression=compression, **kwargs297    )298 299 300class FakeUnaryUnaryCall(_WrappedUnaryUnaryCall):301    """Fake implementation for unary-unary RPCs.302 303    It is a dummy object for response message. Supply the intended response304    upon the initialization, and the coroutine will return the exact response305    message.306    """307 308    def __init__(self, response=object()):309        self.response = response310        self._future = asyncio.get_event_loop().create_future()311        self._future.set_result(self.response)312 313    def __await__(self):314        response = yield from self._future.__await__()315        return response316 317 318class FakeStreamUnaryCall(_WrappedStreamUnaryCall):319    """Fake implementation for stream-unary RPCs.320 321    It is a dummy object for response message. Supply the intended response322    upon the initialization, and the coroutine will return the exact response323    message.324    """325 326    def __init__(self, response=object()):327        self.response = response328        self._future = asyncio.get_event_loop().create_future()329        self._future.set_result(self.response)330 331    def __await__(self):332        response = yield from self._future.__await__()333        return response334 335    async def wait_for_connection(self):336        pass337 
codekingpro/portable-devtools · Team Ai