Cleaning Your Django Project With PyLint And Buildbot
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)
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