The typing spec currently states:
[TypeGuard] is not a subtype of bool. Therefore,
Callable[..., TypeGuard[int]]
is not assignable toCallable[..., bool]
.
That means that the following program should be rejected by type checkers:
from itertools import count
from typing import TypeGuard, Callable, Iterable
def filtered_count(pred: Callable[[int], bool]) -> Iterable[int]:
for i in count():
if pred(i):
yield i
def is_even_int(x: object) -> TypeGuard[int]:
return isinstance(x, int) and x % 2 == 0
for i in filtered_count(is_even_int):
print(i)
This is passing a function returning a TypeGuard to a parameter of type Callable[[int], bool]
, and the spec explicitly says that is not allowed.
However, both mypy and pyright accept this program without errors. It works fine at runtime, and I donāt see any soundness problems that result from accepting it.
The rule in the spec derives from PEP 647 (which created TypeGuard) and was added in this PR. @erictraut told me that the change derived from feedback by Guido, but I havenāt been able to find a more precise motivation.
I propose to change the spec so that Callable[..., TypeGuard[...]]
is a subtype of Callable[..., bool]
. This brings the spec in line with the actual behavior of major type checkers.
The same reasoning applies to the new TypeIs
special form I propose in PEP 742. For the moment I copied the TypeGuard restriction in the new PEP, but if the proposal in this post is accepted, Iāll change PEP 742 accordingly.