Best pre-commit hooks setup for your Python project
Have you ever pushed your code or created a PR and then you realized that you forgot to remove that print statement or that your code formatting is messed up or even better you forgot to remove those unused imports?
If all of the above problems you face every day then you don't need to worry anymore, pre-commit
hooks are here for the rescue.
So now the question is what is a pre-commit
hook in the first place? A pre-commit hook is a bunch of scripts that reside in your .git
directory & is run before every git commit. It is in the name only...
It will not allow you to commit your code if any of the scripts throws an error.
So basically while writing code you need to do some repeated & housekeeping work before you push your changes. We as developers don't like any manual work, right? So git
provides you with a feature called hooks. These are scripts that run automatically every time a particular event occurs in a Git repository.
The most important event we are interested in is the pre-commit
event which occurs before committing your code. There is actually a framework created that can help you set up these pre-commit hooks called pre-commit.
This is a Python package & can be installed using the pip
or homebrew
if you are using a MacBook.
Using pip
pip install pre-commit
Once you have installed this package you have to install hook scripts using this command.
pre-commit install
This command will modify your .git
directory to insert some scripts related to hooks. This pre-commit
package comes with some scripts or hooks which you use out of the box (It will download those scripts for you actually).
Now you need to create a configuration file that tells pre-commit
to what actions to perform before committing your code. This file is called .pre-commit-config.yaml
and should be present at the root of your project folder.
As you guessed it by the .yaml
extension of the file, all the configurations are done using YAML way.
After you have created this file, now add these lines to that.
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- repo: https://github.com/psf/black
rev: 21.12b0
hooks:
- id: black
Let's understand the structure of it bit by bit. At the top, you can see a key called repos
. It is the list of repositories or hook scripts you want to use. Inside this, you will have to list down of URLs of the repositories from which you need to download all the scripts for your pre-commit hooks.
To specify a list of scripts you need to use -
with key repo
and colon & the actual URL pointing to your GitHub repo. The rev
is the revision or tag to clone at. The hooks
are a list of hook mappings. Generally in your git repo, you can have multiple scripts & the hooks
tag specifies that you only want to use some specific scripts.
Now you understand the structure of the configuration file. Now you look at the file you will see that we are using two different repos here. The first one is the official set of hooks maintained by the team behind the package pre-commit
. To know more about those specific hooks you can go to the repo link & read their README.md
file. It does a pretty good job to explain you all the hooks available there.
The second repo is for the black
package. It is a very famous Python package to format your python code using the PEP8 style guide.
After this every time you try to commit your code, these scripts will run. If there is any issue in your code then you will start seeing some error messages on your console pointing to the filename & line number. If everything is okay then something like this.
Fix End of Files..............................................Passed
Trim Trailing Whitespace..............................Passed
black......................................................Passed
One thing to note is that these scripts will be only run against the files under commit. If you want to run it for all the files then use this command,
pre-commit run --all-files
Here are the pre-commit hooks which I use personally in my projects which have done a pretty good job for me & fitted my workflow.
isort
for sorting imports.
It basically sorts your import statements alphabetically & separates them into a group.
hooks:
- id: black
exclude: \.py-tpl$
- repo: https://github.com/PyCQA/isort
flake8
for linting.
flake8
has become pretty much a standard for Python linting. It just basically scans your Python code and finds which line of code does not adhere to Python's style guide. It enforces the PEP8 style guide on you.
repo: https://github.com/PyCQA/flake8
rev: 5.0.0
hooks:
- id: flake8
additional_dependencies: [flake8-print, pep8-naming, flake8-bugbear]
As you can see I have an additional package with flake8
.
flake8-print
- Checks forprint
statement in your code.pep8-naming
- Check your code against PEP 8 naming conventions.flake7-bugbear
- finding likely bugs and design problems in your program. Contains warnings that don’t belong inpyflakes
andpycodestyle
. Check the docs here
pycln
for removing unused imports.
- repo: https://github.com/hadialqattan/pycln
rev: v0.0.1-beta.3
hooks:
- id: pycln
This is how my configuration file looks like,
exclude: (migrations)
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: check-ast
- id: check-case-conflict
- id: check-docstring-first
- id: check-executables-have-shebangs
- id: check-json
- id: pretty-format-json
- id: debug-statements
- id: detect-private-key
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 22.6.0
hooks:
- id: black
- repo: https://github.com/PyCQA/isort
rev: 5.10.1
hooks:
- id: isort
- repo: https://github.com/PyCQA/flake8
rev: 5.0.0
hooks:
- id: flake8
additional_dependencies: [flake8-print, pep8-naming, flake8-bugbear]
- repo: https://github.com/hadialqattan/pycln
rev: v0.0.1-beta.3
hooks:
- id: pycln
These hooks will help you write & ship better code. There are plenty of hooks available out there. You can find any specific hook for your specific requirement or you write one for yourself.
Hope you learned something new today...