shabbyrobe / grpc-stubs

gRPC typing stubs for Python
MIT License
35 stars 21 forks source link

The object returned from `UnaryStreamMultiCallable` and `StreamStreamMultiCallable` should also be a `Future` #41

Open bkeryan opened 1 year ago

bkeryan commented 1 year ago

Description of issue

The object returned from grpc.UnaryStreamMultiCallable and grpc.StreamStreamMultiCallable is a Call, an iterator of response values, and a Future, but the grpc-stubs definition of CallIterator[TResponse] is only a Call and an iterator of response values.

Here's a link to the documentation for this return value: https://grpc.github.io/grpc/python/grpc.html#grpc.UnaryStreamMultiCallable

Returns:

An object that is a Call for the RPC, an iterator of response values, and a Future for the RPC. Drawing response values from the returned Call-iterator may raise RpcError indicating termination of the RPC with non-OK status.

I attached an example main.py that calls a UnaryStreamMultiCallable and uses Future methods done() and exception() to query its status. This works correctly at runtime (the exception is expected).

Exception (expected): <_MultiThreadedRendezvous of RPC that terminated with:
    status = StatusCode.UNAVAILABLE
    details = "failed to connect to all addresses; last error: UNKNOWN: ipv4:127.0.0.1:1234: Failed to connect to remote host: Connection refused"
    debug_error_string = "UNKNOWN:failed to connect to all addresses; last error: UNKNOWN: ipv4:127.0.0.1:1234: Failed to connect to remote host: Connection refused {created_time:"2023-05-22T19:30:15.558641584-05:00", grpc_status:14}"
>

However, it fails type checking with mypy:

main.py:12: error: "CallIterator[HelloReply]" has no attribute "done"  [attr-defined]
main.py:12: error: "CallIterator[HelloReply]" has no attribute "exception"  [attr-defined]
main.py:13: error: "CallIterator[HelloReply]" has no attribute "exception"  [attr-defined]
Found 3 errors in 1 file (checked 1 source file)

Minimum Reproducible Example

main.py ```py from __future__ import annotations import grpc from hellostreamingworld_pb2 import HelloRequest, HelloReply from hellostreamingworld_pb2_grpc import MultiGreeterStub if __name__ == "__main__": channel = grpc.insecure_channel("localhost:1234") stub = MultiGreeterStub(channel) stream = stub.sayHello(HelloRequest(name="world", num_greetings="3")) stream.initial_metadata() if stream.done() and stream.exception() is not None: ex = stream.exception() print(f"Exception (expected): {ex}") ```
run.sh ```sh #!/usr/bin/env bash set -o errexit -o nounset -o pipefail python -m venv venv source ./venv/bin/activate pip install grpcio grpcio-tools grpc-stubs mypy mypy-protobuf python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. --pyi_out=. --mypy_grpc_out=. hellostreamingworld.proto python main.py python -m mypy main.py ```
hellostreamingworld.proto ```sh // Copyright 2015 gRPC authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; option java_package = "ex.grpc"; option objc_class_prefix = "HSW"; package hellostreamingworld; // The greeting service definition. service MultiGreeter { // Sends multiple greetings rpc sayHello (HelloRequest) returns (stream HelloReply) {} } // The request message containing the user's name and how many greetings // they want. message HelloRequest { string name = 1; string num_greetings = 2; } // A response message containing a greeting message HelloReply { string message = 1; } ```
Full output ``` Requirement already satisfied: grpcio in ./venv/lib/python3.9/site-packages (1.55.0) Requirement already satisfied: grpcio-tools in ./venv/lib/python3.9/site-packages (1.55.0) Requirement already satisfied: grpc-stubs in ./venv/lib/python3.9/site-packages (1.53.0.2) Requirement already satisfied: mypy in ./venv/lib/python3.9/site-packages (1.3.0) Requirement already satisfied: mypy-protobuf in ./venv/lib/python3.9/site-packages (3.4.0) Requirement already satisfied: protobuf<5.0dev,>=4.21.6 in ./venv/lib/python3.9/site-packages (from grpcio-tools) (4.23.1) Requirement already satisfied: setuptools in ./venv/lib/python3.9/site-packages (from grpcio-tools) (58.1.0) Requirement already satisfied: mypy-extensions>=1.0.0 in ./venv/lib/python3.9/site-packages (from mypy) (1.0.0) Requirement already satisfied: typing-extensions>=3.10 in ./venv/lib/python3.9/site-packages (from mypy) (4.6.0) Requirement already satisfied: tomli>=1.1.0 in ./venv/lib/python3.9/site-packages (from mypy) (2.0.1) Requirement already satisfied: types-protobuf>=3.20.4 in ./venv/lib/python3.9/site-packages (from mypy-protobuf) (4.23.0.1) WARNING: You are using pip version 22.0.4; however, version 23.1.2 is available. You should consider upgrading via the '/tmp/unary_stream_call_future/venv/bin/python -m pip install --upgrade pip' command. Writing mypy to hellostreamingworld_pb2_grpc.pyi Exception (expected): <_MultiThreadedRendezvous of RPC that terminated with: status = StatusCode.UNAVAILABLE details = "failed to connect to all addresses; last error: UNKNOWN: ipv4:127.0.0.1:1234: Failed to connect to remote host: Connection refused" debug_error_string = "UNKNOWN:failed to connect to all addresses; last error: UNKNOWN: ipv4:127.0.0.1:1234: Failed to connect to remote host: Connection refused {created_time:"2023-05-22T19:47:20.563451309-05:00", grpc_status:14}" > main.py:12: error: "CallIterator[HelloReply]" has no attribute "done" [attr-defined] main.py:12: error: "CallIterator[HelloReply]" has no attribute "exception" [attr-defined] main.py:13: error: "CallIterator[HelloReply]" has no attribute "exception" [attr-defined] Found 3 errors in 1 file (checked 1 source file) ```
shabbyrobe commented 1 year ago

Nice find! Thank you for the bug report, and thank you very much for the MRE. I've taken a run at a fix here, your MRE made verifying this and writing the test trivial. Does this look like it will solve your problem? https://github.com/shabbyrobe/grpc-stubs/pull/42

bkeryan commented 1 year ago

Thanks, I tried it out and left feedback on the PR.