When type checking the following code block with mypy
an error is raised.
from typing import Generic, TypeVar
class Employee:
pass
class Manager(Employee):
pass
T_co = TypeVar('T_co', Employee, Manager, covariant=True)
T_contra = TypeVar('T_contra', Employee, Manager, contravariant=True)
class Base(Generic[T_contra]):
pass
class Derived(Base[T_co]):
pass
def consume_base_manager(_: Base[Manager]) -> None: ...
def consume_base_employee(_: Base[Employee]) -> None: ...
def consume_derived_manager(_: Derived[Manager]) -> None: ...
def consume_derived_employee(_: Derived[Employee]) -> None: ...
base_manager: Base[Manager]
base_employee: Base[Employee]
derived_manager: Derived[Manager]
derived_employee: Derived[Employee]
consume_base_manager(base_employee)
consume_base_manager(base_manager)
consume_base_manager(derived_manager)
consume_base_manager(derived_employee)
consume_base_employee(base_employee)
consume_base_employee(derived_manager)
consume_base_employee(derived_employee)
consume_derived_manager(derived_manager)
consume_derived_employee(derived_manager)
consume_derived_employee(derived_employee)
Output:
error: Argument 1 to "consume_base_employee" has incompatible type "Derived[Manager]"; expected "Base[Employee]"
Out of the four functions and four argument types, I called every function with what I thought to be every valid argument but mypy
gave me an error on consume_base_employee(derived_manager)
. This is weird to me because according to PEP 483 this should be valid.
In complex definitions of derived generics, variance only determined from type variables used. A complex example:
T_co = TypeVar('T_co', Employee, Manager, covariant=True) T_contra = TypeVar('T_contra', Employee, Manager, contravariant=True) class Base(Generic[T_contra]): ... class Derived(Base[T_co]): ...
A type checker finds from the second declaration that
Derived[Manager]
is a subtype ofDerived[Employee]
, andDerived[t1]
is a subtype ofBase[t1]
. If we denote the is-subtype-of relationship with<
, then the full diagram of subtyping for this case will be:Base[Manager] > Base[Employee] v v Derived[Manager] < Derived[Employee]
so that a type checker will also find that, e.g.,
Derived[Manager]
is a subtype ofBase[Employee]
.
What is even weirder to me is that the line that raises an error is the one situation that is explicitly stated in the PEP that should work.
Did I misunderstand the intended meaning of the PEP, is there a mistake in the PEP or is this a bug in mypy
?