-
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 aio support #217
base: main
Are you sure you want to change the base?
add aio support #217
Conversation
1. add grpc.aio.Server to generated `Servicer_to_server` stub Signed-off-by: weiwee <[email protected]>
add use_aio flag use something like following script to generate `aio-featured` grpc stubs ```sh protoc \ --python_out=output/location \ --mypy_out=readable_stubs:output/location \ --grpc_out=output/location \ --mypy_grpc_out=aio,readable_stubs:output/location ``` Signed-off-by: weiwee <[email protected]>
looks lovely! Thanks for the PR! From my reading of the docs of GRPC - it seems that AIO is trying to replace the original server Seems like there are a few options here. I haven't directly used this GRPC generation, so will defer to folks on what makes the most sense here:
def add_DummyServiceServicer_to_server(servicer: DummyServiceServicer, server: Union[Server, AioServer]) -> None: ...
def add_DummyServiceServicer_to_server(servicer: DummyServiceServicer, server: Server) -> None: ...
# w aio flag
def add_DummyServiceServicer_to_server(servicer: DummyServiceServicer, server: AioServer) -> None: ...
def add_DummyServiceServicer_to_server(servicer: DummyServiceServicer, server: Server) -> None: ...
# w aio flag
def add_DummyServiceServicer_to_server(servicer: DummyServiceServicer, server: Union[Server, AioServer]) -> None: ... As implemented - I believe it's option 3. I think I'd prefer either option 1 or option 2 (for simplicity / stronger typing). Option 3 appears to add complexity and weakens typing for the aio case. Are there cases in which we'd want to mix and match server types to servicers? I'd be enthusiastic about starting with (1) and adding complexity as needed. More flags -> more complexity to maintain. Want to acknowledge that I don't understand the use case as well as you - so please poke holes in my reasoning here if you have another viewpoint. Thank you! Action Items
Thank you! |
I would agree for using option 1, as this best represents the actual typing of the .py code. Furthermore, (as is my use case) I generate the files once and use them as a shared library across modules, some of which will use AIO and others will use Threading. Also, this implementation seems to be incomplete, as using asyncIO should allow you to change the return value of the generated functions to a union of It's important to keep the union in this case since the AIO package allows you to define the function in either format. I will make some of these changes and update the PR. |
Has there been any movement on this; it would be very useful for me. I also want to second this point: |
Sorry, I've stepped away from this for a bit. At the moment my team is adding |
I believe it depends on shabbyrobe/grpc-stubs#22 - so we can import from I quickly tried to pick this up and got
I haven't been focusing on it. Feel free to pick up this diff and proceed. You'll probably have to co-develop in both shabbyrobe/grpc-stubs and here. I'm sure @shabbyrobe would be happy to review that side of it if you manage to get it all working. |
I believe we want to generate something like this class DummyServiceServicer:
def UnaryUnary(self,
request: DummyRequest,
context: grpc.ServicerContext,
) -> typing.Union[DummyReply, typing.Awaitable[DummyReply]]: ...
def add_DummyServiceServicer_to_server(
servicer: DummyServiceServicer,
server: typing.Union[grpc.Server, grpc.aio.AioServer]) -> None: ... Even better would be something like this A = TypeVar('A', typing.Awaitable, typing.Union) # Using Union as a noop wrapper
class DummyServiceServicer(Generic[A]):
def UnaryUnary(self,
request: DummyRequest,
context: grpc.ServicerContext,
) -> typing.Union[DummyReply, A[DummyReply]]: ...
@overload
def add_DummyServiceServicer_to_server(
servicer: DummyServiceServicer[Awaitable],
server: grpc.aio.AioServer) -> None: ...
@overload
def add_DummyServiceServicer_to_server(
servicer: DummyServiceServicer[Union],
server: grpc.Server) -> None: ... Feels weird using Union as a noop wrapper in this way, but I couldn't think of another way. |
Ah, I remember now where I got stuck. The aio package itself contains typings! 🎉
There seems to be some Issues that are blocking this: My intention was to do as much of the work as I could on the upstream repos as I could before hacking up shims here. This may not be necessary, and it would be useful to have some placeholder functionality until such time. After some deliberation, It seemed impractical to combine the definition for AIO/non-AIO servicers. This is primarily because the I personally am using the same mypy-protobuf generated files in more than one project, some of which are now slowly transitioning to aio (some never will). I am trying to get a feel for all the nuances of the aio package at the moment, but they are less obvious than they seem. It would be advantageous to me to be able to use both types of services in a single set of typings. I understand that this is not everybody's use case, and most people would be well served by a simple flag that compiles one or the other 😅 . |
This makes sense. One idea could be to add a mypy_protobuf proto extension as a service option. Eg Then you'd declare your proto as something like
or
https://developers.google.com/protocol-buffers/docs/proto#customoptions There are several examples of this in pb-jelly (another project I worked on) https://github.com/dropbox/pb-jelly/blob/main/pb-jelly-gen/codegen/rust/extensions.proto - which codegens rust. |
I wrote up #276 - to give you a starting point for the ideas above. It's not really useful on its own, but just wanted to give you something so you wouldn't get stuck with the nuances of getting proto extensions working. |
Please excuse my flip-flopping. But I don't think that setting whether the service is sync/async in the protos is where we should do it? I would argue it should be a flag as @weiwee originally suggested. This is because the server and client (and indeed multiple different versions of each) can each have different implementations of the proto file and that should be supported. ( I would argue this is one of the strengths of grpc/proto 🦾 ). If It's ok with you I'd like to suggest continuing with the compiler flag approach. |
That logic makes sense. The sync/async is a question of implementation, not of spec. There's still a couple of shortcomings to the flag approach
Using flags seems like the lesser of evils here. That being said - if during implementation you can find a way to do it without the flag, and somehow support both sync/async cases (perhaps via |
Looks like we got very close here! Too bad it wasn't ready for me :D. If I can help move this forward, and it's not a huge lift, I'd be happy to do so. |
see #216
This pr add flag indicate mypy-protobuf should generate grpc stubs with
aio.Server
supportscript like this
generate from dummy.proto to