Callback protocols

Protocols can be used to define flexible callback types that are hard(or even impossible) to express using the Callable[…] syntax, such as variadic,overloaded, and complex generic callbacks. They are defined with a special callmember:

  1. from typing import Optional, Iterable, List
  2. from typing_extensions import Protocol
  3.  
  4. class Combiner(Protocol):
  5. def __call__(self, *vals: bytes, maxlen: Optional[int] = None) -> List[bytes]: ...
  6.  
  7. def batch_proc(data: Iterable[bytes], cb_results: Combiner) -> bytes:
  8. for item in data:
  9. ...
  10.  
  11. def good_cb(*vals: bytes, maxlen: Optional[int] = None) -> List[bytes]:
  12. ...
  13. def bad_cb(*vals: bytes, maxitems: Optional[int]) -> List[bytes]:
  14. ...
  15.  
  16. batch_proc([], good_cb) # OK
  17. batch_proc([], bad_cb) # Error! Argument 2 has incompatible type because of
  18. # different name and kind in the callback

Callback protocols and Callable types can be used interchangeably.Keyword argument names in call methods must be identical, unlessa double underscore prefix is used. For example:

  1. from typing import Callable, TypeVar
  2. from typing_extensions import Protocol
  3.  
  4. T = TypeVar('T')
  5.  
  6. class Copy(Protocol):
  7. def __call__(self, __origin: T) -> T: ...
  8.  
  9. copy_a: Callable[[T], T]
  10. copy_b: Copy
  11.  
  12. copy_a = copy_b # OK
  13. copy_b = copy_a # Also OK