Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: AsyncIO Integration [Part 3] #29

Merged
merged 1 commit into from Jun 4, 2020

Conversation

lidizheng
Copy link
Contributor

Children PR of #26 and #28.

This PR includes AsyncIO version of:

  • LRO client
  • gRPC wrappers & helpers

Related #23

@googlebot googlebot added the cla: yes This human has signed the Contributor License Agreement. label May 18, 2020
@lidizheng lidizheng marked this pull request as ready for review May 18, 2020 22:34
google/api_core/grpc_helpers_async.py Show resolved Hide resolved
google/api_core/grpc_helpers_async.py Outdated Show resolved Hide resolved
Comment on lines +237 to +270
class FakeUnaryUnaryCall(_WrappedUnaryUnaryCall):
"""Fake implementation for unary-unary RPCs.

It is a dummy object for response message. Supply the intended response
upon the initialization, and the coroutine will return the exact response
message.
"""

def __init__(self, response=object()):
self.response = response
self._future = asyncio.get_event_loop().create_future()
self._future.set_result(self.response)

def __await__(self):
response = yield from self._future.__await__()
return response


class FakeStreamUnaryCall(_WrappedStreamUnaryCall):
"""Fake implementation for stream-unary RPCs.

It is a dummy object for response message. Supply the intended response
upon the initialization, and the coroutine will return the exact response
message.
"""

def __init__(self, response=object()):
self.response = response
self._future = asyncio.get_event_loop().create_future()
self._future.set_result(self.response)

def __await__(self):
response = yield from self._future.__await__()
return response

async def wait_for_connection(self):
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these intended for use by tests only? I'd prefer they be named Mock... instead of Fake... but don't feel too strongly about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered Mock... naming, but they are not magic mock objects. It might misdirect users' expectation of its behavior. I'm happy to change if you have better naming.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's leave it as is then. @software-dov do you have any thoughts here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to not mock naming, although I'm a little confused as to why they don't live in the unit test file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think leaving them here makes it easier for dependent libraries to use it in their own tests.

(HttpMock in the apiary library for instance)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the 'correct' think to do then would be to have a testlib. I am in general not a fan of mixing test code and production code, but it's not a hill I'm willing to die on.

Copy link
Contributor Author

@lidizheng lidizheng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@busunkim96 Thank you for the suggestions. PTALA.

google/api_core/grpc_helpers_async.py Outdated Show resolved Hide resolved
Comment on lines +237 to +270
class FakeUnaryUnaryCall(_WrappedUnaryUnaryCall):
"""Fake implementation for unary-unary RPCs.

It is a dummy object for response message. Supply the intended response
upon the initialization, and the coroutine will return the exact response
message.
"""

def __init__(self, response=object()):
self.response = response
self._future = asyncio.get_event_loop().create_future()
self._future.set_result(self.response)

def __await__(self):
response = yield from self._future.__await__()
return response


class FakeStreamUnaryCall(_WrappedStreamUnaryCall):
"""Fake implementation for stream-unary RPCs.

It is a dummy object for response message. Supply the intended response
upon the initialization, and the coroutine will return the exact response
message.
"""

def __init__(self, response=object()):
self.response = response
self._future = asyncio.get_event_loop().create_future()
self._future.set_result(self.response)

def __await__(self):
response = yield from self._future.__await__()
return response

async def wait_for_connection(self):
pass
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered Mock... naming, but they are not magic mock objects. It might misdirect users' expectation of its behavior. I'm happy to change if you have better naming.

Comment on lines 42 to 45
if client_info is not None:
user_agent_metadata = [client_info.to_grpc_metadata()]
else:
user_agent_metadata = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style nit: what about

metadata = [client_info.to_grpc_metadata()] if client_info is not None else None

?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. Updated to the suggested style.

Comment on lines +237 to +270
class FakeUnaryUnaryCall(_WrappedUnaryUnaryCall):
"""Fake implementation for unary-unary RPCs.

It is a dummy object for response message. Supply the intended response
upon the initialization, and the coroutine will return the exact response
message.
"""

def __init__(self, response=object()):
self.response = response
self._future = asyncio.get_event_loop().create_future()
self._future.set_result(self.response)

def __await__(self):
response = yield from self._future.__await__()
return response


class FakeStreamUnaryCall(_WrappedStreamUnaryCall):
"""Fake implementation for stream-unary RPCs.

It is a dummy object for response message. Supply the intended response
upon the initialization, and the coroutine will return the exact response
message.
"""

def __init__(self, response=object()):
self.response = response
self._future = asyncio.get_event_loop().create_future()
self._future.set_result(self.response)

def __await__(self):
response = yield from self._future.__await__()
return response

async def wait_for_connection(self):
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to not mock naming, although I'm a little confused as to why they don't live in the unit test file.

@lidizheng
Copy link
Contributor Author

@software-dov PTALA.

@busunkim96
Copy link
Contributor

@lidizheng It looks like the unit tests need to be tweaked:

func = <function _wrap_unary_errors.<locals>.error_remapped_callable at 0x7fb165893790>
default_retry = <google.api_core.retry_async.AsyncRetry object at 0x7fb166304160>
default_timeout = <google.api_core.timeout.ExponentialTimeout object at 0x7fb1663046d0>
client_info = <google.api_core.gapic_v1.client_info.ClientInfo object at 0x7fb1664eeeb0>

    def wrap_method(
            func,
            default_retry=None,
            default_timeout=None,
            client_info=client_info.DEFAULT_CLIENT_INFO,
    ):
        """Wrap an async RPC method with common behavior.

        Returns:
            Callable: A new callable that takes optional ``retry`` and ``timeout``
                arguments and applies the common error mapping, retry, timeout,
                and metadata behavior to the low-level RPC method.
        """
        func = grpc_helpers_async.wrap_errors(func)

        metadata = [client_info.to_grpc_metadata()] if client_info is not None else None

        return general_helpers.wraps(func)(_GapicCallable(
>           func, default_retry, default_timeout, metadata=user_agent_metadata))
E       NameError: name 'user_agent_metadata' is not defined

Copy link
Contributor

@software-dov software-dov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Just one minor optional tweak and a vote from my end that the test classes be moved into a standalone test library.

google/api_core/operations_v1/__init__.py Outdated Show resolved Hide resolved
* LRO client
* gRPC wrappers & helpers
* With unit tests & docs
@busunkim96 busunkim96 merged commit 7d8d580 into googleapis:master Jun 4, 2020
gcf-merge-on-green bot pushed a commit that referenced this pull request Jun 4, 2020
@busunkim96 busunkim96 mentioned this pull request Jun 17, 2020
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes This human has signed the Contributor License Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants