0

Consider the following set of classes:

from abc import ABC, abstractmethod

class GrandParent(ABC):
    @abstractmethod
    def foo(self):
        raise NotImplementedError()

    @abstractmethod
    def bar(self):
        raise NotImplementedError()


class Parent1(GrandParent):

    def foo(self):
        print("Parent 1")

class Parent2(GrandParent):

    def foo(self):
        print("Parent 2")

class Child1A(Parent1):
    def bar(self):
        print("Child 1A")

class Child1B(Parent1):
    def bar(self):
        print("Child 1B")

Here GrandParent, Parent1 and Parent2 are abstract classes, and the only concrete ones are Child1A and Child1B. However, pylint complains about Parents that:

W0223: Method 'bar' is abstract in class 'GrandParent' but is not overridden (abstract-method)

I can understand the reason is that by looking at the class definition itself one cannot understand that it is supposed to be an abstract class. I wonder if there is a decorator or something that I need to use to indicate this explicitly?

One obvious way would be to re-define an abstract bar method in Parent1 and Parent2 but that does not sounds reasonable to me (what if there were 10 other abstract methods in GrandParent that the children need to override?, should all 10 of them be copy-pasted in Parents?)


Update

It was suggested in the comments that this is a pylint-specific behavior that intermediate implicit ABCs are not supported yet. To get rid of the warning (without disabling it) it is sufficient to redefine one of the abstract methods of GrandParent in Parent1 using @abstractmethod decorator. Indeed this solves the issue, but will cause problems in multiple-inheritance scenarios:

from abc import ABC, abstractmethod

class Base(ABC):
    @abstractmethod
    def foo(self):
        raise NotImplementedError()

    @abstractmethod
    def bar(self):
        raise NotImplementedError()

    def baz(self):
        return 'baz'

    def qux(self):
        return 'qux'

class C1(Base):

    def qux(self):
        return super().qux().upper()

    # @abstractmethod
    # def bar(self):
    #    raise NotImplementedError()


class C2(Base):
    def foo(self):
        return 'FOO'

    def bar(self):
        return 'BAR'


class D(C1, C2):
    pass

Here C1 is an intermediate implicit ABC, hence pylint warns. Yet, D is a concrete class. To get rid of the pylint warning we can uncomment the redefinition of bar in C1. But then D cannot be instantiated --- Python complains that bar is abstract in C1 and not defined in D.

MikeL
  • 2,369
  • 2
  • 24
  • 38
  • 1
    This is `pylint` specific behavior and it is currently not supported to define intermediary "implicit" ABCs; see [this issue](https://github.com/PyCQA/pylint/issues/3098). However note that the declaration of a single `abstractmethod` in the intermediary class is sufficient to indicate to `pylint` that this is an ABC which doesn't require overriding of abstract methods. Hence if you have dozens of abstract methods in `GrandParent` it's sufficient to redefine only one in `Parent1`. – a_guest Mar 31 '20 at 13:26
  • So, I guess simply disabling/ignoring the warning must be enough. There is no issue with my SW architecture or implementation, right? – MikeL Mar 31 '20 at 13:40
  • Well, the question is for what purpose you are using those ABCs. If the purpose is to assist development in some IDEA in order to show still missing methods, I suppose that solution defeats the purpose. If you only want runtime failure upon instantiation, then no problem. Redefining one of the methods seems like a reasonable solution since it also makes clear that you are aware it is still an ABC that you are defining. If you are using type annotations and static type checkers like mypy there should be no need to define ABCs for the sake of the API. – a_guest Mar 31 '20 at 13:44
  • @a_guest I agree with you about redefining one abstract method for clarity. Though I just encountered an example that such a redefinition breaks the code! (See the updated post.) – MikeL Apr 01 '20 at 12:22
  • On the other hand, intermediate ABCs with multiple inheritance seems like too weird to be even practical. Did you encounter this in some real code or is it just a made-up example? – a_guest Apr 01 '20 at 18:35
  • I actually ended up such a class hierarchy in my real code. Are you suggesting that I should re-think my SW architecture? – MikeL Apr 02 '20 at 08:00
  • Well, I'm not familiar with your code, but in the spirit of the Zen of Python: "simple is better than complex". – a_guest Apr 02 '20 at 19:14

0 Answers0