PEP8 or GTFO

Like many people in our profession, I am a bit of a stickler when it comes to style and consistency. I like my code (and my team’s code) to have a specific style, and to stick to it, which is why I love tools like go fmt.

The Python equivalent is, of course, PEP 8. Although I don’t agree with everything (I like spaces but think tabs make more sense in theory, because they separate presentation and formatting), I observe PEP 8 because I think it is important to have one consistent style, no matter what it is.

Sometimes, however, code which doesn’t follow that style slips past my editor. In those cases, I would like my git server to reject my commits and tell me what’s wrong so I can fix it. Luckily, we can combine two great things to form an awesome thing: The flake8 script and git hooks.

Flake8 not only checks your code for PEP8 compliance, but it also checks it for errors, which helps you quickly catch many bugs before it’s too late. We can use the git pre-receive hook to decide whether we want to accept or reject a specific commit.

Writing a pre-receive hook is rather obscure and not very well documented, but, fortunately, I found a very nice hook that checks PHP code, and modified it to my needs. Here it is:

#!/bin/bash

COMMAND='flake8'
TEMPDIR=`mktemp -d`

while read oldrev newrev refname; do
    files=`git diff --name-only ${oldrev} ${newrev}`
    for file in ${files}; do
        object=`git ls-tree --full-name -r ${newrev} | egrep "(\s)${file}\$" | awk '{ print $3 }'`
        if [ -z ${object} ]; then continue; fi
        mkdir -p "${TEMPDIR}/`dirname ${file}`" &> /dev/null
        git cat-file blob ${object} > ${TEMPDIR}/${file}
    done;
done

# Change the filename here if your flake8 configuration
# has a different name.
git show HEAD:tox.ini > ${TEMPDIR}/tox.ini
${COMMAND} ${TEMPDIR}
STATUS=$?
rm -rf ${TEMPDIR} &> /dev/null
echo status $STATUS
exit ${STATUS}

You can paste the code above into the .git/hooks/pre-receive file, make it executable and try to push to that repo. The hook will:

  • Check which files changed in the last commit.
  • Extract them all into a temporary directory.
  • Extract the tox.ini file from the repository to that directory (feel free to change that line if your file is called something else, or remove it entirely).
  • Run flake8 on that directory.
  • Reject the commit if there are errors, printing them, or accept it otherwise.

That is, hopefully, pretty straightforward. The script can easily be adapted to any sort of tool or workflow, but be careful not to check every commit, because the only way for the user to pass the filter then would be to go back and change history.

Please let me know in the comments if you found this useful, or if you have any checking scripts of your own that you’d like to share.