Monitoring Code Quality in Your Django Project

Peter Baumgartner - DjangoCon 2010

Background

  • Working with Django since 2006
  • Founder of Lincoln Loop, a development firm specializing in Django
  • Clients include National Geographic, PBS, Redbeacon, Nasuni
  • We generate lots of code

Overview

  • Why should I worry about code quality?
  • What tools can I use to measure code quality?
  • How do I automate these tools?
  • Putting it all together

Why Should You Care?

Quality code is...

  • maintainable
  • stable
  • debt-free
  • stress-free

How Do You Measure Code Quality?

Our Big Three

  • Test suite passing
  • Test suite coverage (coverage.py, figleaf)
  • Lint/PEP-8 compliance (Pylint, pyflakes, pychecker)

What Else

  • Profiling (hotshot, cProfile)
  • Cross-browser: (Selenium, Windmill)
  • Code complexity (PyMetrics, pygenie)
  • Value (sloccount)

Kwalitee

It looks like quality, it sounds like quality, but it's not quite quality.

http://cpants.perl.org/kwalitee.html

Is That Really "Quality?"

  • Nothing is a substitute for human code review
  • It is really "kwalitee"
  • See also, "Code Smell"

Testing

You are testing your code, right?

Running Your Test Suite

manage.py test is baked in

...but Django's test suite has fails under certain conditions as do many third-party apps. We just want to run tests on our code.

  • manage.py test myapp1 myapp2 myapp3 doesn't scale
  • Use a custom test runner like django-nose: manage.py test myproject

Coverage

How much of your code are you testing?

What's Your Test Suite's Coverage?

  • Shows you which lines were executed during your tests

A simple example with coverage.py

1 $ coverage -x manage.py test
2 $ coverage report --show-missing

Sample output:

Name                               Stmts   Miss  Cover   Missing
----------------------------------------------------------------
django/views/__init__                  0      0   100%   
django/views/decorators/__init__       0      0   100%   
django/views/decorators/cache         32     19    40%   16-17, 39-50, 54-60
django/views/decorators/csrf          22     12    45%   6-7, 22-26, 35-38, 48
django/views/defaults                 11      5    54%   13-14, 24, 34-35
-----------------------------------------------------------------
TOTAL                                 65     36    44%

Coverage Is Not a Magic Bullet

  • Use coverage to see what is not tested
  • Aim for coverage of the complex parts of your software
  • 100% test coverage != bug free

A simple example (what happens when y=0?)

1 def divide(x, y):
2     """
3     Divides the first argument by the second argument
4 
5     >>> divide(8, 2)
6     4
7     """
8     z = x/y
9     return z

Linting

Keeping your code clean

Checking Your Code for Lint

Static analysis of your project looking for "suspicious" code such as:

  • Unused imports
  • Missing imports and methods
  • Mismatched function signatures
  • PEP-8 (style) violations

Python Linting Tools

  • Pylint
  • Pyflakes
  • Pychecker

We use Pylint for a few reasons:

  • Provides the most thorough evaluation
  • Integrates with Hudson's Violation plugin
  • Overall score is easy to track and set minimum expecations

Automation

Keep it simple stupid

The Joel Test

Can you make a build in one step?

The Joel Test

Why?

  • Makes it simple to spool up new developers
  • Good programmers are lazy, they'll never do it unless it is easy
  • Even if they could, it'd be a massive waste of time
  • If you can't automate it, you can't do continuous integration

Automation Methods

  • Bash script
  • Makefile
  • Fabric
  • Buildout

Pick one and use it for all your projects

Our Build and Test Script

 1 #!/bin/bash
 2 
 3 cd $WORKSPACE
 4 TEST_SETTINGS_MODULE=$PROJECT_NAME.conf.test.settings
 5 COVER_PACKAGE=$PROJECT_NAME.apps
 6 
 7 echo "## Setting up virtualenv"
 8 virtualenv -q ve
 9 source ./ve/bin/activate
10 
11 echo "## Installing project and dependencies"
12 python setup.py --quiet develop
13 yes w | pip install -q -r dependencies/production.pip
14 
15 echo "## Installing testing tools"
16 pip install -q -e git+git://github.com/jbalogh/django-nose.git#egg=django-nose
17 pip install -q -e git+git://github.com/cmheisel/nose-xcover.git#egg=nosexcover
18 pip install -q coverage
19 pip install -q pylint
20 
21 echo "## Running tests with coverage"
22 manage.py test -v0 --noinput --settings=$TEST_SETTINGS_MODULE \
23  --with-xunit \
24  --with-coverage --cover-package=$COVER_PACKAGE --with-xcoverage
25 
26 echo "## Running Pylint"
27 pylint --rcfile=$WORKSPACE/.pylintrc -f parseable $PROJECT_NAME > pylint.txt
28 PYLINT_SCORE=`grep "Your code has been rated" pylint.txt | \
29  cut -d" " -f7|cut -d"/" -f1`
30 echo "YVALUE=$PYLINT_SCORE" > pylint.properties

Continuous Integration

  1. Build
  2. Test
  3. Report
  4. Repeat

Continuous Integration Systems

  • Cron
  • Buildbot
  • Hudson
  • CruiseControl
  • Bitten
  • Integrity
  • PonyBuild

Just Use Hudson

Just use Hudson

Titus Brown, PyCon 2010

Why Hudson?

  • Mature
  • Dead-simple setup
  • Lots of plugins
  • Handle lots of projects, build slaves, etc.

Plugins We Use

  • Batch Task Plugin
  • Green Balls
  • Hudson Cobertura plugin
  • Hudson GIT plugin
  • Hudson instant-messaging plugin
  • Hudson Violations plugin
  • Hudson IRC plugin
  • Plot Plugin

Hudson Demo

Putting the pieces together

Questions?

Peter Baumgartner
pete@lincolnloop.com

Please rate this talk at:
http://spkr8.com/t/4365