From 709eb497790f1111c84fd62e4f9cecef9a0440cf Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Thu, 16 Nov 2023 16:30:07 -0800 Subject: [PATCH 01/14] add types to grpc call wrappers --- google/api_core/grpc_helpers.py | 11 +++++---- google/api_core/grpc_helpers_async.py | 34 ++++++++++++++++++--------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index f52e180a..6feb2e12 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -13,6 +13,7 @@ # limitations under the License. """Helpers for :mod:`grpc`.""" +from typing import Generic, TypeVar, Iterator import collections import functools @@ -54,6 +55,8 @@ _LOGGER = logging.getLogger(__name__) +# denotes the type yielded from streaming calls +S = TypeVar("S") def _patch_callable_name(callable_): """Fix-up gRPC callable attributes. @@ -79,7 +82,7 @@ def error_remapped_callable(*args, **kwargs): return error_remapped_callable -class _StreamingResponseIterator(grpc.Call): +class GrpcStream(grpc.Call, Generic[S]): def __init__(self, wrapped, prefetch_first_result=True): self._wrapped = wrapped @@ -97,11 +100,11 @@ def __init__(self, wrapped, prefetch_first_result=True): # ignore stop iteration at this time. This should be handled outside of retry. pass - def __iter__(self): + def __iter__(self) -> Iterator[S]: """This iterator is also an iterable that returns itself.""" return self - def __next__(self): + def __next__(self) -> S: """Get the next response from the stream. Returns: @@ -162,7 +165,7 @@ def error_remapped_callable(*args, **kwargs): # hidden flag to see if pre-fetching is disabled. # https://github.com/googleapis/python-pubsub/issues/93#issuecomment-630762257 prefetch_first = getattr(callable_, "_prefetch_first_result_", True) - return _StreamingResponseIterator( + return GrpcStream( result, prefetch_first_result=prefetch_first ) except grpc.RpcError as exc: diff --git a/google/api_core/grpc_helpers_async.py b/google/api_core/grpc_helpers_async.py index d1f69d98..bd082836 100644 --- a/google/api_core/grpc_helpers_async.py +++ b/google/api_core/grpc_helpers_async.py @@ -21,6 +21,8 @@ import asyncio import functools +from typing import Generic, Generator, AsyncGenerator, Awaitable, TypeVar, Any, Union + import grpc from grpc import aio @@ -31,6 +33,11 @@ # automatic patching for us. But that means the overhead of creating an # extra Python function spreads to every single send and receive. +# denotes the type returned from unary calls +U = TypeVar("U") +# denotes the type yielded from streaming calls +S = TypeVar("S") + class _WrappedCall(aio.Call): def __init__(self): @@ -74,9 +81,8 @@ async def wait_for_connection(self): except grpc.RpcError as rpc_error: raise exceptions.from_grpc_error(rpc_error) from rpc_error - -class _WrappedUnaryResponseMixin(_WrappedCall): - def __await__(self): +class _WrappedUnaryResponseMixin(_WrappedCall, Generic[U]): + def __await__(self) -> Generator[Any, None, U]: try: response = yield from self._call.__await__() return response @@ -84,17 +90,17 @@ def __await__(self): raise exceptions.from_grpc_error(rpc_error) from rpc_error -class _WrappedStreamResponseMixin(_WrappedCall): +class _WrappedStreamResponseMixin(_WrappedCall, Generic[S]): def __init__(self): self._wrapped_async_generator = None - async def read(self): + async def read(self) -> S: try: return await self._call.read() except grpc.RpcError as rpc_error: raise exceptions.from_grpc_error(rpc_error) from rpc_error - async def _wrapped_aiter(self): + async def _wrapped_aiter(self) -> AsyncGenerator[S, None]: try: # NOTE(lidiz) coverage doesn't understand the exception raised from # __anext__ method. It is covered by test case: @@ -104,7 +110,7 @@ async def _wrapped_aiter(self): except grpc.RpcError as rpc_error: raise exceptions.from_grpc_error(rpc_error) from rpc_error - def __aiter__(self): + def __aiter__(self) -> AsyncGenerator[S, None]: if not self._wrapped_async_generator: self._wrapped_async_generator = self._wrapped_aiter() return self._wrapped_async_generator @@ -127,26 +133,32 @@ async def done_writing(self): # NOTE(lidiz) Implementing each individual class separately, so we don't # expose any API that should not be seen. E.g., __aiter__ in unary-unary # RPC, or __await__ in stream-stream RPC. -class _WrappedUnaryUnaryCall(_WrappedUnaryResponseMixin, aio.UnaryUnaryCall): +class _WrappedUnaryUnaryCall(_WrappedUnaryResponseMixin[U], aio.UnaryUnaryCall): """Wrapped UnaryUnaryCall to map exceptions.""" -class _WrappedUnaryStreamCall(_WrappedStreamResponseMixin, aio.UnaryStreamCall): +class _WrappedUnaryStreamCall(_WrappedStreamResponseMixin[S], aio.UnaryStreamCall): """Wrapped UnaryStreamCall to map exceptions.""" class _WrappedStreamUnaryCall( - _WrappedUnaryResponseMixin, _WrappedStreamRequestMixin, aio.StreamUnaryCall + _WrappedUnaryResponseMixin[S], _WrappedStreamRequestMixin, aio.StreamUnaryCall ): """Wrapped StreamUnaryCall to map exceptions.""" class _WrappedStreamStreamCall( - _WrappedStreamRequestMixin, _WrappedStreamResponseMixin, aio.StreamStreamCall + _WrappedStreamRequestMixin, _WrappedStreamResponseMixin[S], aio.StreamStreamCall ): """Wrapped StreamStreamCall to map exceptions.""" +# public type denoting the return type of streaming gapic calls +AwaitableGrpcAsyncStream = Union[_WrappedUnaryStreamCall[S], _WrappedStreamStreamCall[S]] +# public type denoting the return type of unary gapic calls +AwaitableGrpcCall = Union[_WrappedUnaryUnaryCall[U], _WrappedStreamUnaryCall[U]] + + def _wrap_unary_errors(callable_): """Map errors for Unary-Unary async callables.""" grpc_helpers._patch_callable_name(callable_) From 202697441ae923f4f2b8b1d229bebc1f39e44619 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Thu, 16 Nov 2023 16:49:25 -0800 Subject: [PATCH 02/14] fixed tests --- google/api_core/grpc_helpers_async.py | 6 +++--- tests/unit/test_grpc_helpers.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/google/api_core/grpc_helpers_async.py b/google/api_core/grpc_helpers_async.py index bd082836..ac28c386 100644 --- a/google/api_core/grpc_helpers_async.py +++ b/google/api_core/grpc_helpers_async.py @@ -81,7 +81,7 @@ async def wait_for_connection(self): except grpc.RpcError as rpc_error: raise exceptions.from_grpc_error(rpc_error) from rpc_error -class _WrappedUnaryResponseMixin(_WrappedCall, Generic[U]): +class _WrappedUnaryResponseMixin(Generic[U], _WrappedCall): def __await__(self) -> Generator[Any, None, U]: try: response = yield from self._call.__await__() @@ -90,7 +90,7 @@ def __await__(self) -> Generator[Any, None, U]: raise exceptions.from_grpc_error(rpc_error) from rpc_error -class _WrappedStreamResponseMixin(_WrappedCall, Generic[S]): +class _WrappedStreamResponseMixin(Generic[S], _WrappedCall): def __init__(self): self._wrapped_async_generator = None @@ -154,7 +154,7 @@ class _WrappedStreamStreamCall( # public type denoting the return type of streaming gapic calls -AwaitableGrpcAsyncStream = Union[_WrappedUnaryStreamCall[S], _WrappedStreamStreamCall[S]] +AwaitableGrpcStream = Union[_WrappedUnaryStreamCall[S], _WrappedStreamStreamCall[S]] # public type denoting the return type of unary gapic calls AwaitableGrpcCall = Union[_WrappedUnaryUnaryCall[U], _WrappedStreamUnaryCall[U]] diff --git a/tests/unit/test_grpc_helpers.py b/tests/unit/test_grpc_helpers.py index 4eccbcaa..cf563f6f 100644 --- a/tests/unit/test_grpc_helpers.py +++ b/tests/unit/test_grpc_helpers.py @@ -73,14 +73,14 @@ def test_wrap_unary_errors(): assert exc_info.value.response == grpc_error -class Test_StreamingResponseIterator: +class TestGrpcStream: @staticmethod def _make_wrapped(*items): return iter(items) @staticmethod def _make_one(wrapped, **kw): - return grpc_helpers._StreamingResponseIterator(wrapped, **kw) + return grpc_helpers.GrpcStream(wrapped, **kw) def test_ctor_defaults(self): wrapped = self._make_wrapped("a", "b", "c") From f65b0c73d6e906875f3baf7f3d403875f598ee31 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 17 Nov 2023 01:29:52 +0000 Subject: [PATCH 03/14] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot=20?= =?UTF-8?q?post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/api_core/grpc_helpers.py | 5 ++--- google/api_core/grpc_helpers_async.py | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index 6feb2e12..273b1896 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -58,6 +58,7 @@ # denotes the type yielded from streaming calls S = TypeVar("S") + def _patch_callable_name(callable_): """Fix-up gRPC callable attributes. @@ -165,9 +166,7 @@ def error_remapped_callable(*args, **kwargs): # hidden flag to see if pre-fetching is disabled. # https://github.com/googleapis/python-pubsub/issues/93#issuecomment-630762257 prefetch_first = getattr(callable_, "_prefetch_first_result_", True) - return GrpcStream( - result, prefetch_first_result=prefetch_first - ) + return GrpcStream(result, prefetch_first_result=prefetch_first) except grpc.RpcError as exc: raise exceptions.from_grpc_error(exc) from exc diff --git a/google/api_core/grpc_helpers_async.py b/google/api_core/grpc_helpers_async.py index ac28c386..48389ada 100644 --- a/google/api_core/grpc_helpers_async.py +++ b/google/api_core/grpc_helpers_async.py @@ -81,6 +81,7 @@ async def wait_for_connection(self): except grpc.RpcError as rpc_error: raise exceptions.from_grpc_error(rpc_error) from rpc_error + class _WrappedUnaryResponseMixin(Generic[U], _WrappedCall): def __await__(self) -> Generator[Any, None, U]: try: From 373ef40e66d688af8c97ba7268315bcc64125d22 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Thu, 16 Nov 2023 17:07:07 -0800 Subject: [PATCH 04/14] changed type --- google/api_core/grpc_helpers_async.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google/api_core/grpc_helpers_async.py b/google/api_core/grpc_helpers_async.py index 48389ada..89cf4448 100644 --- a/google/api_core/grpc_helpers_async.py +++ b/google/api_core/grpc_helpers_async.py @@ -21,7 +21,7 @@ import asyncio import functools -from typing import Generic, Generator, AsyncGenerator, Awaitable, TypeVar, Any, Union +from typing import Generic, Iterator, AsyncGenerator, Awaitable, TypeVar, Any, Union import grpc from grpc import aio @@ -83,7 +83,7 @@ async def wait_for_connection(self): class _WrappedUnaryResponseMixin(Generic[U], _WrappedCall): - def __await__(self) -> Generator[Any, None, U]: + def __await__(self) -> Iterator[U]: try: response = yield from self._call.__await__() return response From 2c20dec01615e46861fb2688971266d2e51912ca Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Thu, 16 Nov 2023 17:53:14 -0800 Subject: [PATCH 05/14] changed async types --- google/api_core/grpc_helpers_async.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google/api_core/grpc_helpers_async.py b/google/api_core/grpc_helpers_async.py index 89cf4448..a5e4ed4c 100644 --- a/google/api_core/grpc_helpers_async.py +++ b/google/api_core/grpc_helpers_async.py @@ -155,9 +155,9 @@ class _WrappedStreamStreamCall( # public type denoting the return type of streaming gapic calls -AwaitableGrpcStream = Union[_WrappedUnaryStreamCall[S], _WrappedStreamStreamCall[S]] +GrpcAsyncStream = _WrappedStreamResponseMixin[S] # public type denoting the return type of unary gapic calls -AwaitableGrpcCall = Union[_WrappedUnaryUnaryCall[U], _WrappedStreamUnaryCall[U]] +AwaitableGrpcCall = _WrappedUnaryResponseMixin[U] def _wrap_unary_errors(callable_): From 430e0336a8bdc22656354818d1542d1cac942983 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Thu, 16 Nov 2023 17:54:07 -0800 Subject: [PATCH 06/14] added tests --- google/api_core/grpc_helpers_async.py | 4 ++-- tests/asyncio/test_grpc_helpers_async.py | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/google/api_core/grpc_helpers_async.py b/google/api_core/grpc_helpers_async.py index a5e4ed4c..ffabd5b7 100644 --- a/google/api_core/grpc_helpers_async.py +++ b/google/api_core/grpc_helpers_async.py @@ -154,9 +154,9 @@ class _WrappedStreamStreamCall( """Wrapped StreamStreamCall to map exceptions.""" -# public type denoting the return type of streaming gapic calls +# public type alias denoting the return type of streaming gapic calls GrpcAsyncStream = _WrappedStreamResponseMixin[S] -# public type denoting the return type of unary gapic calls +# public type alias denoting the return type of unary gapic calls AwaitableGrpcCall = _WrappedUnaryResponseMixin[U] diff --git a/tests/asyncio/test_grpc_helpers_async.py b/tests/asyncio/test_grpc_helpers_async.py index 95242f6b..d6053986 100644 --- a/tests/asyncio/test_grpc_helpers_async.py +++ b/tests/asyncio/test_grpc_helpers_async.py @@ -266,6 +266,28 @@ def test_wrap_errors_non_streaming(wrap_unary_errors): wrap_unary_errors.assert_called_once_with(callable_) +def test_grpc_async_stream(): + """ + GrpcAsyncStream type be both an AsyncIterator and a grpc.aio.Call. + """ + instance = grpc_helpers_async.GrpcAsyncStream[int]() + assert isinstance(instance, grpc.aio.Call) + # should implement __aiter__ and __anext__ + assert hasattr(instance, "__aiter__") + it = instance.__aiter__() + assert hasattr(it, "__anext__") + + +def test_awaitable_grpc_call(): + """ + AwaitableGrpcCall type should be an Awaitable and a grpc.aio.Call. + """ + instance = grpc_helpers_async.AwaitableGrpcCall[int]() + assert isinstance(instance, grpc.aio.Call) + # should implement __await__ + assert hasattr(instance, "__await__") + + @mock.patch("google.api_core.grpc_helpers_async._wrap_stream_errors") def test_wrap_errors_streaming(wrap_stream_errors): callable_ = mock.create_autospec(aio.UnaryStreamMultiCallable) From 3cf50bb806b690deae160d4134ac6a7206e047a1 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 17 Nov 2023 08:48:06 -0800 Subject: [PATCH 07/14] fixed lint issues --- google/api_core/grpc_helpers_async.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/api_core/grpc_helpers_async.py b/google/api_core/grpc_helpers_async.py index ffabd5b7..fcef6df3 100644 --- a/google/api_core/grpc_helpers_async.py +++ b/google/api_core/grpc_helpers_async.py @@ -21,7 +21,7 @@ import asyncio import functools -from typing import Generic, Iterator, AsyncGenerator, Awaitable, TypeVar, Any, Union +from typing import Generic, Iterator, AsyncGenerator, TypeVar import grpc from grpc import aio From b817527c6173505b013dc37861061faf10550750 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 17 Nov 2023 09:48:10 -0800 Subject: [PATCH 08/14] Update tests/asyncio/test_grpc_helpers_async.py Co-authored-by: Anthonios Partheniou --- tests/asyncio/test_grpc_helpers_async.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/asyncio/test_grpc_helpers_async.py b/tests/asyncio/test_grpc_helpers_async.py index d6053986..67c9b335 100644 --- a/tests/asyncio/test_grpc_helpers_async.py +++ b/tests/asyncio/test_grpc_helpers_async.py @@ -268,7 +268,7 @@ def test_wrap_errors_non_streaming(wrap_unary_errors): def test_grpc_async_stream(): """ - GrpcAsyncStream type be both an AsyncIterator and a grpc.aio.Call. + GrpcAsyncStream type should be both an AsyncIterator and a grpc.aio.Call. """ instance = grpc_helpers_async.GrpcAsyncStream[int]() assert isinstance(instance, grpc.aio.Call) From 623becd2197db3ebd0e607b0fc19a9c960091086 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 17 Nov 2023 09:54:07 -0800 Subject: [PATCH 09/14] turned GrpcStream into a type alias --- google/api_core/grpc_helpers.py | 10 +++++++--- google/api_core/grpc_helpers_async.py | 2 +- tests/unit/test_grpc_helpers.py | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index 273b1896..5346bc0c 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -58,7 +58,6 @@ # denotes the type yielded from streaming calls S = TypeVar("S") - def _patch_callable_name(callable_): """Fix-up gRPC callable attributes. @@ -83,7 +82,7 @@ def error_remapped_callable(*args, **kwargs): return error_remapped_callable -class GrpcStream(grpc.Call, Generic[S]): +class _StreamingResponseIterator(grpc.Call, Generic[S]): def __init__(self, wrapped, prefetch_first_result=True): self._wrapped = wrapped @@ -148,6 +147,9 @@ def trailing_metadata(self): return self._wrapped.trailing_metadata() +GrpcStream = _StreamingResponseIterator[S] + + def _wrap_stream_errors(callable_): """Wrap errors for Unary-Stream and Stream-Stream gRPC callables. @@ -166,7 +168,9 @@ def error_remapped_callable(*args, **kwargs): # hidden flag to see if pre-fetching is disabled. # https://github.com/googleapis/python-pubsub/issues/93#issuecomment-630762257 prefetch_first = getattr(callable_, "_prefetch_first_result_", True) - return GrpcStream(result, prefetch_first_result=prefetch_first) + return _StreamingResponseIterator( + result, prefetch_first_result=prefetch_first + ) except grpc.RpcError as exc: raise exceptions.from_grpc_error(exc) from exc diff --git a/google/api_core/grpc_helpers_async.py b/google/api_core/grpc_helpers_async.py index fcef6df3..5c5dc96b 100644 --- a/google/api_core/grpc_helpers_async.py +++ b/google/api_core/grpc_helpers_async.py @@ -154,7 +154,7 @@ class _WrappedStreamStreamCall( """Wrapped StreamStreamCall to map exceptions.""" -# public type alias denoting the return type of streaming gapic calls +# public type alias denoting the return type of async streaming gapic calls GrpcAsyncStream = _WrappedStreamResponseMixin[S] # public type alias denoting the return type of unary gapic calls AwaitableGrpcCall = _WrappedUnaryResponseMixin[U] diff --git a/tests/unit/test_grpc_helpers.py b/tests/unit/test_grpc_helpers.py index cf563f6f..4eccbcaa 100644 --- a/tests/unit/test_grpc_helpers.py +++ b/tests/unit/test_grpc_helpers.py @@ -73,14 +73,14 @@ def test_wrap_unary_errors(): assert exc_info.value.response == grpc_error -class TestGrpcStream: +class Test_StreamingResponseIterator: @staticmethod def _make_wrapped(*items): return iter(items) @staticmethod def _make_one(wrapped, **kw): - return grpc_helpers.GrpcStream(wrapped, **kw) + return grpc_helpers._StreamingResponseIterator(wrapped, **kw) def test_ctor_defaults(self): wrapped = self._make_wrapped("a", "b", "c") From 781cdeeaf269c7d699116a6b9f4c45e359c6a0bf Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 17 Nov 2023 10:03:06 -0800 Subject: [PATCH 10/14] added test for GrpcStream --- tests/unit/test_grpc_helpers.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/unit/test_grpc_helpers.py b/tests/unit/test_grpc_helpers.py index 4eccbcaa..e19d221b 100644 --- a/tests/unit/test_grpc_helpers.py +++ b/tests/unit/test_grpc_helpers.py @@ -195,6 +195,24 @@ def test_trailing_metadata(self): wrapped.trailing_metadata.assert_called_once_with() +class TestGrpcStream(Test_StreamingResponseIterator): + + @staticmethod + def _make_one(wrapped, **kw): + return grpc_helpers.GrpcStream(wrapped, **kw) + + def test_grpc_stream_attributes(self): + """ + Should be both a grpc.Call and an iterable + """ + call = self._make_one(None) + assert isinstance(call, grpc.Call) + # should implement __iter__ + assert hasattr(call, "__iter__") + it = call.__iter__() + assert hasattr(it, "__next__") + + def test_wrap_stream_okay(): expected_responses = [1, 2, 3] callable_ = mock.Mock(spec=["__call__"], return_value=iter(expected_responses)) From 9c3cd541ac37fdec0b6961064766f1bc661568c0 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 17 Nov 2023 18:05:08 +0000 Subject: [PATCH 11/14] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot=20?= =?UTF-8?q?post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/api_core/grpc_helpers.py | 1 + tests/unit/test_grpc_helpers.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index 5346bc0c..b80afef8 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -58,6 +58,7 @@ # denotes the type yielded from streaming calls S = TypeVar("S") + def _patch_callable_name(callable_): """Fix-up gRPC callable attributes. diff --git a/tests/unit/test_grpc_helpers.py b/tests/unit/test_grpc_helpers.py index e19d221b..58a6a329 100644 --- a/tests/unit/test_grpc_helpers.py +++ b/tests/unit/test_grpc_helpers.py @@ -196,7 +196,6 @@ def test_trailing_metadata(self): class TestGrpcStream(Test_StreamingResponseIterator): - @staticmethod def _make_one(wrapped, **kw): return grpc_helpers.GrpcStream(wrapped, **kw) From 5d674e7fc615ab8a2a4087114fcee3ae093ba297 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 17 Nov 2023 10:05:30 -0800 Subject: [PATCH 12/14] added comment --- google/api_core/grpc_helpers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index b80afef8..f5b218d3 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -148,6 +148,7 @@ def trailing_metadata(self): return self._wrapped.trailing_metadata() +# public type alias denoting the return type of streaming gapic calls GrpcStream = _StreamingResponseIterator[S] From 06a5ea9572f77c043ebe4826b6f7a710e21e08fc Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 17 Nov 2023 10:11:21 -0800 Subject: [PATCH 13/14] reordered types --- google/api_core/grpc_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index f5b218d3..b098ae22 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -83,7 +83,7 @@ def error_remapped_callable(*args, **kwargs): return error_remapped_callable -class _StreamingResponseIterator(grpc.Call, Generic[S]): +class _StreamingResponseIterator(Generic[S], grpc.Call): def __init__(self, wrapped, prefetch_first_result=True): self._wrapped = wrapped From 10ef5bf0d9a3be7d4d466fa504e0447479c1bee0 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 17 Nov 2023 10:23:09 -0800 Subject: [PATCH 14/14] changed type var to P --- google/api_core/grpc_helpers.py | 12 +++++------ google/api_core/grpc_helpers_async.py | 31 ++++++++++++--------------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index b098ae22..793c884d 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -55,8 +55,8 @@ _LOGGER = logging.getLogger(__name__) -# denotes the type yielded from streaming calls -S = TypeVar("S") +# denotes the proto response type for grpc calls +P = TypeVar("P") def _patch_callable_name(callable_): @@ -83,7 +83,7 @@ def error_remapped_callable(*args, **kwargs): return error_remapped_callable -class _StreamingResponseIterator(Generic[S], grpc.Call): +class _StreamingResponseIterator(Generic[P], grpc.Call): def __init__(self, wrapped, prefetch_first_result=True): self._wrapped = wrapped @@ -101,11 +101,11 @@ def __init__(self, wrapped, prefetch_first_result=True): # ignore stop iteration at this time. This should be handled outside of retry. pass - def __iter__(self) -> Iterator[S]: + def __iter__(self) -> Iterator[P]: """This iterator is also an iterable that returns itself.""" return self - def __next__(self) -> S: + def __next__(self) -> P: """Get the next response from the stream. Returns: @@ -149,7 +149,7 @@ def trailing_metadata(self): # public type alias denoting the return type of streaming gapic calls -GrpcStream = _StreamingResponseIterator[S] +GrpcStream = _StreamingResponseIterator[P] def _wrap_stream_errors(callable_): diff --git a/google/api_core/grpc_helpers_async.py b/google/api_core/grpc_helpers_async.py index 5c5dc96b..5685e6f8 100644 --- a/google/api_core/grpc_helpers_async.py +++ b/google/api_core/grpc_helpers_async.py @@ -28,16 +28,13 @@ from google.api_core import exceptions, grpc_helpers +# denotes the proto response type for grpc calls +P = TypeVar("P") # NOTE(lidiz) Alternatively, we can hack "__getattribute__" to perform # automatic patching for us. But that means the overhead of creating an # extra Python function spreads to every single send and receive. -# denotes the type returned from unary calls -U = TypeVar("U") -# denotes the type yielded from streaming calls -S = TypeVar("S") - class _WrappedCall(aio.Call): def __init__(self): @@ -82,8 +79,8 @@ async def wait_for_connection(self): raise exceptions.from_grpc_error(rpc_error) from rpc_error -class _WrappedUnaryResponseMixin(Generic[U], _WrappedCall): - def __await__(self) -> Iterator[U]: +class _WrappedUnaryResponseMixin(Generic[P], _WrappedCall): + def __await__(self) -> Iterator[P]: try: response = yield from self._call.__await__() return response @@ -91,17 +88,17 @@ def __await__(self) -> Iterator[U]: raise exceptions.from_grpc_error(rpc_error) from rpc_error -class _WrappedStreamResponseMixin(Generic[S], _WrappedCall): +class _WrappedStreamResponseMixin(Generic[P], _WrappedCall): def __init__(self): self._wrapped_async_generator = None - async def read(self) -> S: + async def read(self) -> P: try: return await self._call.read() except grpc.RpcError as rpc_error: raise exceptions.from_grpc_error(rpc_error) from rpc_error - async def _wrapped_aiter(self) -> AsyncGenerator[S, None]: + async def _wrapped_aiter(self) -> AsyncGenerator[P, None]: try: # NOTE(lidiz) coverage doesn't understand the exception raised from # __anext__ method. It is covered by test case: @@ -111,7 +108,7 @@ async def _wrapped_aiter(self) -> AsyncGenerator[S, None]: except grpc.RpcError as rpc_error: raise exceptions.from_grpc_error(rpc_error) from rpc_error - def __aiter__(self) -> AsyncGenerator[S, None]: + def __aiter__(self) -> AsyncGenerator[P, None]: if not self._wrapped_async_generator: self._wrapped_async_generator = self._wrapped_aiter() return self._wrapped_async_generator @@ -134,30 +131,30 @@ async def done_writing(self): # NOTE(lidiz) Implementing each individual class separately, so we don't # expose any API that should not be seen. E.g., __aiter__ in unary-unary # RPC, or __await__ in stream-stream RPC. -class _WrappedUnaryUnaryCall(_WrappedUnaryResponseMixin[U], aio.UnaryUnaryCall): +class _WrappedUnaryUnaryCall(_WrappedUnaryResponseMixin[P], aio.UnaryUnaryCall): """Wrapped UnaryUnaryCall to map exceptions.""" -class _WrappedUnaryStreamCall(_WrappedStreamResponseMixin[S], aio.UnaryStreamCall): +class _WrappedUnaryStreamCall(_WrappedStreamResponseMixin[P], aio.UnaryStreamCall): """Wrapped UnaryStreamCall to map exceptions.""" class _WrappedStreamUnaryCall( - _WrappedUnaryResponseMixin[S], _WrappedStreamRequestMixin, aio.StreamUnaryCall + _WrappedUnaryResponseMixin[P], _WrappedStreamRequestMixin, aio.StreamUnaryCall ): """Wrapped StreamUnaryCall to map exceptions.""" class _WrappedStreamStreamCall( - _WrappedStreamRequestMixin, _WrappedStreamResponseMixin[S], aio.StreamStreamCall + _WrappedStreamRequestMixin, _WrappedStreamResponseMixin[P], aio.StreamStreamCall ): """Wrapped StreamStreamCall to map exceptions.""" # public type alias denoting the return type of async streaming gapic calls -GrpcAsyncStream = _WrappedStreamResponseMixin[S] +GrpcAsyncStream = _WrappedStreamResponseMixin[P] # public type alias denoting the return type of unary gapic calls -AwaitableGrpcCall = _WrappedUnaryResponseMixin[U] +AwaitableGrpcCall = _WrappedUnaryResponseMixin[P] def _wrap_unary_errors(callable_):