I want to implement typing for a group of related classes that share a common metaclass (AMeta), there are two ‘de facto’ abstract parent classes:
AMeta
A
ADerived
C
Finally there are actual implementation models of ADerived (D1, D2, … these are dynamically created) and A (E, F, …) parent classes.
D1
D2
E
F
The implementations of A (E, F) have also a class variable of type ADerived (_DerivedModel) and the problem I have is trying to make mypy infer the correct type for them.
_DerivedModel
mypy
I know this piece of code might not make sense, but the actual implementation is more complex and the derived models are dynamically created. Here’s a simplification of it:
from __future__ import annotations from typing import TypeVar, Type, ClassVar _BModel = TypeVar("_BModel", bound="ADerived") class C: pass class AMeta(type): @property def BModel(cls: Type[A]) -> Type[_BModel]: return cls._DerivedModel # Abstract Models class A(metaclass=AMeta): _DerivedModel: ClassVar[Type[_BModel]] class ADerived(A, C): pass # Derived Models (this models are dynamically created) class D1(ADerived): pass class D2(ADerived): pass # Implementations class E(A): _DerivedModel = D1 class F(A): _DerivedModel = D2 MyDerived1 = E.BModel # Should be infered by mypy as type[D1] MyDerived2 = F.BModel # Should be infered by mypy as type[D2]
In this case, you can use the Type function from the typing module to explicitly annotate the type of _DerivedModel in the parent classes E and F. This will help mypy infer the correct types for MyDerived1 and MyDerived2.
Type
typing
MyDerived1
MyDerived2
Here’s the modified code:
from __future__ import annotations from typing import TypeVar, Type, ClassVar _BModel = TypeVar("_BModel", bound="ADerived") class C: pass class AMeta(type): @property def BModel(cls: Type[A]) -> Type[_BModel]: return cls._DerivedModel # Abstract Models class A(metaclass=AMeta): _DerivedModel: ClassVar[Type[_BModel]] class ADerived(A, C): pass # Derived Models (these models are dynamically created) class D1(ADerived): pass class D2(ADerived): pass # Implementations class E(A): _DerivedModel: ClassVar[Type[D1]] = D1 class F(A): _DerivedModel: ClassVar[Type[D2]] = D2 MyDerived1: Type[D1] = E.BModel MyDerived2: Type[D2] = F.BModel
By explicitly annotating _DerivedModel with ClassVar[Type[D1]] and ClassVar[Type[D2]] in classes E and F respectively, you guide mypy in inferring the correct types for MyDerived1 and MyDerived2.
ClassVar[Type[D1]]
ClassVar[Type[D2]]