-
Notifications
You must be signed in to change notification settings - Fork 79
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
Add support for proto2 extensions #145
Add support for proto2 extensions #145
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.
This looks great! Couple of comments around the testing, then we can go with it.
Trying to think of how we can add a negative test here - to confirm that this mypy typing catches bugs - has better typing than Any. Perhaps in test_negative.py
we can add something of the sort
x = 5
x = Bar.ext # E
Where we'd expect a type error of a FieldDescriptor being assigned to an int.
Thank you so much for the contribution! Thank you! |
Would also ask that you update CHANGELOG.md with the new change in the "Upcoming" section |
The PR got more complicated, but we now have correct type inference for extensions. |
Hmm - looks like this diff has developed some additional complexity for typing the extensions dict - and potentially has some points up for debate. If you were to split the PR into uncontroversial part (w/o typing extensions dict) - we can move forward with that easily, so we can focus discussion on this more controversial complexity and find the right way to solve it. I don't want to hold up this entire diff on this discussion - since it's clearly adding value! |
@nipunn1313 I made a more minimal PR here: #149 |
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.
Sorry Forgot to hit submit on the comments! Here they are.
class _ExtensionDict(typing___Generic[_ContainerMessageT]): | ||
def __getitem__(self, extension_handle: _ExtensionFieldDescriptor[_ContainerMessageT, _ExtenderMessageT]) -> _ExtenderMessageT: ... | ||
|
||
def __setitem__(self, extension_handle: _ExtensionFieldDescriptor[_ContainerMessageT, _ExtenderMessageT], value: _ExtenderMessageT) -> 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 you explain what's going on here in this file?
1 - Why does this need to be autogenerated? Seems like it potentially should belong either directly in typeshed itself, or inside the mypy_protobuf library as something to be imported
2 - Can you explain what value this is adding over a Dict[ContainerMessage, ExtenderMessage]
?
This file does indeed appear to add complexity, and I'm not sure I understand what value the complexity adds over a simple dict.
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.
1 - Why does this need to be autogenerated? Seems like it potentially should belong either directly in typeshed itself, or inside the mypy_protobuf library as something to be imported
Yes I agree the autogeneration is not the best solution. I can think of 3 possibilities:
-
Generate a file as part of the protoc plugin (i.e. what we have currently). I changed it to use a template file instead of having python code to write the file on the fly. The benefit of this is that we can ensure these stubs will be present whenever
mypy
is run and don't add any new package requirements. -
As part of the
mypy_protobuf
library. This simplifies the generated code and still ensures that the same version of the base classes used to generate stubs is used to evaluate them (assuming the user doesn't change library versions in between). However, I think it also means thatmypy_protobuf
would need to be present whenevermypy
is run, which is not currently a requirement. -
As part of the
typeshed
. This addresses the issue of needingmypy_protobuf
present when runningmypy
. However, it means that we'd require the newmypy
version to have these stubs be present. It would also pose difficulties if we want to change anything about this file in the future.
There may be a way to do either of the latter two options in a "soft" way; i.e. fallback to the minimal extension support already in master
if the respective dependency is not present. I don't have a ton of experience publishing to the typeshed
to know if/how they support API changes across package versions.
2 - Can you explain what value this is adding over a Dict[ContainerMessage, ExtenderMessage]
Two reasons. Message.Extensions
is not a dictionary, and does not support the various other methods associated with one (e.g. you can't do foo.Extensions.values()
). Additionally, I don't think using a static typing would allow for the polymorphism we currently have (i.e. _ExtensionDict
is a class parametrized by _ContainerMessageT
and __{gs}estitem__
is a method of that class parametrized by _ExtenderMessageT
). The actual source code is here. I'm not sure why the typeshed implementation does not include the other methods; I added all the public ones here.
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.
Ah! We can totally add these stubs to typeshed - as long as the stubs match the source code.
We have lots of precedent doing that (much of the google/protobuf typeshed stubs were contributed by dropboxers / folks who work on mypy-protobuf)
Typeshed: https://github.com/python/typeshed/tree/master/third_party/2and3/google/protobuf/internal
https://github.com/python/typeshed/tree/master/third_party/2and3/google/protobuf/internal
You can also test your typeshed change + mypy-protobuf change within mypy-protobuf by doing
CUSTOM_TYPESHED_DIR=~/path/to/your/typeshed/fork ./run_test.sh
Though one gotcha - when developing you should run the typeshed tests first - because errors within typeshed get suppressed when you use CUSTOM_TYPESHED_DIR.
It does mean that we'll have to wait for another release of mypy before we launch the next version of mypy-protobuf, but I think that's ok! #140 is similarly waiting on a typeshed change (you can look at that as an example)
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.
Interesting... I didn't realize the typeshed stubs for protobuf came from this same group (thank you!). Are there any further comments you have on the current code before moving typeshed.pyi
to the official typeshed?
Here are some examples of updates to typeshed for sake of mypy-protobuf |
This will be blocked on python/typeshed#4619 |
the typeshed change landed, and I landed a mypy->typeshed pin bump afterward! I spoke with the mypy maintainers (they're also at Dropbox), and they mentioned that it may be until around November before there's a mypy release w/ your change. From
to
referencing python/mypy@7273e9a |
# correct base model. This fails at runtime but passes mypy. | ||
for x in s1.Extensions: | ||
assert isinstance(x, FieldDescriptor) | ||
y = s1.Extensions[x] # y should be typed as an AnyMessage |
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 comment might leave some confusion as to the why, I think more specifically, explaining what "x" should be typed as will help understand why "y" is an Any
here.
Specifically, to add clarity that y
is Any
because x
came out of an iterator, which can't know the value type.
This is awesome!!! Thank you so much for the contributions here. I left one message about a comment in the tests. Going to go ahead and merge it - feel free to add another PR to update the comment if you're excited. |
Adds support for message extensions. Now if there is a message
then
Bar.ext
will no longer raise a mypy error. Furthermore, we are able to correctly type extension messages, so for something likethe inferred type for
bar
isBar
.