mypy declaring incompatible types, despit the fact that all types are explicit and using Union
Question:
from enum import Enum
class MyEnum(Enum):
FIRST = 1, 'first'
SECOND = 2 , 'second'
__myenum_int_ref: Dict[int, MyEnum] = {k.value[0]: k for k in MyEnum}
__myenum_str_ref: Dict[str, MyEnum] = {k.value[1]: k for k in MyEnum}
__myenum_str_ref.update({k.name: k for k in MyEnum})
__flags_ref: Dict[Union[Type[str], Type[int]], Dict[Union[str, int], MyEnum]] = {
str: __myenum_str_ref,
int: __myenum_int_ref,
}
When I run mypy I get this error
function.py:14: error: Dict entry 0 has incompatible type "Type[str]": "Dict[str, MyEnum]"; expected "Union[Type[str], Type[int]]": "Dict[Union[str, int], MyEnum]"
function.py:15: error: Dict entry 1 has incompatible type "Type[int]": "Dict[int, MyEnum]"; expected "Union[Type[str], Type[int]]": "Dict[Union[str, int], MyEnum]"
I have explicitly described that the keys can be of type int and of type str, I inserted in the dictionary a key type int and a key type str.
I know that it is correct because on python 3.10, using Type[int|str]
is the preferred way and mypy does accept it perfectly, and it is the "sugar syntax" added so you don’t have to write Union[Type[int], Type[str]]
.
But I need to use python 3.9, and I am stuck with the use of Union for types, and mypy is complaining that it is incompatible even when I am dealing with explicit types.
Answers:
Because your value type is Dict[str, MyEnum]
or Dict[int, MyEnum]
, you should use Union[Dict[str, MyEnum], Dict[int, MyEnum]]
instead of Dict[Union[str, int], MyEnum]
:
Dict[Union[Type[str], Type[int]], Union[Dict[str, MyEnum], Dict[int, MyEnum]]]
# in Python3.9
dict[Union[type[str], type[int]], Union[dict[str, MyEnum], dict[int, MyEnum]]]
# in Python3.10+
dict[type[str] | type[int], dict[str, MyEnum] | dict[int, MyEnum]]
Update:
Using Protocol
, you can pass the mypy check:
from typing import Protocol, TypeVar
_T = TypeVar('_T')
class FlagsRef(Protocol[_T]):
def __getitem__(self, item: type[_T]) -> dict[_T, MyEnum]: ...
__flags_ref: FlagsRef = { # pass
str: __myenum_str_ref,
int: __myenum_int_ref,
}
int_ref: dict[int, MyEnum] = __flags_ref[int] # pass
from enum import Enum
class MyEnum(Enum):
FIRST = 1, 'first'
SECOND = 2 , 'second'
__myenum_int_ref: Dict[int, MyEnum] = {k.value[0]: k for k in MyEnum}
__myenum_str_ref: Dict[str, MyEnum] = {k.value[1]: k for k in MyEnum}
__myenum_str_ref.update({k.name: k for k in MyEnum})
__flags_ref: Dict[Union[Type[str], Type[int]], Dict[Union[str, int], MyEnum]] = {
str: __myenum_str_ref,
int: __myenum_int_ref,
}
When I run mypy I get this error
function.py:14: error: Dict entry 0 has incompatible type "Type[str]": "Dict[str, MyEnum]"; expected "Union[Type[str], Type[int]]": "Dict[Union[str, int], MyEnum]"
function.py:15: error: Dict entry 1 has incompatible type "Type[int]": "Dict[int, MyEnum]"; expected "Union[Type[str], Type[int]]": "Dict[Union[str, int], MyEnum]"
I have explicitly described that the keys can be of type int and of type str, I inserted in the dictionary a key type int and a key type str.
I know that it is correct because on python 3.10, using Type[int|str]
is the preferred way and mypy does accept it perfectly, and it is the "sugar syntax" added so you don’t have to write Union[Type[int], Type[str]]
.
But I need to use python 3.9, and I am stuck with the use of Union for types, and mypy is complaining that it is incompatible even when I am dealing with explicit types.
Because your value type is Dict[str, MyEnum]
or Dict[int, MyEnum]
, you should use Union[Dict[str, MyEnum], Dict[int, MyEnum]]
instead of Dict[Union[str, int], MyEnum]
:
Dict[Union[Type[str], Type[int]], Union[Dict[str, MyEnum], Dict[int, MyEnum]]]
# in Python3.9
dict[Union[type[str], type[int]], Union[dict[str, MyEnum], dict[int, MyEnum]]]
# in Python3.10+
dict[type[str] | type[int], dict[str, MyEnum] | dict[int, MyEnum]]
Update:
Using Protocol
, you can pass the mypy check:
from typing import Protocol, TypeVar
_T = TypeVar('_T')
class FlagsRef(Protocol[_T]):
def __getitem__(self, item: type[_T]) -> dict[_T, MyEnum]: ...
__flags_ref: FlagsRef = { # pass
str: __myenum_str_ref,
int: __myenum_int_ref,
}
int_ref: dict[int, MyEnum] = __flags_ref[int] # pass