Python aio API typing issues

122 views
Skip to first unread message

Robin McCorkell

unread,
Jan 30, 2023, 6:13:59 PM1/30/23
to grpc.io
I'm trying to use the aio API in Python, and having a lot of issues when it comes to typing my stubs. I think the Python codegen needs to generate separate async stubs and servicers that have different type signatures (either in this project or in mypy-protobuf), thoughts?

Here's why:

mypy-protobuf attempts to generate typing stubs, but these only apply to the sync API, leaving aio usage incorrectly typed. So I tried to fix mypy-protobuf to support the aio API, and ran into a lot of issues. I'd go so far as to say it's impossible to type aio stubs, here's why (C and R are placeholders):

class FooServicer:
    def DoFoo(self, request: DoFooRequest, context: C) -> R: ...

class SyncFooServicer(FooServicer):
    def DoFoo(self, request: DoFooRequest, context: grpc.ServicerContext) -> DoFooResponse: ...

class AsyncFooServicer(FooServicer):
    async def DoFoo(self, request: DoFooRequest, context: grpc.aio.ServicerContext) -> DoFooResponse: ...

So R needs to support both DoFooResponse (in the sync case) and Awaitable[DoFooResponse] (in the async case). A Union could work, but we also need C to be either grpc.ServicerContext or grpc.aio.ServicerContext depending on whether the function is async or not, and I don't think this is possible to express in Python. This is before we even get to streaming requests, where the type of the request itself changes (Iterator vs AsyncIterator).

Effectively, the type of DoFoo in FooServicer is:
Union[
    Callable[[Self, DoFooRequest, grpc.ServicerContext], DoFooResponse],
    Callable[[Self, DoFooRequest, grpc.aio.ServicerContext], Awaitable[DoFooResponse]
]

But this doesn't appear to work correctly with Python type checkers.

I imagine the same thing happens with stubs too, but I haven't worked out how yet.

In addition to all this, we have the fact that sync Servicers can be used with sync Servers and aio Servers (that have a thread pool), but async Servicers can only be used with aio Servers.

Given the difference between the sync and async APIs, I believe the right approach is to generate separate stubs and servicers for the sync vs async case. Then, the sync API can generally stick to only the sync stubs and servicers, while the aio API can use both (given the compatibility).
Reply all
Reply to author
Forward
0 new messages