root/releases/pkgcore/0.1.2/lintplugin/pkgcore.py @ marienz%2540gentoo.org-20060906170310-86eee996942d25b4

Revision marienz%2540gentoo.org-20060906170310-86eee996942d25b4, 4.4 KB (checked in by Marien Zwart <marienz@…>, 2 years ago)

Add a pylint plugin providing a better "line too long" check, a trailing whitespace check, and demandload support.

Line 
1
2"""Pylint plugin checking for trailing whitespace."""
3
4
5from pylint import interfaces, checkers
6from logilab.astng import nodes, raw_building, utils
7from logilab.common import interface
8
9
10class BasicLinesChecker(checkers.BaseChecker):
11
12    __implements__ = (interfaces.IRawChecker, interfaces.IASTNGChecker)
13
14    name = 'pkgcore-lines'
15
16    # XXX move some of those over to RewriteDemandload somehow
17    # (current monkey patch running the rewriter does not support that)
18
19    msgs = {
20        'CPC01': ('line too long',
21                  'More complete version of the standard line too long check'),
22        'CPC02': ('trailing whitespace', 'trailing whitespace sucks.'),
23        'WPC01': ('demandload with arglen != 2 ignored',
24                  'A call which is probably a demandload has the wrong number '
25                  'of arguments. Either fix the checker to not detect it as '
26                  'demandload when it is really not or fix the code to call '
27                  'demandload correctly.'),
28        'WPC02': ('demandload with non-string-constant arg ignored.',
29                  'A call which is probably a demandload has a second arg '
30                  'that is not a string constant. Fix the code to cooperate '
31                  'with the dumb checker.'),
32        }
33
34    def process_module(self, stream):
35        for linenr, line in enumerate(stream):
36            line = line.rstrip('\r\n')
37            if len(line) > 80:
38                self.add_message('CPC01', linenr)
39            if line.endswith(' ') or line.endswith('\t'):
40                self.add_message('CPC02', linenr)
41
42
43class RewriteDemandload(utils.ASTWalker):
44
45    def __init__(self, linter):
46        utils.ASTWalker.__init__(self, self)
47        self.linter = linter
48
49    def visit_callfunc(self, node):
50        """Hack fake imports into the tree after demandload calls."""
51        # XXX inaccurate hack
52        if not node.node.as_string().endswith('demandload'):
53            return
54        # sanity check.
55        if len(node.args) != 2:
56            self.linter.add_message('WPC01', node=node)
57            return
58        if not isinstance(node.args[1], nodes.Const):
59            self.linter.add_message('WPC02', node=node)
60            return
61        modules = node.args[1].value
62        if not isinstance(modules, str):
63            self.linter.add_message('WPC02', node=node)
64            return
65        for mod in modules.split():
66            col = mod.find(':')
67            if col == -1:
68                # Argument to Import probably works like this:
69                # "import foo, foon as spork" is
70                # nodes.Import([('foo', None), ('foon', 'spork')])
71                # (not entirely sure though, have not found documentation.
72                # The asname/importedname might be the other way around fex).
73                newstuff = nodes.Import([(mod, None)])
74                raw_building._attach_local_node(node.frame(), newstuff, mod)
75            else:
76                for name in mod[col+1:].split(','):
77                    raw_building.attach_import_node(node.frame(), mod[:col],
78                                                    name)
79
80
81def register(linter):
82    """Required method to get our checker registered."""
83
84    rewriter = RewriteDemandload(linter)
85    # XXX HACK: monkeypatch the linter to transform the tree before
86    # the astng checkers get at it.
87    #
88    # Why do we do this? Because a whole bunch of places work with
89    # copies of astng data, not the data itself, by the time a normal
90    # checker runs it is too late to manipulate the data reliably. And
91    # pylint does not provide a hook that gets run at the right point
92    # to do this tree rewriting. So we monkeypatch in the hook.
93    #
94    # Ideally we would do something like
95    #
96    # linter.register_preprocessor(rewriter)
97    #
98    # and the linter would call walk(astng) on everything registered
99    # that way before the IASTNGCheckers run (not sure if it should be
100    # before or after the raw checkers run, probably does not matter).
101    # Perhaps give those preprocessors a priority attribute too.
102    # Definitely give them a msgs attribute.
103
104    original_check_astng_module = linter.check_astng_module
105    def pkgcore_check_astng_module(astng, checkers):
106        rewriter.walk(astng)
107        original_check_astng_module(astng, checkers)
108    linter.check_astng_module = pkgcore_check_astng_module
109
110    linter.register_checker(BasicLinesChecker(linter))
Note: See TracBrowser for help on using the browser.