I have some classes that more or less can be reduced to this example
from typing import Protocol, Callable, Any, runtime_checkable
@runtime_checkable
class Loader(Protocol):
def load(self,*a,**k) -> Any:...
class Foo:
def __init__(self, path:str, loader:Loader|None) -> None:
self.path = path
self.loader = loader
@property
def loader(self) -> Loader | None:
return self._loader
@loader.setter
def loader(self, value:Loader|None) -> None:
if value and not isinstance(value,Loader):
raise ValueError
self._loader = value
class Baz(Foo):
def prepare(self) -> Callable[...,Any] | None:
if not hasattr(self.loader,"load"):
return None
...#prepare
return lambda x:x #for example purposes
def do_stuff(self) -> Any:
if (pre:=self.prepare()):
a = pre()
#assert self.loader is not None
return self.loader.load(a)
...#do stuff without the loader
when tested with mypy give me
test9.py:33: error: Item "None" of "Optional[Loader]" has no attribute "load"
Found 1 error in 1 file (checked 1 source file)
is there a way to tell mypy that if the prepare method return something also means that self.loader is non-None?
(other than uncommenting that assert or type ignore or something else that result in some useless runtime piece of code that does nothing such as cast)