Hey all, I recently ran into some trouble and that I think deserves some
attention. Consider the following case:
class A(ABC):
@abstractmethod
def __lt__(self, other):
pass
@dataclass(order=True)
class B(A):
x: int = 0
class C(B):
pass
Although B technically implements A's abstract methods, it is still
considered an abstract class, and calling B() will fail. However, C is
considered a non-abstract class and calling C() will succeed.…
[View More] This is
because a class's "abstraction" is decided when a class is created. When B
is created it does not implement __lt__, the implementation is added after
creation by the dataclass decorator. Since C inherits from B after having
its __lt__ set, C is considered non-abstract. This will also affect any
"mixin-decorator" such as total_ordering, and also (a less-preferred use
case) implementing abstract methods after class creation.
There are many solutions for this, my proposed solution is for abc.ABCMeta
to implement a __setattr__, that erases unimplemented metaclasses f they
are implemented after class creation, Something along these lines:
class ABCMeta2(ABCMeta):
def __setattr__(self, key, value):
super().__setattr__(key, value)
if key != '__abstractmethods__' and value is not None:
self.__abstractmethods__ -= {key}
Obviously some additional logic is needed to handle sub-classes and such,
but I think it's a good starting point.
Any thoughts?
[View Less]