shabbyrobe/grpc-stubs

`ServerInterceptor.intercept_service` return type doesn't match continuation return type

bkeryan opened this issue · 2 comments

Description of issue

ServerInterceptor.intercept_service returns RpcMethodHandler[TRequest, TResponse] but the continuation returns Optional[RpcMethodHandler[TRequest, TResponse]].

Writing an interceptor that calls return continuation(handler_call_details) like in request_header_validator_interceptor.py causes mypy to emit this error:

error: Incompatible return value type (got "Optional[RpcMethodHandler[grpc.TRequest, grpc.TResponse]]", expected "RpcMethodHandler[grpc.TRequest, grpc.TResponse]")  [return-value]

The docs say that intercept_service may return None.

Minimum Reproducible Example

main.py
from __future__ import annotations
import grpc
import typing

class NullServerInterceptor(grpc.ServerInterceptor):
    def intercept_service(
        self,
        continuation: typing.Callable[
            [grpc.HandlerCallDetails],
            typing.Optional[grpc.RpcMethodHandler[grpc.TRequest, grpc.TResponse]]
        ],
        handler_call_details: grpc.HandlerCallDetails,
    ) -> grpc.RpcMethodHandler[grpc.TRequest, grpc.TResponse]:
        return continuation(handler_call_details)
run.sh
#!/usr/bin/env bash                                                             
set -o errexit -o nounset -o pipefail
python -m venv venv
source ./venv/bin/activate
pip install grpcio grpc-stubs mypy
mypy main.py
Full output
Requirement already satisfied: grpcio in ./venv/lib/python3.9/site-packages (1.57.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.5.0)
Requirement already satisfied: mypy-extensions>=1.0.0 in ./venv/lib/python3.9/site-packages (from mypy) (1.0.0)
Requirement already satisfied: tomli>=1.1.0 in ./venv/lib/python3.9/site-packages (from mypy) (2.0.1)
Requirement already satisfied: typing-extensions>=4.1.0 in ./venv/lib/python3.9/site-packages (from mypy) (4.7.1)
WARNING: You are using pip version 22.0.4; however, version 23.2.1 is available.
You should consider upgrading via the '/tmp/serverinterceptor/venv/bin/python -m pip install --upgrade pip' command.
main.py:14: error: Incompatible return value type (got "Optional[RpcMethodHandler[grpc.TRequest, grpc.TResponse]]", expected "RpcMethodHandler[grpc.TRequest, grpc.TResponse]")  [return-value]
Found 1 error in 1 file (checked 1 source file)

hi @bkeryan. the issue isn't the library in this case.

the return type you have defined for continuation is typing.Optional[grpc.RpcMethodHandler[grpc.TRequest, grpc.TResponse]], which means returning it directly would violate the return type you've defined for intercept_service()

one way to demonstrate this is to raise an exception when the value is None, this will cause the typing to work out:

class NullServerInterceptor(grpc.ServerInterceptor[grpc.TRequest, grpc.TResponse]):
    def intercept_service(
        self,
        continuation: Callable[
            [grpc.HandlerCallDetails],
            Optional[grpc.RpcMethodHandler[grpc.TRequest, grpc.TResponse]]
        ],
        handler_call_details: grpc.HandlerCallDetails,
    ) -> grpc.RpcMethodHandler[grpc.TRequest, grpc.TResponse]:
        response = continuation(handler_call_details)
        if response is None:
            raise NotImplementedError
        return response

Hi @macro1,

Thanks for the heads up.

According to the linked documentation, intercept_service is allowed to return None. It is not required to raise an exception when the intercepted service returns None.

The return type in the example was copied from grpc-stubs. At the time I filed this issue, grpc-stubs annotated intercept_service with a return type of grpc.RpcMethodHandler[grpc.TRequest, grpc.TResponse]. It has since been fixed by #43, which added typing.Optional to the return type.