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

[Aio] Use Metadata type #23045

Merged
merged 10 commits into from
Jun 12, 2020
Merged

Conversation

rmariano
Copy link

Summary

Use the new metadata type (created in #22306), in all places where the code interacted with metadata.

Fixes #21953

Compatibility

Before in the code there was a mix of types to refer to the metadata: tuples, None, and in some cases dicts, because different parts used the metadata in different ways. Now that we have a specific type, it returns an empty metadata object where before it was None. The idea being to always use an io.Metadata object, to have consistent types (as it already behaves as a dictionary, for instance).

cc/ @pfreixes @lidizheng @gnossen

@lidizheng lidizheng added lang/Python release notes: yes Indicates if PR needs to be in release notes labels May 26, 2020
@lidizheng lidizheng changed the title Issue 21953 use metadata type [Aio] Use Metadata type May 26, 2020
@lidizheng lidizheng added this to In progress in gRPC Async API via automation May 26, 2020
src/python/grpcio/grpc/_compression.py Outdated Show resolved Hide resolved
src/python/grpcio/grpc/experimental/aio/_call.py Outdated Show resolved Hide resolved
@@ -56,20 +59,12 @@
),
(
TypeError,
(({}, {}),),
((None, {}),),
Copy link
Contributor

Choose a reason for hiding this comment

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

The invalid metadata test is testing if our library is resistant against badly formed metadata. Maybe we should test both cases?

Copy link
Contributor

Choose a reason for hiding this comment

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

Please take a look at the corner cases here. If certain bad value crash the process, we should try to prevent it from happening.

src/python/grpcio_tests/tests_aio/unit/metadata_test.py Outdated Show resolved Hide resolved
src/python/grpcio_tests/tests_aio/unit/metadata_test.py Outdated Show resolved Hide resolved
self._initial_metadata = initial_metadata
self._trailing_metadata = trailing_metadata
self._initial_metadata = Metadata(*(initial_metadata or ()))
self._trailing_metadata = Metadata(*(trailing_metadata or ()))
Copy link
Collaborator

Choose a reason for hiding this comment

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

If Im not wrong most of the signatures that we have for returning metadata are Optional[MetadataType] [1] , meaning that if there is no metadata a None is returned. If this is the case every time that we build a metadata object we should do that if and only if there are values, having something like this:

self._trailing_metadata = Metadata(*initial_metadata) if initial_metadata is not None else None

Also, note that maybe for avoiding the expansion - which might have a little impact - we could change the contractor of the Metadata for taking one parameter that would be the sequence.

[1] https://github.com/grpc/grpc/pull/23045/files#diff-802047f7d320089cb1d9c7de77c9cae5R107

Copy link
Author

Choose a reason for hiding this comment

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

In the cases where it was returning None it's better to now return an empty metadata object now that we have the more specific type, as that will allow us to treat all calls to these methods the same (without exceptions of the kind if metadata is not None: ... ), so I'd replace the Optional[MetadataType] by Metadata. WDYT?

Copy link
Collaborator

Choose a reason for hiding this comment

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

For me I'm happy! WDYT @lidizheng | @gnossen ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Agree. Deterministic Metadata is better.

src/python/grpcio/grpc/experimental/aio/_call.py Outdated Show resolved Hide resolved
Mariano Anaya added 7 commits June 2, 2020 09:30
In all places where a tuple was used for metadata (in the aio version),
replace it by the new ``Metadata`` class.
* Replace ``MetadataType`` by ``Metadata`` in all places
* Fix annotations
* Use the new ``Metadata.from_tuple`` to create Metadata objects
Using the new aio.Metadata() type instead of tuple.
Replace the signature to allow methods to use the metadata object.
Internally, they'll still wrap the data in a tuple, but the interface
makes it clear that the ``aio.Metadata()`` object is supported.

Remove the ``tuple(<metadata>)`` conversions done in the tests.
The old interface of accepting the metadata as a list, should be kept
due to a backwards incompatibility with a client.

The new ``aio.Metadata()`` type supports iteration, so creating a list
from it, is possible.
@rmariano rmariano force-pushed the issue-21953_use-metadata-type branch from 8f52446 to 6e83eb7 Compare June 2, 2020 10:39
@rmariano
Copy link
Author

rmariano commented Jun 2, 2020

@pfreixes @lidizheng @gnossen feedback has been addressed or replied. PTAL

@@ -37,6 +37,12 @@ def __init__(self, *args: Tuple[MetadataKey, MetadataValue]) -> None:
for md_key, md_value in args:
self.add(md_key, md_value)

@classmethod
def from_tuple(cls, raw_metadata: tuple):
if raw_metadata:
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the reasoning behind the conditional here? Shouldn't return cls(*raw_metadata) just work?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, but raw_metadata can be None.
I'll change it to be explicit, like

if raw_metadata is None: ...

Copy link
Collaborator

@pfreixes pfreixes left a comment

Choose a reason for hiding this comment

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

@rmariano I left a couple of comments!

initial_metadata: Optional[MetadataType] = None,
trailing_metadata: Optional[MetadataType] = None,
initial_metadata: Optional[Metadata] = None,
trailing_metadata: Optional[Metadata] = None,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can be them set to None? If so the return type for the getter methods would need to be changed, for example [1], if not we should remove the Optional (the same for the class type definition)

https://github.com/grpc/grpc/pull/23045/files#diff-802047f7d320089cb1d9c7de77c9cae5R107

Copy link
Author

Choose a reason for hiding this comment

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

Good point, I think we can remove the Optional[] because it's always a Metadata object, however, I wouldn't remove the default argument = None because that would imply changing the position of the parameters (they would have to go right after code), and that could cause backward incompatibilities, and also confuse users.
The proposal is then to remove the Optional[..] annotation but keep the default argument to None. WDYT?

Copy link
Collaborator

Choose a reason for hiding this comment

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

weird :), If we keep the = None I will keep the Optional, regarding backward compatibility the constuctor of AioRpcError is internal, so it menas that If Im not wrong we can turn it to a positional argument.

WDYT @lidizheng ?

Copy link
Contributor

Choose a reason for hiding this comment

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

To me, Optional looks better in terms of conveying the variable is "optional".

Yes, this is an internal API, users won't be impacted. I don't have strong opinion here, either way looks fine to me. Since @rmariano is the author of the PR, maybe he should make the call?

Copy link
Collaborator

Choose a reason for hiding this comment

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

up to you @rmariano !

Copy link
Author

Choose a reason for hiding this comment

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

I'll refactor this, to make these parameters no longer optional.

@@ -248,7 +248,7 @@ async def intercept_stream_stream(


class InterceptedCall:
"""Base implementation for all intecepted call arities.
"""Base implementation for all intercepted call arities.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for fixing the typo. In any case seems that within the _interceptror.py file there are some reference to the old Metadata type like this [1] which would need to be changed, the same for the ClientCallDetaills which is still using an Optional[MetadataType] should this be changed to Metadata? [2]

[1] https://github.com/grpc/grpc/pull/23045/files#diff-173e8bd5d2219ba109f86237680068a4R383
[2] https://github.com/grpc/grpc/blob/master/src/python/grpcio/grpc/experimental/aio/_interceptor.py#L85

Copy link
Collaborator

Choose a reason for hiding this comment

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

One last thing, if the input from the user hasn't changed, so its still providing the metadata for te call using a tuple I wold not change either the ClientCallDetails since this is an interface used by the interceptors for mutating the metadata provided by the user.

Copy link
Contributor

@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.

Thank you for the changes! This should improve the usability of metadata quite a bit. One minor comment, LGTM if it is taken care of.

@@ -56,20 +59,12 @@
),
(
TypeError,
(({}, {}),),
((None, {}),),
Copy link
Contributor

Choose a reason for hiding this comment

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

Please take a look at the corner cases here. If certain bad value crash the process, we should try to prevent it from happening.

@lidizheng
Copy link
Contributor

@pfreixes Is there anything else blocking this PR?
@rmariano Please take a look at the sanity check, also the invalid metadata test.

Copy link
Collaborator

@pfreixes pfreixes left a comment

Choose a reason for hiding this comment

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

Considering that the open conversations are not a blockers and everything if it is needed can be changed later without breaking compatibility, approving the PR!

@rmariano
Copy link
Author

@lidizheng I'll fix the sanity check, but I was also taking a look at the tests for invalid metadata. Basically they're as they were before, but now I'm wondering if they make sense, because when passing something that's not a Metadata object, they should break, which is what the tests are asserting. Is there anything I'm missing?
Maybe those tests can be changed to directly test the new _init_metadata method, rather than composing the entire call object, and awaiting it.

@lidizheng
Copy link
Contributor

when passing something that's not a Metadata object, they should break

Yes. They should break, and they should raise one exception. The test was there because in the past a malformed metadata could cause segfault or abort which crashes entire process. So, to be sure, this test tried various malformed cases to ensure no crashes and exception are raised as expected.

I see you removed (({}, {}),), and added ((None, {}),),, can we add the former one back? It's a corner case, sorry for being nitpicking here.

@rmariano
Copy link
Author

@lidizheng oh thanks, now I get it. Sorry I thought it was restored, but yes, I'll add the case for (({}, {}),),

@rmariano
Copy link
Author

hi @pfreixes @lidizheng I've addressed your comments. Let me know if it looks OK for you, and in case not, I'll iterate further.

@lidizheng
Copy link
Contributor

Known failures: #23190

@lidizheng lidizheng merged commit c47bc1a into grpc:master Jun 12, 2020
gRPC Async API automation moved this from In progress to Done Jun 12, 2020
@rmariano rmariano deleted the issue-21953_use-metadata-type branch June 15, 2020 08:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lang/Python release notes: yes Indicates if PR needs to be in release notes
Projects
Development

Successfully merging this pull request may close these issues.

[Aio] Type of metadata?
5 participants