grpc-stubs icon indicating copy to clipboard operation
grpc-stubs copied to clipboard

`CallIterator` is missing `__next__` method

Open bkeryan opened this issue 1 year ago • 0 comments

Description of issue

The type hints for grpc.CallIterator define __iter__ but not __next__. Based on Iterator Types I think this makes grpc.CallIterator an iterable, not an iterator.

The result is that passing a grpc.CallIterator to the next() function works at runtime but causes mypy to emit this error:

main.py:16: error: No overload variant of "next" matches argument type "CallIterator[T]"  [call-overload]
main.py:16: note: Possible overload variants:
main.py:16: note:     def [_T] next(SupportsNext[_T], /) -> _T
main.py:16: note:     def [_T, _VT] next(SupportsNext[_T], _VT, /) -> Union[_T, _VT]

The docs for grpc.UnaryStreamMultiCallable say the returned object is an iterator for response values, which suggests it implements the Iterator protocol, not only the Iterable protocol.

The implementation in the grpc._channel._Rendezvous class supports __next__.

Minimum Reproducible Example

main.py
from __future__ import annotations
import grpc
import typing

T = typing.TypeVar("T")

if typing.TYPE_CHECKING:
    # grpc.CallIterator is only defined when type checking                      
    class CallIteratorPlusNext(grpc.CallIterator[T]):
        def __next__(self) -> T:
            raise NotImplementedError()

# This generates mypy errors because grpc.CallIterator does not have a          
# __next__() method.                                                            
def call_next(call_iterator: grpc.CallIterator[T]) -> T:
    return next(call_iterator)

# This does not generate mypy errors because the derived class adds a           
# __next__() method.                                                            
def call_next_plus(call_iterator: CallIteratorPlusNext[T]) -> T:
    return next(call_iterator)
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: tomli>=1.1.0 in ./venv/lib/python3.9/site-packages (from mypy) (2.0.1)
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>=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/grpc_itr/venv/bin/python -m pip install --upgrade pip' command.
main.py:16: error: No overload variant of "next" matches argument type "CallIterator[T]"  [call-overload]
main.py:16: note: Possible overload variants:
main.py:16: note:     def [_T] next(SupportsNext[_T], /) -> _T
main.py:16: note:     def [_T, _VT] next(SupportsNext[_T], _VT, /) -> Union[_T, _VT]
Found 1 error in 1 file (checked 1 source file)

bkeryan avatar Aug 10 '23 18:08 bkeryan