Skip to content
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

Not getting the type for the grpc response #277

Closed
NixBiks opened this issue Aug 18, 2021 · 4 comments · Fixed by #278
Closed

Not getting the type for the grpc response #277

NixBiks opened this issue Aug 18, 2021 · 4 comments · Fixed by #278

Comments

@NixBiks
Copy link

NixBiks commented Aug 18, 2021

The type of response in the following code is Unknown.

statement = exchange_statement_pb2.ExchangeStatement(title="JohnDoe")
with grpc.insecure_channel("alba:50051") as channel:
    stub = alba_pb2_grpc.AlbaStub(channel)
    response = stub.TagStatement(statement)

Is that expected?

I'd expect a typed TagReply since my proto looks like this

service Alba {
    rpc TagStatement (ExchangeStatement) returns (TagReply) {}
}

message TagReply {
    repeated string tags = 1;
}
@nipunn1313
Copy link
Owner

Hiya, thanks for the report. Just to check

  1. Are you using mypy-protobuf? It isn't automatic. You have to add flags to your protoc invocation as per here: https://github.com/dropbox/mypy-protobuf#grpc

  2. What does Alba and TagReply look like in your exchange_statement_pb2.pyi and alba_pb2_grpc.pyi?

To help you debug, mypy has a feature called reveal_type which can help you debug what's going on. https://mypy.readthedocs.io/en/stable/common_issues.html#reveal-type

@NixBiks
Copy link
Author

NixBiks commented Aug 19, 2021

  1. Yes and all types works except the response type. I use the following command for generating
python -m grpc_tools.protoc --proto_path=protos --python_out=. --mypy_out=. --grpc_python_out=. --mypy_grpc_out=. protos/greeter.proto
  1. I've made a small toy example instead where response is Unknown.

greeter.proto

syntax = "proto3";


service Greeter {
    rpc HelloWorld (Request) returns (Response) {}
}

message Response {
    repeated string tags = 1;
}

message Request {
    string title = 1;
}

client.py

import grpc
import greeter_pb2
import greeter_pb2_grpc


request = greeter_pb2.Request(title="abc")

with grpc.insecure_channel("alba:50051") as channel:
    stub = greeter_pb2_grpc.GreeterStub(channel)
    response = stub.HelloWorld(request)

Note greeter_pb2.Request and greeter_pb2_grpc.GreeterStub are perfectly fine typed but it seems issues arise with stub.HelloWorld - I get the named inputs but they are all Unknown and the output is also Unknown (see screenshot). It seems stub.HelloWorld is typed with UnaryUnaryMultiCallable[global___Request, global___Response]

Screenshot from 2021-08-19 10-11-05

Workaround

I can get the typing by simply enforcing it with

response: greeter_pb2_grpc.Response = stub.HelloWorld(request)

but it shouldn't be necessary.

@nipunn1313
Copy link
Owner

I reproduced the issue by creating your test case. When running mypy - I see this

➜  greeter_test mypy client.py
greeter_pb2_grpc.pyi:8: error: Skipping analyzing ".greeter_pb2": found module but no type hints or library stubs
greeter_pb2_grpc.pyi:8: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
...

I am not totally sure why, but it doesn't seem to be resolving the relative import of .greeter_pb2 from greeter_pb2_grpc.pyi

we definitely have tests confirming the types here
https://github.com/dropbox/mypy-protobuf/blob/main/test/test_grpc_usage.py#L61
and negative test here
https://github.com/dropbox/mypy-protobuf/blob/main/test_negative/negative_3.8.py#L24

So there's clearly some difference in configuration between the testsuite and the repro that you've provided.

I added a couple of reveal_type to test/test_grpc_usage.py to show that it's working there.

➜  mypy-protobuf git:(main) ✗ mypy test/
test/test_grpc_usage.py:61: note: Revealed type is "testproto.grpc.dummy_pb2.DummyRequest"
test/test_grpc_usage.py:62: note: Revealed type is "testproto.grpc.dummy_pb2.DummyReply*"

I was able to repro the issue in the testsuite by removing test/generated/testproto/grpc/__init__.py, so my guess is that the __init__.py file is critical for the relative import to work.

test/generated/testproto/grpc/dummy_pb2_grpc.pyi:9: error: Skipping analyzing ".dummy_pb2": found module but no type hints or library stubs  [import]
test/generated/testproto/grpc/dummy_pb2_grpc.pyi:9: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports

From this - I was able to get your testcase to work by adding an __init__.py next to greeter_pb2.py

Sadly protobuf itself does absolute imports (protocolbuffers/protobuf#881) - and doesn't produce __init__.py files.

I will consider using absolute imports similarly in the .pyi files for grpc after consulting with the others. With absolute imports this problem doesn't so much arise - since mypy looks for the top level on the path. Then your original plan will work without __init__.py files.

@NixBiks
Copy link
Author

NixBiks commented Aug 20, 2021

Wow great investigative work. Thanks for that!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants