Let me take you on a journey. It's the journey everyone goes through when they first hear about pylint.

Step 1: Install it

$ pip install pylint

Step 2: Run it

$ pylint
No config file found, using default configuration
Usage:  pylint [options] module_or_package
... reams of help output ...

Step 3: It probably needs a path then

$ pylint .
No config file found, using default configuration
************* Module my_project
F:  1, 0: error while code parsing: Unable to load file '__init__.py' ([Errno 2] No such file or directory: '__init__.py') (parse-error)

Step 4: Ok, so it needs a package. But my project has many packages... ok then.

$ pylint my_project/package1 my_project/package2 my_project/package3

At this point, Pylint runs, and you sit there aghast as 1,000,000 errors stream past you faster than you can read them. From what you spot, you see that a lot are 'line too long' errors, because pylint uses 80 characters by default. You also see a lot of 'not documented' warnings, because let's be honest, who needs to document every function?

It's quite easy to give up at this point, as it seems like the output will be useless to you.

Step 5:

$ pip uninstall pylint

Stop! Pylint is Great

Pylint is a fantastic tool which has been going for centuries and has several dedicated maintainers who are also really lovely people - at EuroPython 2014, I was able to meet them at the 'static analysis dinner'. The logilab blog shows just how much work goes on.

To get the best out of Pylint, however, you do need to spend the time tweaking it. It's like a new puppy, very eager to impress by showing you all the things it can do, but what you actually want is usually a subset of that.

This is where prospector comes in. It is the command-line spinoff from Landscape, designed to give a simple, quick way to get useful output from static analysis tools.

Step 1:

$ pip install prospector
$ prospector
... lots of useful stuff ...

Sensible Defaults

Chances are, you will consider most of the warnings that come from tools like pylint or pep8 or pyflakes to be a bit picky. There are warnings about line length, there are warnings about whitespace on empty lines, there are warnings about how much space there is between methods on your class. What you probably want, however, is a list of actual problems in your code.

For that reason, Prospector has a series of settings and default behaviours to supress the more picky warnings and only provide things that are important. This is the 'strictness' of Prospector and can be configured:

$ prospector --strictness high

A strictness of veryhigh is basically all output of all tools, but the defaut of medium is designed to hide the pernickety in favour of the important. Ideally, the real errors are not lost in a swathe of stylistic errors.

Changing Behaviour Based on Dependencies

If you run pylint on a Django project with its default settings, it will warn you:

Class 'MyModel' has no 'objects' member

It will also inspect all of the south migrations that you have, and various other things that are... unwelcome. Django creates lots of things at runtime via meta-programming, so it's hard for static analysis tools to get a complete picture of what's actually going to happen without running the code.

For Django specifically, I wrote a pylint plugin called pylint-django to aid it in understanding Django code. There's also one for celery although it is very basic at the moment. I hope to add more plugins for more libraries and frameworks in the future.

Prospector will do its best to detect what your project uses as libraries and will automatically adjust the output of the tools it runs depending on what you use. For example, it will detect Django and include the pylint-django plugin. This will give you many useful messages that are specific to Django code.

Composable configuration

Should you wish to delve further into the configuration of prospector, you can use profiles. These are YAML files with directives about which tools and messages to disable or enable.

If you have a project using django, you may have configuration to reflect that. If all you can have is one single configuration file, then you can't reuse configuration in a project which uses django and also celery. Profiles in prospector are able to inherit from each other - so you could create one specific to how you want to use django, and one specific to how you want to use celery, and if you use both in the same project, simply inherit from both.

When I first described Landscape to a friend of mine, he liked the idea of creating your own 'coding style guides' and letting people inspect and share the guides of others. Profiles were designed with this in mind; it may be a space-fairy-unicorn kind of idea but I'd like to see that in the future.

Unified Output

All tools have their own way of outputting messages, their own way of formatting their message, and even their own idea of what exit codes to use if messages are found. This can make it tricky to parse the output of more than one tool. With Prospector, since all tools are sub-commands, and the output is handled and reorganised into a consistent whole. There are several output formats available, including human readable and machine readable.

As Prospector runs six different tools by default, there is naturally some overlap. The same error can be pointed out by several of the tools a the same time. Prospector therefore has the 'blender', which merges messages together when they are warning about the same thing, so that you only get a single message.

Drop-in Replacement

One of the benefits of pylint is that there are several plugins and tools designed to use it already out there. For example, there's a Jenkins plugin. By using the pylint output format, it should be possible to slot prospector in where pylint once was.

$ prospector --output-format pylint

This feature is somewhat untested in the wild, so if you find that it doesn't work in your case, please add an issue and I'll see what I can do to help!

Static Analysis For Humans

The whole purpose of this project is to get people using static analysis. I think they're an excellent resource for projects and can help you find problems in your code and even learn new things. I even gave a rather rambly talk about it at EuroPython, because I honestly believe that software gets better by using automatic checks. At the very least, it won't get worse.

I hope that prospector becomes the 'gateway drug' for static analysis. It's likely that if you want complete control over the analysis of your project, prospector won't be quite configurable enough. However, if you want that level of control, you're probably already using static analysis tools. The goal of prospector therefore is to get people kickstarted, to give projects a sensible base to start from, and the hope is that they will get a lot of benefit, and perhaps even branch out into configuring the tools directly.

Prospector is open source and available on GitHub, and all contributions are welcome. And, if you would like to have prospector run continuously against your code, you can try out Landscape.io!

If you enjoyed this article and would like to receive email notifications when new articles are published, sign up below:

About Landscape.io

Landscape.io is a tool to measure and track code quality and technical debt in your project. It can analyse Python code to point out errors and problems, and provides continuous metrics so you can see if your code is deteriorating.