We understood the difference between pep8 and linters in previous post. We’ll see various linters available in python with sample example.
Sample program:
I’ve written a error-prone program. It contains some unused imported modules, variables and arguments. It also overriddes one function. I’ve intentionally not followed pep8’s guidelines. Let’s see output of each linter.
#!/usr/bin/env python
# encoding: utf-8
#unused modules
import os
import sys
import argparse
#unused global constant variable
user_name = 'rootpy'
def setUserName(self, name):
fname = 'Prasad'
u_name = name
return u_name
class User(object):
def __init__(self, str, int):
self.name = None
self.password = None
return
def getUserPassword(self, password):
self.password = password
return
def changeUserName(self, email):
return
print "How can I reach here?"
def getUserPassword(self, password):
return self.password == password
###PyChecker * It imports each module and compiles the each function, class and method for possible errors. * It determines the imported modules, classes and functions and creates a tree based on it. * It can’t process the module if there is an import error. * It compiles and executes the imported module so your code get executed. * For example: If you’ve SQL queries in python program then it executes every time which is bad (IMO) * Let’s pass the sample_program.py and analyze the output.
prasad@Rootpy: master ⚡
$ pychecker sample_program.py
Processing module test_program_rootpy (test_program_rootpy.py)...
Warnings...
sample_program.py:5: Imported module (os) not used
sample_program.py:6: Imported module (sys) not used
sample_program.py:7: Imported module (argparse) not used
sample_program.py:15: self is argument in function
sample_program.py:16: Local variable (fname) not used
sample_program.py:23: Parameter (int) not used
sample_program.py:23: Parameter (str) not used
sample_program.py:32: Redefining attribute (getUserPassword) original line (28)
FAIL: 1
prasad@Rootpy: master ⚡
$
Here,
- It detects following errors:
- unused imported module, local variable and arguments errors.
- self is argument in function
- But it doesn’t detect following errors:
- unused global variable user_name.
- pep8 related errors (some linters detect it).
- It becomes slower than other linters because it imports all the modules and execute it one by one.
- You can create configure it by creating configuration file (.pycheckerr)
- Check here for more information
###PyFlake * It is a static code analyzer as It identifies errors without executing python code. * It is faster than PyChecker because it doesn’t import any module and execute it. * Let’s see output for sample_program.py
prasad@Rootpy: master ⚡
$ pyflakes sample_program.py
sample_program.py:5: 'os' imported but unused
sample_program.py:6: 'sys' imported but unused
sample_program.py:7: 'argparse' imported but unused
sample_program.py:16: local variable 'fname' is assigned to but never used
sample_program.py:32: redefinition of unused 'getUserPassword' from line 28
FAIL: 1
prasad@Rootpy: master ⚡
$
Here,
- It detects unused imported module, local variable and arguments related errors.
- It doesn’t detect following errors:
- Unused global variable user_name.
- pep8 style guide related error.
- Self is used as argument in function
- Redefining parameters like int, str which are built-in data type in python
- It examines the syntax tree of each file individually So it is faster.
- Check on github or docs for more information.
###Flake8 * It is a static code analyzer. Actually it is combination of pep8 and PyFlake. * It identifies programming errors as well as pep8 style-guide errors. * Let’s check what it gives on sample_program.py
prasad@Rootpy: master ⚡
$ flake8 sample_program.py
sample_program.py:5:1: F401 'os' imported but unused
sample_program.py:6:1: F401 'sys' imported but unused
sample_program.py:7:1: F401 'argparse' imported but unused
sample_program.py:15:1: E303 too many blank lines (3)
sample_program.py:16:5: F841 local variable 'fname' is assigned to but never used
sample_program.py:32:5: F811 redefinition of unused 'getUserPassword' from line 28
sample_program.py:39:1: W391 blank line at end of file
FAIL: 1
prasad@Rootpy: master ⚡
$
Here,
- It detects following errors:
- unused imported module, local variable, arguments error.
- pep8 related errors like blank line, too many blank line etc.
- It doesn’t detect following errors:
- unused global variable user_name.
- Self is used as argument in function
- Parameters like int, str which are built-in data type in python
- Check flake8’s docs for more information.
###PyLint * It is a static code analyzer which checks errors in python code. * It looks for bad codes and tries to enforce a coding standard. * It displays statistics about the number of warnings and errors found in different files. * It also gives overall mark, based on numbers of warnings and errors. Some key features as below: * Refactoring: * It also detects duplicates codes. * Fully Customizable: * You can create your own .pylintrc and modify which errors are important for you. * UML Diagram: * It creates UML diagrams for python code using Pyreverse * Editor and IDE integration: * Editor Integration are available for various editors like emacs, vim and eclipse * IDE integration are also available for various IDEs like spyder, editra and TextMate * Check list of supported editors and IDEs * It gives very descriptive output. The output is divided into two categories message and reports. * message contains warnings, errors, coding standard convention, Refactoring and Fatal errors. * error messages type: * [R]efactor for a “good practice” metric violation * [C]onvention for coding standard violation * [W]arning for stylistic problems, or minor programming issues * [E]rror for important programming issues (i.e. most probably bug) * [F]atal for errors which prevented further processing. * Reports contains statistics information for by type, raw metrics, duplication, message by category etc. * Let’s run pylint on sample_program.py and check the output.
prasad@Rootpy: master ⚡
$ pylint sample_program.py
************* Module sample_program
C: 1, 0: Missing module docstring (missing-docstring)
C: 11, 0: Invalid constant name "user_name" (invalid-name)
C: 15, 0: Invalid function name "setUserName" (invalid-name)
C: 15, 0: Missing function docstring (missing-docstring)
W: 15,16: Unused argument 'self' (unused-argument)
W: 16, 4: Unused variable 'fname' (unused-variable)
C: 21, 0: Missing class docstring (missing-docstring)
W: 23,28: Redefining built-in 'int' (redefined-builtin)
W: 23,23: Redefining built-in 'str' (redefined-builtin)
W: 23,28: Unused argument 'int' (unused-argument)
W: 23,23: Unused argument 'str' (unused-argument)
C: 28, 4: Invalid method name "getUserPassword" (invalid-name)
C: 28, 4: Missing method docstring (missing-docstring)
E: 32, 4: method already defined line 28 (function-redefined)
C: 32, 4: Invalid method name "getUserPassword" (invalid-name)
C: 32, 4: Missing method docstring (missing-docstring)
C: 35, 4: Invalid method name "changeUserName" (invalid-name)
C: 35, 4: Missing method docstring (missing-docstring)
W: 37, 8: Unreachable code (unreachable)
W: 35,29: Unused argument 'email' (unused-argument)
R: 35, 4: Method could be a function (no-self-use)
W: 5, 0: Unused import os (unused-import)
W: 6, 0: Unused import sys (unused-import)
W: 7, 0: Unused import argparse (unused-import)
Report
======
22 statements analysed.
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 |4 |NC |NC |25.00 |75.00 |
+---------+-------+-----------+-----------+------------+---------+
|function |1 |NC |NC |0.00 |100.00 |
+---------+-------+-----------+-----------+------------+---------+
Raw metrics
-----------
+----------+-------+------+---------+-----------+
|type |number |% |previous |difference |
+==========+=======+======+=========+===========+
|code |19 |55.88 |NC |NC |
+----------+-------+------+---------+-----------+
|docstring |1 |2.94 |NC |NC |
+----------+-------+------+---------+-----------+
|comment |4 |11.76 |NC |NC |
+----------+-------+------+---------+-----------+
|empty |10 |29.41 |NC |NC |
+----------+-------+------+---------+-----------+
Duplication
-----------
+-------------------------+------+---------+-----------+
| |now |previous |difference |
+=========================+======+=========+===========+
|nb duplicated lines |0 |NC |NC |
+-------------------------+------+---------+-----------+
|percent duplicated lines |0.000 |NC |NC |
+-------------------------+------+---------+-----------+
Messages by category
--------------------
+-----------+-------+---------+-----------+
|type |number |previous |difference |
+===========+=======+=========+===========+
|convention |11 |NC |NC |
+-----------+-------+---------+-----------+
|refactor |1 |NC |NC |
+-----------+-------+---------+-----------+
|warning |11 |NC |NC |
+-----------+-------+---------+-----------+
|error |1 |NC |NC |
+-----------+-------+---------+-----------+
Messages
--------
+-------------------+------------+
|message id |occurrences |
+===================+============+
|missing-docstring |6 |
+-------------------+------------+
|invalid-name |5 |
+-------------------+------------+
|unused-argument |4 |
+-------------------+------------+
|unused-import |3 |
+-------------------+------------+
|redefined-builtin |2 |
+-------------------+------------+
|unused-variable |1 |
+-------------------+------------+
|unreachable |1 |
+-------------------+------------+
|no-self-use |1 |
+-------------------+------------+
|function-redefined |1 |
+-------------------+------------+
Global evaluation
-----------------
Your code has been rated at -2.73/10
FAIL: 30
prasad@Rootpy: master ⚡
$
Here,
- It detects highest number of errors compared to other linters.
Following errors which are detected by all other linters as well as PyLint:
- unused variables, imported modules, arguments etc.
- Invalid variable name, method name etc.
- But there is a lot of differnece between Pylint’s output and other’s output.
It detects following errors which other linters don’t detect.:
- Unreachable code
- Redefining built in int and str
- unused global variable user_name which other linters are failed to detect.
- missing doc strings.
- Report contains various statistics information which is useful for programmer.
- It’s awesome code rating. It has given -2.7 rating to sample program. (OMG)
- Pylint has great documentation. Every thing is well explained.
- You can configure pylint by creating configuration file i.e .pylintrc file.
- For example: I’ve created a pylint configuration file for Web2Py.
###PyLama * It is a static code analyzer which combines power of other linters. * It wrapes following tools: * PEP8 * PEP257 * PyFlakes * Mccabe * PyLint * Let’s check it’s output
prasad@Rootpy: master ⚡
$ pylama sample_program.py
sample_program.py:4:1: E265 block comment should start with '# ' [pep8]
sample_program.py:5:1: W0611 'os' imported but unused [pyflakes]
sample_program.py:6:1: W0611 'sys' imported but unused [pyflakes]
sample_program.py:7:1: W0611 'argparse' imported but unused [pyflakes]
sample_program.py:10:1: E265 block comment should start with '# ' [pep8]
sample_program.py:15:1: E303 too many blank lines (3) [pep8]
sample_program.py:16:1: W0612 local variable 'fname' is assigned to but never used [pyflakes]
sample_program.py:32:1: W0404 redefinition of unused 'getUserPassword' from line 28 [pyflakes]
sample_program.py:39:1: W391 blank line at end of file [pep8]
FAIL: 1
prasad@Rootpy: master ⚡
Here,
- It detects following errors:
- unused variable, imported module and argument errors.
- redefinition of function.
- pep8 errors
- It doesn’t detect following errors:
- unreachable code
- missing doc string
- Self is used as argument in function
- redefining built-in datatype like int, str etc.
- invalid function name like getUserPassword etc.
Conclusion:
- Most of these tools detect some of the errors in sample_program.py. We saw all tools output for sample_program.py
- PyLint gives us very descriptive output as well as detects various errors.
- So we can deduce that Pylint’s is a very powerful linters for python programming.