Search code examples
pythondjangopylint

How to make pylint recognize variable via astroid


I'm trying to "teach" pylint to recognize member function, my scenario can be simplified to the following:

Main module: a.py

from plugins import plu

class FooClass(object):
    def bar(self):
        self.plu.foo("asdasd")

if __name__ == '__main__':
    a = FooClass()
    for plugin_name, plugin in plu.plugins.items():
       setattr(a, plugin_name, plugin())
 a.bar()

Plugins folder file plu.py:

plugins = {}
class Plugin(object):
   def foo(self, arg):
       print("bar %s" % arg)

plugins['plu'] = Plugin

The idea that plugins register themselves and some other method "introduces" them on the FooClass object.

Output of the a.py is:

bar asdasd

(as expected)

Running pylint -E a.py yields the following error:

pylint  -E a.py 
************* Module a
E:  5, 8: Instance of 'FooClass' has no 'plu' member (no-member)

I had tried to write the following pylint-plugin:

import astroid
from astroid import nodes


def transform_test_class(mod):
   if mod.name != 'a':
       return
   foo_cls = mod.lookup('FooClass')[1]
   plugin_cls_def =   astroid.MANAGER.ast_from_module_name('plugins.plu')
   foo_cls[0].locals['plu'] = plugin_cls_def.lookup('Plugin')[1]


def register(linter):
   astroid.MANAGER.register_transform(nodes.Module,    transform_test_class)

Output of running pylint --load-plugins=$(pwd)/pylint-plugin -E a.py is:

************* Module a
E:  5, 8: No value for argument 'arg' in unbound method call (no-value-for-parameter)

From the error I suspect that somehow I introduce the property incorrectly, as pylint thinks that method foo is unbounded, but it is indeed bounded. I need to find a way to tell pylint that foo_cls[0].locals['plu'] is actually an instance of class Plugin and not the class itself. Any idea how this can be achieved?


Solution

  • You need to instantiate the plugin class when adding it to the locals of the FooClass. So replace this line foo_cls[0].locals['plu'] = plugin_cls_def.lookup('Plugin')[1] with foo_cls[0].locals['plu'] = [cls.instantiate_class() for cls in plugin_cls_def.lookup('Plugin')[1]]