Enable Dark Mode!
evaluating-code-quality-using-python-pylint.jpg
By: Nihala MK

Evaluating Code Quality Using Python Pylint

Technical Odoo 16 SourceCode

Pylint is a Python programming language quality checker that follows the PEP 8 style guide. With the primary objective of enhancing the readability and uniformity of the code, this article offers recommendations for writing understandable Python code. It is essential to follow standards that will make it easier for ourselves and others to comprehend accessible code since code is read far more frequently than it is created. This Blog explains 10 PEP 8 guidelines that can help make your Python code simpler to understand for others and how to use Pylint to verify them. Let's get going!

Installation

Make sure Python is set up on your computer before you install Pylint. The following command should be entered into your computer's command prompt (Windows) or terminal (Linux).

pip install pylint

Enter the following command to check the pylint installation.

Use Pylint

Once Pylint is installed, we can quickly use it by entering the following command along with the file name:

Pylint file_name.py

Coding conventions (PEP 8)

PEP 8 is a style manual that outlines the proper formatting for Python code to improve readability. We will only touch on a few major issues in this Blog, so I recommend checking out the guide if you want to learn more about the subject in depth.

The link is here: https://peps.python.org/pep-0008/

1. Multi-line statement

Python uses parenthesis (()), brackets ([]), and braces () to indicate an implied continuation line. The term "implicit" refers to the use of a statement that spans numerous lines without using the line continuation character ().

It is recommended to position the wrapped element vertically or to use hanging indentation when using implicit continuation lines. When describing parenthesized statements in Python, hanging indentation refers to the practice of indenting lines up until the closing parenthesis, with the opening parenthesis serving as the final non-whitespace character on each line.

Bad Practice

Arguments included in parentheses are not indented or vertically aligned.

 # multi-line statement
 def multiply(arg1, arg2, arg3,
    arg4, arg5):
    """The function multiplies five numbers""" 
    return arg1 * arg2 * arg3 * arg4 * arg5

Pylint Output

Pylint recognizes the wrong indentation.

Good Practice

Arguments that are included in parentheses are vertically aligned.

 # multi-line statement
 def multiply(arg1, arg2, arg3,
              arg4, arg5):
     """The function multiplies five numbers"""
     return arg1 * arg2 * arg3 * arg4 * arg5

Arguments inside the parenthesis are aligned using hanging indentation.

# multi-line statement
def multiply(
       arg1, arg2, arg3,
       arg4, arg5):
    """The function multiplies five numbers"""
    return arg1 * arg2 * arg3 * arg4 * arg5

2. Operators

Always add a single space to each side of the following operators:

Assignment (=)

* Augmented assignment (+=, -=, etc)

* Comparison operators (<, >, <=, >=, ==, !=)

* Logical operators (and, or, not)

* Membership operator(in, not in)

* Identity operator(is, not is)

Bad Practice

There are no spaces around the operators.

# pylint: disable=C0114
# Comparison and assignment operators
print(5<4)
 # Membership operator
 if 5in[1, 2, 3, 4, 5]:
     print('is contained')
 Else:
     print('is not contained')

By adding a comment at the beginning of the code, as shown above (#pylint: disable=C0114), we may quickly disable a warning in Pylint.

The absence of a module docstring is indicated by the C0114 warning. A module, function, class, or method definition's opening statement is referred to as a docstring. All modules should include a docstring at the start that describes what the module does in accordance with PEP 257 (the handbook providing docstring norms).

In order to keep things simple, we won't put a docstring at the top of each module in this post. Writing documentation in practice, however, is strongly advised. The same as previously, I advise you to have a closer look at PEP 257 recommendations if you want to learn more about how to utilize docstrings in Python.

For the sake of simplicity, we will not include a docstring at the start of each module in this post. However, it is strongly advised to write doctrings in practice. As previously said, if you want to learn more about how to use doctrings in Python, I recommend you read the PEP 257 recommendations.

link:https://peps.python.org/pep-0257/

3. Whitespaces after a comma, semicolon, or colon (but not before!)

After a comma, semicolon, or colon, we must use whitespace. However, PEP 8 suggests avoiding them as soon before.

Bad practice

We can see the following improper practices in the code:

* There is no whitespace following the comma that separates each element of the list.

* There is no whitespace behind the colon (:) that separates each key-value pair in the dictionary.

* There is a whitespace exactly before the comma that separates each element of the tuple.

# list 
numbers = [1,2,3,4,5]
# dictionary - students and grades 
grades = {'Ramu':10, 'Anna':9.5, 'Pooja':3.5}
# tuple - latitude and longitude
coordinates = (48.137 , 11.576)

Pylint output

Pylint finds that a whitespace character is required after each comma that separates the list's items (numbers). Furthermore, pylint identifies a space before the comma in the tuple (coordinates). It does not, however, recognize the missing space after the colon that divides each key-value combination.

I'd like to point out that the PEP8 guidelines do not state that a whitespace is necessary after a comma, colon, or semicolon. However, it is a common practice that most style checkers identify as pylint.

Good Practice

There are spaces after each comma, colon, and semicolon, but not before.

# list 

numbers = [1, 2, 3, 4, 5]

# dictionary - students and grades 

grades = {'Ramu': 10, 'Anna': 9.5, 'Pooja': 3.5}

# tuple - latitude and longitude 

coordinates = (48.137, 11.576)

4. Whitespaces between brackets, brackets or braces.

Within brackets, brackets or brackets, whitespaces are utilized.

# immediately inside parentheses, brackets or braces.
# after parentheses
print( 'Hello world' )
# after brackets 
numbers = [ 1, 2, 3, 4, 5 ]
# after braces
cities = { 'Mumbai', 'Goa', 'Chennai' }

Pylint Output

Pylint detects that there are no spaces before or after brackets, brackets, or braces.

Good Practise

There are no whitespaces directly inside brackets, brackets, or braces.

# immediately inside parentheses, brackets or braces.

# after parentheses

print('Hello world')

# after brackets 

numbers = [1, 2, 3, 4, 5]

# after braces

cities = {'Mumbai', 'Goa', 'Chennai'}

5. Arguments for keywords and defaults

Keyword arguments

An argument is a value that is passed to a function when it is called. Python functions take two kinds of parameters: positional arguments and keyword arguments. When utilizing positional arguments in order to call a function, the parameters must be provided in the correct sequence. On the contrary, because the keyword arguments are preceded by an identifier (keyword=value), they can be supplied in any sequence. When invoking a function with keyword parameters, we do not need to put spaces around the = sign, according to PEP8.

Default arguments

Default values can be assigned to function parameters. If no argument is given when calling a function, the default value is used. When defining default parameters, no spaces are used around the = sign, as previously.

Bad Practice

When applying keyword and default parameters, spaces are used around the = symbol.

# pylint: disable=C0114
# keyword and default arguments 
# function that calculates cylinder volume 
def cylinder_volume(height, radius = 5.0):
     """Volume of a cylinder 
     Parameters:
     height (float): Height of the cylinder
     radius (float): Radius of the cylinder (default 5.0)
     Returns:
     float:Volume of the cylinder 
     """ 
     return height * 3.14159 * radius**2
# calling the function using positional arguments 
cylinder_volume(10.3, 7.2)
# calling the function using keyword arguments 
cylinder_volume(height = 10.3, radius = 7.2)

Pylint output

Spaces are not permitted around keyword arguments, according to Pylint.

Good practice

There are no spaces surrounding the = symbol.

# pylint: disable=C0114
# keyword and default arguments 
# function that calculates cylinder volume 
def cylinder_volume(height, radius=5.0):
     """Volume of a cylinder 
     Parameters:
     height (float): Height of the cylinder
     radius (float): Radius of the cylinder (default 5.0)
     Returns:
     float:Volume of the cylinder 
     """ 
     return height * 3.14159 * radius**2
# calling the function using positional arguments 
cylinder_volume(10.3, 7.2)
# calling the function using keyword arguments 
cylinder_volume(height=10.3, radius=7.2)

6. Catching exceptions

Python error messages are classified into two types: syntax errors and exceptions. Syntax errors arise when Python is unable to comprehend the code, signaling that something is incorrect with the program's syntax. Exceptions, on the other hand, arise when unexpected things happen during code execution.

Python displays a notice in the following block code showing that we encountered a TypeError when attempting to convert a text to a hexadecimal number because the hex function requires an integer as input.

# an exception is raised when trying to convert a string to a hexadecimal number
hex('1')
# TypeError: 'str' object cannot be interpreted as an integer

In Python, we use the try and except statements to manage exceptions. The operation that potentially causes an exception is contained in the try clause and the code that handles the exception is contained in the except clause. The try clause is executed one statement at a time. If an exception occurs, the try statement is terminated, and the except statement is performed.

Bad practice

When an exception occurs during the execution of the try block, the code in the except block is performed. According to PEP8 standards, using bare unless clauses is not advised since they capture all exceptions, including SystemExit and KeyboardInterrupt exceptions, making it more difficult to terminate a programme with Control-C.

# pylint: disable=C0114
# exceptions 
def int2hex(integer):
    """Function that converts an integer to a hexadecimal string""" 
    try: 
        hexadecimal = hex(integer) 
        return hexadecimal 
    except: 
        print('An integer should be provided as input')
# call the function 
int2hex(5)

Pylint output

Pylint detects the use of a simple unless clause.

Good practice

In the except block, we should define the error we wish to handle:

# pylint: disable=C0114
# exceptions 
def int2hex(integer):
    """Function that converts an integer to a hexadecimal string""" 
    try: 
        hexadecimal = hex(integer) 
        return hexadecimal 
    except TypeError: 
        print('An integer should be provided as input')
# call the function 
int2hex(5)

The programme now captures the TypeError error but not the others.

7. Boolean variables

Bad practice

It is not permissible to use == or is to compare boolean variables to True or False in if statements.

# pylint: disable=C0114 
# boolean
is_raining = True
if is_raining == True:
    print('Take an umbrella') 
else: 
    print('You do not need an umbrella')
Pylint output
Pylint recognizes that the comparison to True should just be the expression (is_raining).
Good practice
In the if statement, we have to use the boolean variable directly as follows:
# pylint: disable=C0114 
# boolean
is_raining = True
if is_raining:
    print('Take an umbrella') 
else: 
    print('You do not need an umbrella')

8. Check for prefixes and suffixes

Bad practice

Using string slicing to check for prefixes or suffixes is a bad practice.

# pylint: disable=C0103
# pylint: disable=C0114 
# check for prefixes and suffixes 
name = 'Meena' 
if name[0] == 'M': 
    print('Name starts with M')
if name[-3:] == 'ing':
   print('Name ends with ing')
Pylint output

pylint, however, does not identify this style issue, despite the fact that it is mentioned in the PEP 8 guide.

Good practice

To check for prefixes or suffixes, we must use str.startswith() and str.endswith() according to the PEP8 style rule.

# pylint: disable=C0103
# pylint: disable=C0114 
# check for prefixes and suffixes 
name = 'Meena' 
if name.startswith('M'): 
    print('Name starts with M')
if name.endswith('ing')':
   print('Name ends with ing')

str.startswith(prefix, start, end) determines if a string begins with a given prefix. If the string begins with the provided prefix, the method returns True; otherwise, it returns False. Start and end are optional parameters that define where the check starts and concludes.

If the string (str) finishes with a provided suffix, the str.endswith(suffix, start, end) method returns True. If it does not, it returns False. The function, like previously, contains two optional parameters start and end, which indicate the indices from which the test begins and finishes.

9. Imports

A module is a Python code file. When working on large projects, it makes logical to organize your code into numerous files and import them into other Python files as needed. To import a Python module, just write import followed by the filename. In addition to importing our own modules, we may import built-in modules from the Python standard library and third-party libraries. Imports are written at the top of the Python script, one at a time, according to PEP8 rules. Furthermore, imports should be organized in the following order:

Imports from a standard library

Third-party imports

Imports of local scripts

Bad practice

Imports are not written in separate lines and are not organized correctly.

# pylint: disable=C0103
# pylint: disable=C0114

import pandas, matplotlib

import csv

Pylint output

Pylint notices that the modules pandas and matplotlib are not separated by lines. Furthermore, Pylint notices that the Python standard library module (csv) should come before the third-party imports (pandas and matplotlib). Pylint recognizes that all imports are unneeded, as shown below, because we did not add any further code for simplicity.

Good practice

Imports are put on distinct lines and sorted alphabetically.

# pylint: disable=C0103
# pylint: disable=C0114

import csv 

import pandas 

import matplotlib

10. Lambda expressions

These functions can take several parameters and return the value specified by the expression. Lambda functions are not just found in Python; they are also supported by other programming languages.

It is not encouraged by PEP8 standards to assign a lambda function directly to an identifier. It is best to utilize ordinary functions (defined with the def keyword) in this scenario.

Bad practice

A lambda expression is applied to an identifier directly (multiply).

# pylint: disable=C0103
# pylint: disable=C0114
# lambda functions
# define the function
multiply = lambda x, y: x * y 
# call the function 
multiply(5, 7)

Good Practice

Using the def keyword, we define the function.

# pylint: disable=C0103
# pylint: disable=C0114
# lambda functions
def multiply(x, y):
    """The function multiplies two numbers"""
    return x*y
# call the function 
multiply(5, 7)

Pylint is not perfect, but it may significantly enhance the quality of your code! Remember that code is read far more frequently than it is created, which is why it is critical to follow standards that will assist others in understanding your code. A clear code also demonstrates professionalism. That's why I recommend running your code via quality checkers like Pylint (there are lots more!). It will appear more clear and professional.


If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



whatsapp_icon
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message