Static checking Python code

Python's sanity checking features are very limited, a module like this will not produce any errors:
import os

def function_with_error():
    print this_isnt_defined

In my opinion this practice is pretty lame, especially with big code bases. Even if Python is dynamic, I would appreciate some sanity checking to reduce the number of bugs. I have researched and extended Pyflakes to do the most simple checks.

Sanity checking is also really appreciated when refactoring code, I am currently cleaning up Plurk's code base and I find myself handicapped without any checks what so ever.

Ways to check code

Currently, there are three solutions to check sanity of Python code:

  • Pylint: Mainly checks for code smell
  • Pychecker: More advanced checking than Pylint, it evaluates code thought and does not support recursive checks. It's also slow.
  • Pyflakes: Checks for errors without evaluating code (by using compiler package). Pretty limited checks thought.

I have tested all of them and I think Pyflakes is the most simple and solves the current problem I am having with refactoring code.

Limitations of Pyflakes

Pyflake does very basic checking (tests like if things are used or defined) and it also skips checking modules that use star imports (which we use at some places in Plurk's codebase). The code base is pretty simple and I have extended Pyflakes with following things:

  • Ability to specify what star imports resolve to
  • Better sorting of warnings/errors
  • Support for Python 2.5

Example usage

A module had following imports:

import time
import logging
import atexit
from datetime import datetime, timedelta

import json
from amiweb.amiweb import session, request, AppError, profile, expose

from import partial, has_keys
from plurk.signals import signal, send
from plurk.db import *
from plurk import exceptions
from users import getCurrentUser

After running it through Pyflakes we can see that a lot of these imports are not used any longer:

$ pyflakes -s plurk/
plurk/ 'AppError' imported but unused
plurk/ 'profile' imported but unused
plurk/ 'has_keys' imported but unused
plurk/ 'send' imported but unused
plurk/ 'signal' imported but unused

Cleaning up the imports reduces them to following:

import json
from datetime import datetime, timedelta

from plurk.standard import *

I can now rerun Pyflakes to see if everything is alright.

Grab my patches to Pyflakes

I'll try to send my patched to the folks of Divmod, but until then download my version of Pyflakes.

Hope for the future

I really hope in the future that we'll see much smarter checks for Python code. Python (and all the other dynamic languages) can learn a lot by Java's or Haskell's extreme useful compilers.

4. Nov 2008 Code · Code rewrite · Python
© Amir Salihefendic