-
Notifications
You must be signed in to change notification settings - Fork 10.5k
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
[Aio] Use Metadata type #23045
Conversation
@@ -56,20 +59,12 @@ | |||
), | |||
( | |||
TypeError, | |||
(({}, {}),), | |||
((None, {}),), |
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.
The invalid metadata test is testing if our library is resistant against badly formed metadata. Maybe we should test both cases?
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.
Please take a look at the corner cases here. If certain bad value crash the process, we should try to prevent it from happening.
self._initial_metadata = initial_metadata | ||
self._trailing_metadata = trailing_metadata | ||
self._initial_metadata = Metadata(*(initial_metadata or ())) | ||
self._trailing_metadata = Metadata(*(trailing_metadata or ())) |
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.
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
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.
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?
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.
For me I'm happy! WDYT @lidizheng | @gnossen ?
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.
Agree. Deterministic Metadata
is better.
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.
8f52446
to
6e83eb7
Compare
@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: |
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.
What's the reasoning behind the conditional here? Shouldn't return cls(*raw_metadata)
just work?
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.
Yes, but raw_metadata
can be None.
I'll change it to be explicit, like
if raw_metadata is None: ...
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.
@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, |
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.
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
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.
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?
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.
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 ?
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.
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?
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.
up to you @rmariano !
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.
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. |
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.
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
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.
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.
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.
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, {}),), |
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.
Please take a look at the corner cases here. If certain bad value crash the process, we should try to prevent it from happening.
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.
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!
@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 |
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 |
@lidizheng oh thanks, now I get it. Sorry I thought it was restored, but yes, I'll add the case for |
Code formatted.
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. |
Known failures: #23190 |
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 casesdicts
, 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 wasNone
. The idea being to always use anio.Metadata
object, to have consistent types (as it already behaves as a dictionary, for instance).cc/ @pfreixes @lidizheng @gnossen