Skip to content

grpcio: Interceptor base classes are incompatible with mypy --disallow-any-generics or --strict options #14641

@bkeryan

Description

@bkeryan

The grpcio package provides interceptor base classes (ServerInterceptor, UnaryUnaryClientInterceptor, etc.) that clients can subclass in order to intercept calls made by a gRPC client or server. However, the grpcio package and typeshed disagree about whether they are generic, which makes it difficult to use them with mypy's --disallow-any-generics or --strict options without type: ignore comments.

  • In grpc/grpc the interceptor classes are ABCs but not generics, and they do not implement __class_getitem__, so they cannot be subscripted at run time. The interceptor example subclasses the interceptor base classes without subscripting.
  • In typeshed the interceptor classes are ABCs and generics. If you use --disallow-any-generics or --strict, you will get a [type-arg] error if you subclass an interceptor base class without subscripting.

I also reported this to grpc/grpc as grpc/grpc#40550

Example 1 (no subscripting):

from __future__ import annotations
import grpc
import typing

class MyClientInterceptor(grpc.UnaryUnaryClientInterceptor):
    def intercept_unary_unary(
        self,
        continuation: typing.Callable[[grpc.ClientCallDetails, grpc._TRequest], grpc._CallFuture[grpc._TResponse]],
        client_call_details: grpc.ClientCallDetails,
        request: grpc._TRequest
    ) -> grpc._CallFuture[grpc._TResponse]:
        return continuation(client_call_details, request)
$ python test.py
$ mypy --disallow-any-generics test.py
test.py:5: error: Missing type parameters for generic type "UnaryUnaryClientInterceptor"  [type-arg]
Found 1 error in 1 file (checked 1 source file)
$ mypy --strict test.py
test.py:5: error: Missing type parameters for generic type "UnaryUnaryClientInterceptor"  [type-arg]
Found 1 error in 1 file (checked 1 source file)

Example 2 (subscripting):

from __future__ import annotations
import grpc
import typing

TRequest = typing.TypeVar("TRequest")
TResponse = typing.TypeVar("TResponse")

class MyClientInterceptor(
    typing.Generic[TRequest, TResponse],
    grpc.UnaryUnaryClientInterceptor[TRequest, TResponse]
):
    def intercept_unary_unary(
        self,
        continuation: typing.Callable[[grpc.ClientCallDetails, TRequest], grpc._CallFuture[TResponse]],
        client_call_details: grpc.ClientCallDetails,
        request: TRequest
    ) -> grpc._CallFuture[TResponse]:
        return continuation(client_call_details, request)
$ python test2.py
Traceback (most recent call last):
  File "test2.py", line 10, in <module>
    grpc.UnaryUnaryClientInterceptor[TRequest, TResponse]
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
TypeError: type 'UnaryUnaryClientInterceptor' is not subscriptable
$ mypy --disallow-any-generics test2.py
Success: no issues found in 1 source file
$ mypy --strict test2.py
Success: no issues found in 1 source file

Versions used:

  • Python 3.11.9 on Windows x64
  • Packages
Package           Version
----------------- --------------
grpcio            1.74.0
mypy              1.17.1
mypy_extensions   1.1.0
pathspec          0.12.1
pip               24.0
setuptools        65.5.0
types-grpcio      1.0.0.20250703
typing_extensions 4.15.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions