On a daily basis, we are surrounded by various rules to make our lives easier. For example, drivers must obey the highway code. Thanks to this, each driver knows how to behave on the road and what rules to follow. Therefore, driving a car is safer, drivers’ behaviour is predictable and everyone knows how to drive in such a way that others understand their intentions. For Python, there is a highway code equivalent named the Python Enhancement Proposal (PEP). This document contains rules to improve your Python code by making it more readable and consistent. Unluckily, even experienced programmers may have a problem recalling all of the rules written in the PEP. This is why Python linters were created.
What are Linters?
Linters are programs that advise about code quality by displaying warnings and errors. They can detect your Python code mistakes, notice invalid code patterns and find elements that do not follow your conventions. Python linters have a number of advantages, such as:
- Preventing bugs in a project.
- Making code readable for any python programmer.
- Catching unneeded pieces of code.
- Making code cleaner and less complex.
Of course, every approach has its disadvantages:
- Linters can return false positives.
- This process can be time-consuming.
- Some errors could be overlooked.
There are many Python linters that you can leverage in your day-to-day work. The logical lint tells us about actual code errors and the statistical lint helps the programer spot formatting issues. I will present how to install and run the most popular linters like:
- Pylint (version 2.12.1)
- Flake8 (version 4.0.1)
- PyFlakes (version 2.4.0)
Moreover, you will find a comparison of each output. This could help you choose the linter that best fits your needs.
An example of bad quality Python code
Let’s write some code that has several mistakes and doesn’t follow the PEP rules. Then we will check the Python code quality using different linters with the default configuration.
import math; global_value = 0 def Bad_Quality_Function(argument1): a = [1,2,3] a = 10 DivideByZero = math.e/0 if a==1: Another_Bad_Quality_Function() else: return result pass
Below some problems with the example code are listed:
- unnecessary semicolon in import math;
- missing newlines and whitespaces
- missing docstring
- unused global_value and argument1
- incorrect naming style
- selecting element from the list with incorrect index
- dividing by zero expression
- calling method that doesn’t exist
- returning undefined value
- unnecessary pass statement
The Pylint is a static Python code analysis tool that finds programming and stylistic errors, bugs, invalid code constructs and inconsistencies with the coding standards. Moreover, Pylint can be customized to our needs by silencing some of the reported bugs, e.g. output messages can contain only information about errors for the most important programming issues.
Pylint message consists of five types of errors:
- R – means you have to refactor code following good programming practices.
- C – your code violates the coding standards convention.
- W – warnings for stylistic problems or some slight programming issues.
- E – means errors, there are important programming issues.
- F – fatal errors that stop code processing.
To get Pylint you can use pip.
pip install pylint
To run this Python linter, simply write:
in your console where script.py is the name of the Python script with our bad code example.
(venv) user@user project % pylint script.py ************* Module main main.py:1:0: W0301: Unnecessary semicolon (unnecessary-semicolon) main.py:12:0: C0305: Trailing newlines (trailing-newlines) main.py:1:0: C0114: Missing module docstring (missing-module-docstring) main.py:2:0: C0103: Constant name "global_value" doesn't conform to UPPER_CASE naming style (invalid-name) main.py:3:0: C0103: Function name "Bad_Quality_Function" doesn't conform to snake_case naming style (invalid-name) main.py:3:0: C0116: Missing function or method docstring (missing-function-docstring) main.py:4:4: C0103: Variable name "a" doesn't conform to snake_case naming style (invalid-name) main.py:6:4: C0103: Variable name "DivideByZero" doesn't conform to snake_case naming style (invalid-name) main.py:8:8: E0602: Undefined variable 'Another_Bad_Quality_Function' (undefined-variable) main.py:11:8: W0101: Unreachable code (unreachable) main.py:10:15: E0602: Undefined variable 'result' (undefined-variable) main.py:11:8: W0107: Unnecessary pass statement (unnecessary-pass) main.py:3:0: R1710: Either all return statements in a function should return an expression, or none of them should. (inconsistent-return-statements) main.py:3:25: W0613: Unused argument 'argument1' (unused-argument) main.py:6:4: W0612: Unused variable 'DivideByZero' (unused-variable) ---------------------------------------------------------------------- Your code has been rated at -13.00/10 (previous run: -13.00/10, +0.00)
The Pylint output message informs about 15 issues that should be resolved if we want to improve Python code quality. Let’s summarize them in the table below:
Pylint informed us about all of the errors that we had expected, apart from the errors related to selecting an element from a list with an incorrect index, the division by zero expression and missing whitespaces. Additionally, Pylint pointed out that after the if-else statement the code becomes unreachable, which means that it will never be executed. At the end of the Pylint message, we can see that Pylint rated the code at -13.00/10 (yes, minus thirteen!).
Another example of a Python linter is Flake8. It returns a report that contains information about incorrect style, syntax inconsistencies and complexity errors. There are many extra options that allow you to fit Flake8 to your needs.
Flake8 runs the three tools listed below by launching the single command flake8:
- pycodestyle – checking style
- PyFlakes – checking syntax
- Ned Batchelder’s McCabe script – checking complexity
Flake8’s output consists of error types such as:
- E – style errors
- W – style warnings
- F – syntax errors
- C – complexity errors
- N – naming conventions errors
To install Flake8 you can use pip.
pip install flake8
Running Flake8 is easy. Run:
to see the results.
(venv) user@user project % flake8 script.py main.py:1:12: E703 statement ends with a semicolon main.py:3:1: E302 expected 2 blank lines, found 0 main.py:4:11: E231 missing whitespace after ',' main.py:4:13: E231 missing whitespace after ',' main.py:6:5: F841 local variable 'DivideByZero' is assigned to but never used main.py:7:13: E225 missing whitespace around operator main.py:8:9: F821 undefined name 'Another_Bad_Quality_Function' main.py:10:16: F821 undefined name 'result' main.py:12:1: W391 blank line at end of file
Flake8 reported 9 possible errors. Let’s summarize them in a table.
Flake8 identified the unnecessary semicolon, missing newlines and whitespaces, unused local argument (but it didn’t report the unused global argument), returning undefined value and the calling method that doesn’t exist. This output message is definitely less detailed than the Pylint one, but this exactly why some developers prefer Flake8. It reports only about the most important style, syntax, and complexity errors.
PyFlakes analyzes python codes for simple errors. PyFlakes will never complain about style and will try very hard to never throw out false positives.
To install Pyflakes use pip:
pip install pyflakes
Running this Python linter is very similar to Pylint and Flake8. Just write the program name and path to your code like in the example:
(venv) user@user project % pyflakes script.py main.py:6:5 local variable 'DivideByZero' is assigned to but never used main.py:8:9 undefined name 'Another_Bad_Quality_Function' main.py:10:16 undefined name 'result'
PyFlakes reported only 3 problems. Let’s summarize them in a table.
PyFlakes only complains about the non-existent calling method and unused argument. The output is clear and errors are not categorized into specific types.
Which is the best python linter for your project?
Objectively choosing the best programming tools is probably impossible. The thing is, some technologies and tools are right for one type of projects, while others are better for other types of projects. So, how do you select the proper linters for your project? The Python linters mentioned in this article are open-source and available for free; hence they have been tested by many professionals, and they are quite popular among developers around the world. If you want to improve your Python code with linters – and you really should leverage them in your work – first try those recommended in this article.
In this article, I have compared three linters: Pylint, Flake8, and PyFlakes. Each program was run without any additional settings. Pylint reported most of the bugs, including some that we hadn’t expected (unreachable code after the if-else statement). Flake8 reported fewer bugs than Pylint. It didn’t inform us about missing docstrings, incorrect naming styles and the unnecessary pass statement. However, only Flake8 reported missing whitespaces. PyFlakes returned the shortest list of errors. It reported only about the unused argument, undefined value and non-existent calling method. None of the tested linters warned us about selecting an element from the list with incorrect index or the division by zero expression. Even so, this is explainable because these are not stylistic, but rather, logical errors.
It is difficult to decide which of the linters tested in this article is the best. Please note that each of the tools can be customized with additional settings. However, I hope that the article has helped you choose the linter that best fits your needs. Contact us if you require assistance from experienced Python programmers for your project.
Check out our blog for more details on Data Engineering:
- What Is A Relational Database? Advantages and disadvantages of relational databases.
- Database Denormalization – what is it and practical examples
- What is Programmatic Advertising