I have this code
from abc import ABC
from typing import Type
class AbstractFoo(ABC):
"""Abstract foo."""
foo_attr: int
def foo_factory(foo_attr_val: int) -> Type[AbstractFoo]:
class Foo(AbstractFoo):
foo_attr = foo_attr_val
return Foo
FooA = foo_factory(5)
class FooB(FooA):
"""Public concrete foo."""
print(FooB.foo_attr)
This executes just fine, but then I run mypy with mypy foo.py
, I get an error that looks like
foo.py:21: error: Variable "foo.FooA" is not valid as a type
foo.py:21: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
foo.py:21: error: Invalid base class "FooA"
Found 2 errors in 1 file (checked 1 source file)
I don't understand what is wrong here and why this type is invalid. What's the way to fix this?
I'm currently on Python 3.9 with mypy version 0.971.
Your problem is that mypy
does not support dynamic base classes (doesn't matter if they are well-defined). See this mypy
issue for more workarounds and details.
If you're happy enough to have a factory producing class without major modifications, you can just pretend that return type is that class (try me online!):
from abc import ABC
from typing import Type
class AbstractFoo(ABC):
"""Abstract foo."""
foo_attr: int
def foo_factory(foo_attr_val: int) -> Type[AbstractFoo]:
class Foo(AbstractFoo):
foo_attr = foo_attr_val
return Foo
if TYPE_CHECKING:
FooA = AbstractFoo
else:
FooA = foo_factory(5)
class FooB(FooA):
"""Public concrete foo."""
print(FooB.foo_attr)
TYPE_CHECKING
is a special constant that is False
at runtime and True
for type checkers. Here you make type checker think that your FooA
is just a type alias (and on py3.10+ or with typing_extensions
you can even make it explicit with TypeAlias
type annotation).