Search code examples
pythonmetaprogrammingfactory-patternpylint

Python Astroid (Pylint) How to mimic inheritance when class passes through factory?


Where I work, we extensively use SQLAlchemy. Overtime we developped a base class for our models that fits our needs. But when time comes to lint our code, we are always overwhelmed by warning we know we could ignore. But so far we only managed to do this globally with the generated-members directive which tends to hide issues. So I started to wonder: “How could I teach that to pylint ?”

Here is the situation:

from sqlalchemy.ext.declarative import declarative_base

class CustomBaseModel(object):
    def feature(self):
        pass

Model = declarative_base(cls=CustomBaseModel)

class Thing(Model):
    id = Column(Integer, primary_key=True)
    label = Column(String(64))

t = Thing()
t.feature()  # Pylint says Thing has not `feature()` method.

So what I'd like to do is tell pylint that Model is in fact, more or less CustomBaseModel.

Hence it looks more like I should use an inference_tip on the return value of the call to declarative_base(). But I am not exactly sure on how to proceed. And it looks like the API changed over time and I am not going anywhere.

Another strategy I looked into is copy attributes found on the CustomBaseModel to Model. But it does not work. Indeed to Pylint Model seems to just be a name... it lost track of what it is and has no clue that it is a class.

Any hints would be much appreciated...


Solution

  • If you replace this:

    Model = declarative_base(cls=CustomBaseModel)
    

    with something like this:

    def base_decorator(cls):
        return declarative_base(cls = cls)
    
    @base_decorator
    class Model(CustomBaseModel):
        pass
    

    This will result in something similar to the following sequence of execution:

    class Model(CustomBaseModel):
        pass
    Model = declarative_base(cls = Model)
    

    This is functionally the same as the direct call you have in your sample code, but it gives pylint a clue that Model is derived from CustomBaseModel.