Cleaning Your Django Project With PyLint And Buildbot

 class=

There are a number of tools for checking whether your Python code meets a coding standard. These include pep8.py, PyChecker and PyLint. Of these, PyLint is the most comprehensive and is the tool which I prefer to use as part of my buildbot checks that run on every commit.

PyLint works by parsing the Python source code itself and checking things like using variables that aren’t defined, missing doc strings and a large array of other checks. A downside of PyLint’s comprehensiveness is that it runs the risk of generating false positives. As it parses the source code itself it struggles with some of Python’s more dynamic features, in particular metaclasses, which, unfortunately, are a key part of Django. In this post I’ll go through the changes I make to the standard PyLint settings to make it more compatible with Django.

disable=W0403,W0232,E1101

This line disables a few problems that are picked up entirely. W0403 stops relative imports from generating a warning, whether you want to disable these or not is really a matter of personal preference. Although I appreciate why there is a check for this, I think this is a bit too picky. W0232 stops a warning appearing when a class has no __init__ method. Django models will produce this warning, but because they’re metaclasses there is nothing wrong with them. Finally, E1101 is generated if you access a member variable that doesn’t exist. Accessing members such as id or objects on a model will trigger this, so it’s simplest just to disable the check.

output-format=parseable include-ids=yes

These makes the output of PyLint easier to parse by Buildbot, if you’re not using it then you probably don’t need to include these lines.

good-names= …,qs

Apart from a limited number of names PyLint tries to enforce a minimum size of three characters in a variable name. As qs is such a useful variable name for a QuerySet I force this be allowed as a good name.

max-line-length=160

The last change I make is to allow much longer lines. By default PyLint only allows 80 character long lines, but how many people have screens that narrow anymore? Even the argument that it allows you to have two files side by side doesn’t hold water in this age where multiple monitors for developers are the norm.

PyLint uses the exit code to indicate what errors occurred during the run. This confuses Buildbot which assumes that a non-zero return code means the program failed to run, even when using the PyLint buildstep. To work around this I use a simple management command to duplicate the pylint program’s functionality but that doesn’t let the return code propagate back to Builtbot.

from django.core.management.base import BaseCommand
from pylint import lint

class Command(BaseCommand):
    def handle(self, *args, **options):
        lint.Run(list(args + ("--rcfile=../pylint.cfg", )), exit=False)
Want to read more like this? Follow me with your favourite feed reader (e.g. Feedly), or subscribe to my SubStack newsletter.

Comments

Thanks for the tips! Put me on the list of people who still prefer 80 columns; my second monitor is in portrait mode and with a file browser and taglist open on either side, I get about 100 columns of text for code display. It's also easier to scan and navigate shorter lines.

Tom Davis

07 Mar 2011

80 columns please!!

People who don't use IDE but use hard core editors like vim, jed, emacs, etc. often have one terminal or terminal screen for the editor and one for the unit test runner or a development server running side by side.

Another very real argument for 80 characters is readability. Code is supposed to be read from top to bottom or bottom to top. Like a parser/compiler sort of. When you're then reading and sporadically have to track your attention away to the side the code becomes harder to read.

A third very real argument for 80 characters is that if you allow your code to grow too much horizontally, perhaps huge control flows need to be refactored into functions or new classes.

Peter Bengtsson

09 Mar 2011

All your arguments make sense, and I do keep my lines as short as possible. What I don't do is keep to a hard limit of 80 columns. Sometimes I think it's clearer to have a function call on one line, even though it might be 85 or 90 characters long. If you're hitting 80 columns because of nested ifs or for loops then your code definitely needs to be refactored.

I think it's important to only get PyLint to alert to the more serious problems with the code, otherwise you'll be overwhelmed with changes that need to be made.

Andrew Wilkinson

09 Mar 2011