-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Implement AMP version 2 #1417
Implement AMP version 2 #1417
Conversation
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 the contribution
|
@rodrigc I have added the type annotations as you requested. May I resolve the comments you added ? |
| @@ -2613,6 +2634,35 @@ def connectionLost(self, reason): | |||
| self.transport = None | |||
|
|
|||
|
|
|||
| class AMPv2(AMP): | |||
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.
This class and all added methods here need docstrings.
|
@jothan At the top-level Twisted directory, you can run |
|
@rodrigc Thanks for the tip. However, I'm a bit stumped by the remaining errors, do you have any tips ? |
|
Alright, I figured out which type annotations I needed, mypy is now happy. |
|
Thanks for being patient and submitting the annotations. This is slowly making the Twisted codebase better, and easier to understand. |
|
@rodrigc I totally understand the need for those annotations, especially in a large code base. My AMP implementation is in Rust with types everywhere ;P https://github.com/jothan/amp-async |
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 think this looks OK, but I'll leave open for another reviewer to give it a once over.
Nice to see you have experience writing AMP protocol parsers in Rust. I need to learn Rust one of these days. :)
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'm sorry that I didn't have time to get in a code review before this landed, but my attention was brought to it and I have some post-hoc feedback.
| @@ -0,0 +1 @@ | |||
| Implement the AMP version 2 protocol to allow values greater than 64k. | |||
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.
We shouldn't have duplicate NEWS entries like this, as it produces useless duplicative noise in the changelog. The correct thing to do here is to close the second ticket as a duplicate in trac, then have there be one NEWS file.
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.
You might want to update the guidelines in this case, from the news fragment instructions: " You should replace with the ticket number which is being resolved by the change (if multiple tickets are resolved, multiple files with the same contents should be added). "
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.
@jothan Hmm. Apparently I'm wrong about this. I thought that this was an idiom that predated our usage of towncrier, but apparently towncrier adopted this same idiom of hashing the contents of the files; I just created a sample project to test it out and it does the right thing in all the edge cases that I thought would not be handled. Sorry for the mistake! I am still probably going to be rolling this back out for the other reasons discussed below, but at least we can forget about this...
| @@ -239,6 +239,7 @@ def trapZero(result): | |||
|
|
|||
| __all__ = [ | |||
| "AMP", | |||
| "AMPv2", | |||
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.
This should not be exposed as a separate class, but rather a protocol-version parameter.
| @@ -608,7 +609,7 @@ class IncompatibleVersions(AmpError): | |||
| PROTOCOL_ERRORS = {UNHANDLED_ERROR_CODE: UnhandledCommand} | |||
|
|
|||
|
|
|||
| class AmpBox(dict): | |||
| class AmpBox(Dict[bytes, bytes]): | |||
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.
We really should have landed type annotations as a separate, prior change. They're not related to "AMPv2".
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 know, sorry for the noise, I wanted to follow trunk closely with tests passing.
| @@ -657,15 +658,15 @@ def copy(self): | |||
| newBox.update(self) | |||
| return newBox | |||
|
|
|||
| def serialize(self): | |||
| def serialize(self, version2: bool = False) -> bytes: | |||
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.
This "version2" parameter needs a @param in the docstring.
| vio = BytesIO(v) | ||
| del v | ||
|
|
||
| # If the value is an exact multiple of 65535, the last |
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.
how is the value "an exact multiple" of 65535 if it is not 65535 itself?
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.
Guess I should remove the word "exact" here.
| # (i.e. when read() returns EOF). | ||
| chunk = vio.read(MAX_VALUE_LENGTH) | ||
| while True: | ||
| w(pack("!H", len(chunk))) |
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 realize that this is coming from the "spec" on amp-protocol.net, but there were never any discussions I saw about how to implement a V2. Given that this is neither backwards compatible nor has any wire-level negotiation it seems like a pretty bad feature for a version bump.
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.
(A better way to have done this would be to normalize the meaning of repeated keys, but I digress.)
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.
Well, it is mostly backwards compatible as long as values are smaller than 65534 bytes. Considering it is a breaking change, it should be signaled via a compatibility version change. I would have called AMP v1.1, but AMP v2 already existed conceptually.
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 you have a key which is just a bit over 64k bytes, you end up screwing up the alternation of keys and values, which means that the failure mode involves the peer hanging forever; that's a pretty bad failure mode.
There's a way to do this which is entirely backwards compatible, though; just send the same key repeatedly, or send a key with .1, .2, etc appended to it. This ends up adding 0.02% or so worth of overhead to the data (and if you have really large data, you want what's described on https://twistedmatrix.com/trac/ticket/2529 anyway, not just a way to circumvent the length-prefix limit).
You can also do this as a new parameter-type declaration, which also allows you to interrogate your peer's capabilities by checking for an UNHANDLED response if you have something you want to upgrade.
If we wanted to do something dramatically better, netstrings are the way to go; arguably AMP should have used these in the first place.
| def callRemoteString(self, command, requiresAnswer=True, **kw): | ||
| def callRemoteString( | ||
| self, command: bytes, requiresAnswer: bool = True, **kw: bytes | ||
| ) -> Optional[Deferred]: |
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.
Optional[Deferred] is the wrong way to specify this signature; what you want is an @overload to correlate the value of requiresAnswer to the type of the response.
| assert self.transport is not None | ||
| self.transport.write(box.serialize(version2=True)) | ||
|
|
||
| def proto_value(self, string: bytes) -> str: |
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.
These methods do not belong on an AMP subclass, if they belong on a subclass at all, but a BinaryBoxProtocol subclass.
There's a mea-culpa here too; the inheritance-for-convenience design of AMP was a mistake, and should not have created this dichotomy; but, this is also why this should not be a new class, though, and instead just a parameter to BinaryBoxProtocol that AMP can propagate.
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.
Yeah, I got hit by that. My initial unpublished patch was version parameters everywhere.
|
@jothan in case you are not on the Twisted mailing list, I began a discussion about this here: https://twistedmatrix.com/pipermail/twisted-python/2020-October/065288.html I'm leaning towards doing a revert based on that. Thanks for your contribution to Twisted, sorry to burden it with more work! |
|
@jothan Would you mind re-submitting just the type-annotation bits of this PR first, since they're not really related to the other stuff happening here, and would be useful to integrate on their own? |
Implement AMP v2 as described here: https://amp-protocol.net/conversations_v2.html
Contributor Checklist:
tox -e black-reformatto format my patch to meet the Twisted Coding Standardreviewto the keywords field in Trac, and putting a link to this PR in the comment; it shows up in https://twisted.reviews/ now.