From abstract super-class enumerations Action
and Activity
there are the inherited enumerations:
ActivityA
upon whichActionA
can be performed; andActivityB
upon whichActionB
can be performed.
How can a type declaration be added to the action
argument of the perform
method on the abstract Activity
method such that MyPy will respect the inheritance or which action applies to which activity?
from abc import ABC, ABCMeta, abstractmethod
from enum import EnumMeta, IntEnum
class ABCEnumMeta(EnumMeta, ABCMeta):
...
class Action(ABC, IntEnum, metaclass=ABCEnumMeta):
...
class ActionA(Action):
start = 1
stop = 2
class ActionB(Action):
start = 1
pause = 2
resume = 3
complete = 4
fail = 5
class Activity(ABC, IntEnum, metaclass=ABCEnumMeta):
@abstractmethod
def perform(
self: "Activity",
action, # <- This line
) -> str:
...
class ActivityA(Activity):
this = 1
that = 2
def perform(
self: "ActivityA",
action: ActionA,
) -> str:
return f"A: {action.name} {self.name}"
class ActivityB(Activity):
something = 1
another = 2
def perform(
self: "ActivityB",
action: ActionB,
) -> str:
return f"B: {action.name} {self.name}"
print( ActivityB.something.perform(ActionB.pause) )
print( ActivityA.this.perform(ActionA.stop) )
print( ActivityB.another.perform(ActionA.start) )
print( ActivityA.that.perform(ActionB.fail) )
The mypy.ini
settings file being used is:
[mypy]
disallow_any_expr = True
disallow_any_decorated = True
disallow_any_explicit = True
disallow_any_generics = True
disallow_subclassing_any = True
disallow_untyped_calls = True
disallow_untyped_defs = True
disallow_incomplete_defs = True
The output from MyPy is:
test_enum.py:26: error: Function is missing a type annotation for one or more arguments test_enum.py:26: error: Type of decorated function contains type "Any" ("Callable[[Activity, Any], str]") test_enum.py:56: error: Argument 1 to "perform" of "ActivityB" has incompatible type "ActionA"; expected "ActionB" test_enum.py:57: error: Argument 1 to "perform" of "ActivityA" has incompatible type "ActionB"; expected "ActionA"
(The last two errors are expected.)
In this situation, I would normally use generics and define:
A = TypeVar("A", bound=Action)
class Activity(ABC, Generic[A], IntEnum, metaclass=ABCEnumMeta):
@abstractmethod
def perform(
self: "Activity",
action A,
) -> str:
...
class ActivityA(Activity[ActionA]):
...
class ActivityB(Activity[ActionB]):
...
However, the Enum
class raises exceptions when you try to add in a generic.
How could this be solved to properly define the argument types of the abstract method?