Search code examples
pythonpython-2.7pylintabc

Pylint cannot handle abstract subclasses of abstract base classes


I have a concrete class MyConcreteClass which subclasses an abstract class MyABC2, which, in turn, subclasses another abstract class MyABC1:

import abc

class MyABC1 (object):
    __metaclass__ = abc.ABCMeta
    @abc.abstractmethod
    def my_abstract_method (self):
        raise NotImplementedError("MyABC1.my_abstract_method")

class MyABC2 (MyABC1):
    __metaclass__ = abc.ABCMeta

class MyConcreteClass (MyABC2):
    def my_abstract_method (self):
        print "MyConcreteClass.my_abstract_method"

pylint does not like that I do not define my_abstract_method in MyABC2:

/usr/local/bin/epylint abctest.py
************* Module abctest
 abctest.py:9: warning (W0223, abstract-method, MyABC2) Method 'my_abstract_method' is abstract in class 'MyABC1' but is not overridden

Is this a known bug? Maybe I am doing something wrong?


Solution

  • Pylint's notion of when a class is intended to be abstract is a bit limited. Basically, it can't tell that you intended the MyABC2 class to be an abstract class. Its logic assumes that if you don't declare any methods in the class that are decorated with @abstractmethod (or raise NotImplementedError), you intended the class to be concrete. In such a situation, failing to override the inherited abstract methods would be an error.

    Declaring the metaclass in MyABC2 doesn't help. In fact, that declaration is completely redundant, since a subclass inherits its metaclass from its parent class.

    So that's the reason for the incorrect warning. Now the question is what to do about it. Ideally, pylint would be a bit more sophisticated and handle this situation better, but fixing the issue would be difficult. I don't really know how it can tell the difference between a mid-level abstract class that doesn't add any new abstract methods and a incorrectly implemented concrete class.

    So probably the only option is to disable the warning for that class. Try adding the following comment to your code just above the class declaration: # pylint: disable=W0223. (It might also work if you put the comment inside the class, I'm not sure.)