shabbyrobe / grpc-stubs

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

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

Closed bkeryan closed 5 months ago

bkeryan commented 1 year ago

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 ```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 ```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) ```
macro1 commented 5 months ago

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
bkeryan commented 5 months ago

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.