Using the most restrictive full-stub syntax, here's how we'd express a few Callable types:
# from functools.pyi
f0: Callable[[_T1, _T2], S)
f0: (t1: _T1, t2: _T2, /) -> S
# from shapeflow/api.py
f1: Callable[[Optional[int], np.ndarray], stream_image]
f1: (i: Optional[int], a: np.ndarray, /) -> stream_image
# from pyexpat/__init__.pyi
f2: Callable[[str, Optional[str], int], Any]
f2: (s0: str, s1: Optional[str], i: int, /) -> Any
Here are the same examples if we allow underscores for irrelevant names:
f0: (_: _T1, _: _T2, /) -> S
f1: (_: Optional[int], _: np.ndarray, /) -> stream_image
f2: (_: str, _: Optional[str], _: int, /) -> Any
We also discussed making an underscore name always imply a positional-only argument, in which case we could omit the / and write:
f0: (_: _T1, _: _T2) -> S
f1: (_: Optional[int], _: np.ndarray) -> stream_image
f2: (_: str, _: Optional[str], _: int) -> Any
Here are the same examples if we allow a compact syntax for positional-only callable types:
f: (_T1, _T2) -> S
f1: (Optional[int], np.ndarray) -> stream_image
f2: (str, Optional[str], int) -> Any
Here's a callable type that can't be written using the existing Callable
, expressed using each convention
# most restrictive stub syntax
(a: A, b: B = ..., /, c: C = ...) -> D
# stub syntax with _
(_: A, _: B = ..., /, c: C = ...) -> D
# if we let _ imply positional-only
(_: A, _: B = ..., c: C = ...) -> D
# if we allow no argument name for positional-only
(A, B = ..., c: C = ...) -> D
Here are several different function stubs that should typecheck when used as the type above:
def f0(a: A, b: B = ..., /, c: C = ...) -> D:
...
def f1(apple: A, bannana: SupertypeOfB = ..., c: C = ...) -> D:
...
def f2(apple: A, bannana: B = ..., *args: Any, **kwargs: Any) -> SubtypeOfD:
...