I'm trying to implement a generic Protocol. My intent is to have a Widget[key_type, value_type] protocol with a simple getter. Mypy complained about Protocol[K, T]
so that became Protocol[K_co, T_co]
. I've already stripped out all the other constraints, but I can't even get the most basic situation, widg0: Widget[Any, Any] = ActualWidget()
, to work. ActualWidget.get should be totally compatible with get(self, key: K) -> Any
, which makes me think I'm using the generics/protocol wrong in some way, or mypy just can't handle this.
command/error from mypy:
$ mypy cat_example.py
cat_example.py:34: error: Argument 1 to "takes_widget" has incompatible type "ActualWidget"; expected "Widget[Any, Any]"
cat_example.py:34: note: Following member(s) of "ActualWidget" have conflicts:
cat_example.py:34: note: Expected:
cat_example.py:34: note: def [K] get(self, key: K) -> Any
cat_example.py:34: note: Got:
cat_example.py:34: note: def get(self, key: str) -> Cat
Found 1 error in 1 file (checked 1 source file)
or alternatively, if I try to force the assignment with widg0: Widget[Any, Any] = ActualWidget()
:
error: Incompatible types in assignment (expression has type "ActualWidget", variable has type "Widget[Any, Any]")
The full code:
from typing import Any, TypeVar
from typing_extensions import Protocol, runtime_checkable
K = TypeVar("K") # ID/Key Type
T = TypeVar("T") # General type
K_co = TypeVar("K_co", covariant=True) # ID/Key Type or subclass
T_co = TypeVar("T_co", covariant=True) # General type or subclass
K_contra = TypeVar("K_contra", contravariant=True) # ID/Key Type or supertype
T_contra = TypeVar("T_contra", contravariant=True) # General type or supertype
class Animal(object): ...
class Cat(Animal): ...
@runtime_checkable
class Widget(Protocol[K_co, T_co]):
def get(self, key: K) -> T_co: ...
class ActualWidget(object):
def get(self, key: str) -> Cat:
return Cat()
def takes_widget(widg: Widget):
return widg
if __name__ == '__main__':
widg0 = ActualWidget()
#widg0: Widget[str, Cat] = ActualWidget()
#widg0: Widget[Any, Any] = ActualWidget()
print(isinstance(widg0, Widget))
print(isinstance({}, Widget))
takes_widget(widg0)