This repository was archived by the owner on Feb 23, 2026. It is now read-only.
Repository navigation
feat: add type annotations to wrapped grpc calls #554
Merged
Merged
Changes from 8 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
709eb49
add types to grpc call wrappers
daniel-sanche 2026974
fixed tests
daniel-sanche f65b0c7
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] 373ef40
changed type
daniel-sanche 2c20dec
changed async types
daniel-sanche 430e033
added tests
daniel-sanche 3cf50bb
fixed lint issues
daniel-sanche 4f8fbcd
Merge branch 'main' into type-annotate-grpc-wrappers
daniel-sanche b817527
Update tests/asyncio/test_grpc_helpers_async.py
daniel-sanche 623becd
turned GrpcStream into a type alias
daniel-sanche 781cdee
added test for GrpcStream
daniel-sanche 9c3cd54
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] 5d674e7
added comment
daniel-sanche 06a5ea9
reordered types
daniel-sanche 10ef5bf
changed type var to P
daniel-sanche 7e43af7
Merge branch 'main' into type-annotate-grpc-wrappers
daniel-sanche File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,8 @@ | |
| import asyncio | ||
| import functools | ||
|
|
||
| from typing import Generic, Iterator, AsyncGenerator, TypeVar | ||
|
|
||
| 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): | ||
|
|
@@ -75,26 +82,26 @@ async def wait_for_connection(self): | |
| raise exceptions.from_grpc_error(rpc_error) from rpc_error | ||
|
|
||
|
|
||
| class _WrappedUnaryResponseMixin(_WrappedCall): | ||
| def __await__(self): | ||
| class _WrappedUnaryResponseMixin(Generic[U], _WrappedCall): | ||
| def __await__(self) -> Iterator[U]: | ||
| try: | ||
| response = yield from self._call.__await__() | ||
| return response | ||
| except grpc.RpcError as rpc_error: | ||
| raise exceptions.from_grpc_error(rpc_error) from rpc_error | ||
|
|
||
|
|
||
| class _WrappedStreamResponseMixin(_WrappedCall): | ||
| class _WrappedStreamResponseMixin(Generic[S], _WrappedCall): | ||
| 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 +111,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 +134,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 alias denoting the return type of streaming gapic calls | ||
| GrpcAsyncStream = _WrappedStreamResponseMixin[S] | ||
| # public type alias denoting the return type of unary gapic calls | ||
| AwaitableGrpcCall = _WrappedUnaryResponseMixin[U] | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let me know if you have other naming suggestions for these ( I liked |
||
|
|
||
|
|
||
| def _wrap_unary_errors(callable_): | ||
| """Map errors for Unary-Unary async callables.""" | ||
| grpc_helpers._patch_callable_name(callable_) | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think this could be a breaking change? Could we keep the name as
_StreamingResponseIterator? There are many hits for_StreamingResponseIteratorin Google search. I'm worried that changing_StreamingResponseIteratorcould cause issues downstream in user code as it is a response we provide.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, I'll create a type alias instead, like I did on the async side
I thought it would be safe to change since it's a private class, but better to be on the safe side