mypy 1.14.1 does not appear to treat cast(T, a_thing)
as narrowing a_thing
to whatever T when T is a type variable. Is that expected behavior?
Update: In the course of composing this message, I have realized that cast
wasn’t working as I expected even with cast(bool, other_thing)
. But you will have to endure my thought process below anyway.
What follows is not a carefully constructed example, but I hope it does enough to communicate what I am after.
from typing import Any, Generic, Optional, TypeVar, cast, Protocol
...
K = TypeVar("K")
type KeyGenerator[K] = Callable[[], K]
type Cryptor[K] = Callable[[K, bytes], bytes]
class Ind(Generic[K]):
...
def __init__(
self,
key_gen: KeyGenerator[K],
encryptor: Cryptor[K],
decryptor: Optional[Cryptor[K]] = None,
transition_table: Optional[TransitionTable] = None,
) -> None:
...
self._key: Optional[K] = None
self._b: Optional[bool] = None
....
def encrypt_one(self, m0: bytes, m1: bytes) -> bytes:
...
# We know that self._b and self._key aren't None at this point
cast(bool, self._b)
cast(K, self._key). # Fails to narrow self._key
....
m = m1 if self._b else m0 # mypy is happy here
...
ctext = self._encryptor(self._key, m) # mypy is not happy here
Mypy’s unhappiness:
error: Argument 1 has incompatible type "K | None"; expected "K" [arg-type]
Ok. I just realized when composing this that it is possilbe that the cast for the bool isn’t “working” either, but that bool | None
would be fine where self._b
is used. … I have just checked and that is the case.
If I add a line with foo: bool = self._b
, I get a similar complaint from mypy. So this isn’t about type variables, I appear to be using cast wrong.
Until I understand what I don’t understand about cast
, I will just use assertions for type narrowing in these cases, but my intuition is that there should be a way to narrow without anything having to happen at run time.