PyLint: Analyzing Python Code

Python code analysis can be a heavy subject, but it can be very helpful in making your programs better. There are several Python code analyzers that you can use to check your code and see if they conform to standards. pylint is probably the most popular. It's very configurable, customizable and pluggable too. It also checks your code to see if it conforms to PEP8, the official style guide of Python Core and it looks for programming errors too. We're going to spend a few minutes looking at some of the things you can do with this handy tool.

Getting Started

Sadly, pylint isn't included with Python, so you'll need to go out and download it from Logilab or PyPI. If you have pip installed, then you can install it like this:


pip install pylint

Now you should have pylint installed along with all its dependencies. Now we are ready to roll!

Analyzing Your Code

The latest version as of this writing is 0.25.1. Once pylint is installed, you can run it on the command line without any arguments to see what options it accepts. Now we need some code to test with. Since I wrote some crummy code for my PyChecker article last year, we'll re-use that here and see if pylint picks up the same problems. There should be four issues. Here's the code:

import sys
 
########################################################################
class CarClass:
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self, color, make, model, year):
        """Constructor"""
        self.color = color
        self.make = make
        self.model = model
        self.year = year
 
        if "Windows" in platform.platform():
            print "You're using Windows!"
 
        self.weight = self.getWeight(1, 2, 3)
 
    #----------------------------------------------------------------------
    def getWeight(this):
        """"""
        return "2000 lbs"

Now let's run pylint against this code and see what it finds. You should get something like the following:


C:\Users\mdriscoll\Desktop>pylint crummy_code.py
No config file found, using default configuration
************* Module crummy_code
C: 1,0: Missing docstring
C: 4,0:CarClass: Empty docstring
E: 15,24:CarClass.__init__: Undefined variable 'platform'
E: 18,22:CarClass.__init__: Too many positional arguments for function call
E: 21,4:CarClass.getWeight: Method should have "self" as first argument
C: 21,4:CarClass.getWeight: Invalid name "getWeight" (should match [a-z_][a-z0-9
_]{2,30}$)
C: 21,4:CarClass.getWeight: Empty docstring
R: 21,4:CarClass.getWeight: Method could be a function
R: 4,0:CarClass: Too few public methods (1/2)
W: 1,0: Unused import sys

Report
======
13 statements analysed.

Messages by category
--------------------

+-----------+-------+---------+-----------+
|type |number |previous |difference |
+===========+=======+=========+===========+
|convention |4 |NC |NC |
+-----------+-------+---------+-----------+
|refactor |2 |NC |NC |
+-----------+-------+---------+-----------+
|warning |1 |NC |NC |
+-----------+-------+---------+-----------+
|error |3 |NC |NC |
+-----------+-------+---------+-----------+

Messages
--------

+-----------+------------+
|message id |occurrences |
+===========+============+
|C0112 |2 |
+-----------+------------+
|W0611 |1 |
+-----------+------------+
|R0903 |1 |
+-----------+------------+
|R0201 |1 |
+-----------+------------+
|E1121 |1 |
+-----------+------------+
|E0602 |1 |
+-----------+------------+
|E0213 |1 |
+-----------+------------+
|C0111 |1 |
+-----------+------------+
|C0103 |1 |
+-----------+------------+

Global evaluation
-----------------
Your code has been rated at -6.92/10

Statistics by type
------------------

+---------+-------+-----------+-----------+------------+---------+
|type |number |old number |difference |%documented |%badname |
+=========+=======+===========+===========+============+=========+
|module |1 |NC |NC |0.00 |0.00 |
+---------+-------+-----------+-----------+------------+---------+
|class |1 |NC |NC |0.00 |0.00 |
+---------+-------+-----------+-----------+------------+---------+
|method |2 |NC |NC |50.00 |50.00 |
+---------+-------+-----------+-----------+------------+---------+
|function |0 |NC |NC |0 |0 |
+---------+-------+-----------+-----------+------------+---------+

Raw metrics
-----------

+----------+-------+------+---------+-----------+
|type |number |% |previous |difference |
+==========+=======+======+=========+===========+
|code |12 |63.16 |NC |NC |
+----------+-------+------+---------+-----------+
|docstring |4 |21.05 |NC |NC |
+----------+-------+------+---------+-----------+
|comment |3 |15.79 |NC |NC |
+----------+-------+------+---------+-----------+
|empty |0 |0.00 |NC |NC |
+----------+-------+------+---------+-----------+

Duplication
-----------

+-------------------------+------+---------+-----------+
| |now |previous |difference |
+=========================+======+=========+===========+
|nb duplicated lines |0 |NC |NC |
+-------------------------+------+---------+-----------+
|percent duplicated lines |0.000 |NC |NC |
+-------------------------+------+---------+-----------+

If you want to know what those items in the Messages section mean with their cryptic ids, you can make pylint tell you by doing this on the command line:


pylint --help-msg=C0112

However, we really only care about the first section of the report as the rest is basically just tables showing the same information in a more obscure manner. Let's take a look at that section more closely:


C: 1,0: Missing docstring
C: 4,0:CarClass: Empty docstring
E: 15,24:CarClass.__init__: Undefined variable 'platform'
E: 18,22:CarClass.__init__: Too many positional arguments for function call
E: 21,4:CarClass.getWeight: Method should have "self" as first argument
C: 21,4:CarClass.getWeight: Invalid name "getWeight" (should match [a-z_][a-z0-9
_]{2,30}$)
C: 21,4:CarClass.getWeight: Empty docstring
R: 21,4:CarClass.getWeight: Method could be a function
R: 4,0:CarClass: Too few public methods (1/2)
W: 1,0: Unused import sys

First we need to figure out what the letters designate: C is for convention, R is refactor, W is warning and E is error. pylint found 3 errors, 4 convention issues, 2 lines that might be worth refactoring and 1 warning. The 3 errors plus the warning were what I was looking for. We should try to make this crummy code better and reduce the number of issues. We'll fix the imports and change the getWeight function to get_weight since camelCase isn't allowed for method names. We also need to fix the call to get_weight so it passes the right number of arguments and fix it so it has "self" as the first argument. Here's the new code:

# crummy_code_fixed.py
import platform
 
########################################################################
class CarClass:
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self, color, make, model, year):
        """Constructor"""
        self.color = color
        self.make = make
        self.model = model
        self.year = year
 
        if "Windows" in platform.platform():
            print "You're using Windows!"
 
        self.weight = self.get_weight(3)
 
    #----------------------------------------------------------------------
    def get_weight(self, this):
        """"""
        return "2000 lbs"

Let's run this against pylint and see how much we've improved the results. For brevity, we'll just show the first section below:


C:\Users\mdriscoll\Desktop>pylint crummy_code_fixed.py
No config file found, using default configuration
************* Module crummy_code_fixed
C: 1,0: Missing docstring
C: 4,0:CarClass: Empty docstring
C: 21,4:CarClass.get_weight: Empty docstring
W: 21,25:CarClass.get_weight: Unused argument 'this'
R: 21,4:CarClass.get_weight: Method could be a function
R: 4,0:CarClass: Too few public methods (1/2)

That helped a lot! If we added docstrings, we could halve the number of issues.

Wrapping Up

The next step would be to try running pylint against some of your own code or against a Python package like SQLAlchemy and seeing what gets output. You can learn a lot about your own code using this tool. If you have Wingware's Python IDE, you can install pylint as a tool that you can run with just a couple mouse clicks. You may find some of the warnings to be annoying or not even really applicable. There are ways to suppress such things as deprecation warnings through command line options. Or you can use the --generate-rcfile to create an example config file that will help you control pylint. Note that pylint does not import your code, so you don't have to worry about undesirable side effects.

At this point you should be ready to start improving your own code base. Go forth and make your code amazing!

Further Reading

Copyright © 2024 Mouse Vs Python | Powered by Pythonlibrary