-
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
A better way to annotation the AsyncStub #536
Comments
Like these: class ContinuumStub:
def __init__(self, channel: typing.Union[grpc.Channel, grpc.aio.Channel]) -> None: ...
Tick: grpc.UnaryUnaryMultiCallable[
essence.service_pb2.TickRequest,
essence.service_pb2.HistoryResult,
] | grpc.aio.UnaryUnaryMultiCallable[
essence.service_pb2.TickRequest,
essence.service_pb2.HistoryResult,
] But it didn't seem like the best practise, because user could use a |
Faced same problems with async support. If I try to run the code that imports my The only workaround is to defined custom factory function with type string and add type ignore for return statement. def create_my_stub(channel: grpc.aio.Channel) -> "my_pb2_grpc.MyServiceAsyncStub":
return my_pb2_grpc.MyServiceStub(channel) # type: ignore[return-value] Then use this factory function in your code with normal mypy support. P.S. I think that mypy protobuf plugin should generate one function that may return either sync stub or async stub according to channel type in input. class _GreeterServiceSyncStub:
Greet: grpc.UnaryUnaryMultiCallable[
my_pb2.GreetRequest,
my_pb2.GreetResponse,
]
class _GreeterServiceAsyncStub:
Greet: grpc.aio.UnaryUnaryMultiCallable[
my_pb2.GreetRequest,
my_pb2.GreetResponse,
]
@typing.overload
def GreeterServiceStub(channel: grpc.Channel) -> _GreeterServiceSyncStub:
...
@typing.overload
def GreeterServiceStub(channel: grpc.aio.Channel) -> _GreeterServiceAsyncStub:
... |
seems reasonable! Would take an improvement as a PR if you can get it working. |
I tried the above out here: https://github.com/artificialinc/mypy-protobuf/tree/aidan/async-stub-overload It didn't really work. As far as I can tell you can't overload classes, and without the proper runtime definition of that function, it falls apart in certain ways. It works in the simple test case, but because it isn't typed as a class things get weird when passing the type around. And so you end up with an object that is a class, typed as a function. |
But I did try something else that seems promising. Using the above example, this is what gets generated:
The overloaded init methods set the generic type on the stub. So when an asyncio channel is passed in, it sets the correct Type vars. Pushed to the same branch. Will do some more testing |
Example of code for the DummyService
|
Bummer, this also falls apart when you are trying to use the type of the Stub.
|
Ok, deeper down the rabbit hole. Using TypeVar default parameter, and a type alias I think I've restored existing behavior when explicitly typing something, and when inferring types i've made an improvement
|
Ok, I think I actually have something that works. The thing that caught me up was I had to quote the
|
After some time I decided to implement my own stub generator https://github.com/zerlok/pyprotostuben . It allows you to choose to generate either async or sync stubs. There better support with HasField & WhichOneof + more clear required parameters in constructor. |
I was about to suggest this as a solution, since most people will probably want to work either with sync or async stubs, but not both. Can we just have a protoc option to make the stub async or sync? I would rather have an option in this project, which is trusted and widely maintained, than having to switch to some other stub gen. |
Here is a quick attempt at implementing this: I tried it out and seems to work well. If this solution would be accepted, I can finish up the PR, updating docs and adding tests. @nipunn1313 |
I am writing async code.
The proto file is like these:
The pyi file is partially like these:
The ContinuumStub has 2 different params for its init function, which are Channel and aio.Channel. When using aio.Channel, the Stub instance need a type:ignore or typing.cast to convert to AsyncStub, like:
typing.cast is not working here because there isn't a true AsyncStub here, AsyncStub only exists in pyi file.
To solve this problem, maybe using Generic or overload the Tick function in pyi file?
Any idea?
The text was updated successfully, but these errors were encountered: