Keep your code clean using Black & Pylint & Git Hooks & Pre-commit | by Ismail Mebsout | Towards Data Science

Coding can be a very hard task especially when working on a project with different developers. Each member of the team has his/her own way of coding leading to very heterogeneous scripts. This is why it is important to have a similar code formatter and code linter in order to make your git commits cleaner. This can be carried out either between the staging and committing phase or during the CI/CD chain.
In this article, we will see how to do so as a pre-commit step using git hooks.
The summary is as follows:
BlackPylintPre-commits as Git Hooks
Black
Pylint
Pre-commits as Git Hooks
Black is a python code formatter which style configurations are deliberately limited. It makes your code cleaner so you can focus more on the content. The code review is also more efficient since the diffs are as minor as possible.
It can be installed using the following command line:
pip install black
You can run black on any python file by typing:
Black pathtofile.py
Black can be slightly configured using the file pyproject.toml which should be placed in the root of your project. Below an example of this file:
[tool.black]line-length = 88target-version = [‘py36’, ‘py37’, ‘py38’]include = ‘\.pyi?$’exclude = '''/(  \.toml  |\.sh  |\.git  |\.ini  |Dockerfile  |Jenkinfile)/'''
We can choose the length of the lines of codes for example and also set the extensions which files should not be formatted.
Pylint is a “Python static code analysis tool” which evaluates the quality of the developed scripts. It guides the team into adopting the same standards.
Pylint can be installed using the following command line:
pip install pylint
To evaluate your coding quality on a given script you can run:
pylint pathtofile.py
NB: In order to run pylint on your entire project, your repository should include an __init__.py file.
Like Black, Pylint can also be configured using the file .pylintrc which is also placed on the root of your project
[MASTER]jobs=4 #number of processes to use[BASIC]good-names=nameOfYourProject #names to be considered ok[pre-commit-hook]command=custom_pylintdisable=E0401, C0301
You can check this page for more details on the configuration.
Git hooks are defined scripts that are launched when a git action is run.There are two types of hooks:
Client-side hooks: are run after committing and merging
Server-side hooks: are run on network operations, after pushing commits for example
In this article we will focus on client-side ones which workflow can be described by the following graph:
In a git repository, hooks are placed in .git/hooks/ . They can be visualized in VSCode for example by adding the following lines to your Json settings:
"files.exclude": {     "**/.git": false}
To add a pre-commit action to your repository:
1.Create the file pre-commit in the folder .git/hooks/. You should not include any extension in its name.
2.Add bash commands in your file. In our case:
#!/bin/shblack .python lint.py -p ../projectName/
The first line applies black formatter and the second line applies the linting on every python file of your project. You can download the file lint.py from this link:
import argparseimport loggingfrom pylint.lint import Runlogging.getLogger().setLevel(logging.INFO)parser = argparse.ArgumentParser(prog="LINT")parser.add_argument('-p',                    '--path',                    help='path to directory you want to run pylint | '                         'Default: %(default)s | '                         'Type: %(type)s ',                    default='./src',                    type=str)parser.add_argument('-t',                    '--threshold',                    help='score threshold to fail pylint runner | '                         'Default: %(default)s | '                         'Type: %(type)s ',                    default=7,                    type=float)args = parser.parse_args()path = str(args.path)threshold = float(args.threshold)logging.info('PyLint Starting | '             'Path: {} | '             'Threshold: {} '.format(path, threshold))results = Run([path], do_exit=False)final_score = results.linter.stats['global_note']if final_score < threshold:    message = ('PyLint Failed | '               'Score: {} | '               'Threshold: {} '.format(final_score, threshold))    logging.error(message)    raise Exception(message)else:    message = ('PyLint Passed | '               'Score: {} | '               'Threshold: {} '.format(final_score, threshold))    logging.info(message)    exit(0)
NB: A threshold of 7 is set by default. If the pylint score is below this number, the commit will fail and you will have to clean your script before commit again.
3. Make it executable by running the following bash command:
chmod +x .git/hooks/pre-commit
With that being done, each commit of your project will be preceded by the formatting of black and linting of pylint.We consider the following app for illustration:
When committing a new change, we get the following results:
Now that you have set up black as your formatter and pylint as your linter, you definitely want your team to have the same configuration. To do so, you can use the following bash file which will be placed at the root of your project and run by each member of your team after cloning the repository:
#!/bin/bashecho $"#/bin/sh\nblack .\npython lint.py -p ../projectName/'> .git/hooks/pre-commitchmod +x .git/hooks/pre-commit
Pre-commits are very powerful tools to keep your code well-formatted and to standardize the developing methods across the member of your team. Similarly, they can include other checks besides linting and formatting. In this article, we have used a classical method that calls installed Python libraries, you can also use the python module pre-commit which includes many checks and calls on their GitHub repositories. It is very efficient but can be difficult to set up especially when facing proxy filters.