Sunday, September 15, 2013

Pre-commit hook - PEP8 version


A couple of days ago I've refactored and enhanced my pre-commit hook to check also for PEP8 compliancy of Python source files. To achieve this I used the pep8 tool that you can easily install with:

pip install pep8

And here's the new pre-commit hook:

#!/bin/bash
#
# TAB CHECK:
#
# Searches tab characters in staged changes. 
# To specify file extensions to check use "hooks.notabs" variable.
# Extensions are separated by "|".
# Eg:
#   git config hooks.notabs html|js
#
# PEP8 CHECK:
#
# run pep8 on all python staged files.
# To specify pep8 command options use "hook.pep8options" variable
#
# Eg:
#   git config hook.pep8 "--ignore=E226,E302"

exec 1>&2

notabs=$(git config hooks.notabs)
notabs_error=0
for f in `git diff --cached --name-only | egrep "\.("$notabs")$"`
do
    lines=`git show :$f | fgrep -n $'\t'`
    if [ -n "$lines" ]; then
        echo "TAB(s) found in:" $f "at line(s)"
        echo $lines
        notabs_error=1
    fi
done

if [ $notabs_error -eq 1 ]; then
    echo 
    echo "Remove all TAB(s) characters and stage again your changes."
    echo
    exit 1
fi

pep8_options=$(git config hooks.pep8options)
pep8_error=0
for f in `git diff --cached --name-only | egrep ".py$"` 
do
    git show :$f | pep8 --format=$f":%(row)d:%(col)d: %(code)s %(text)s" $pep8_options -
    if [ $? -eq 1 ]; then
        pep8_error=1
    fi
done

if [ $pep8_error -eq 1 ]; then
    echo 
    echo "Your commit is cause of one or more PEP8 error(s)"
    echo
    echo "Please fix these errors and stage again your changes"
    echo
    exit 1
fi

This new version is quite straightforward so I'm not digging too much into it.
The two main changes are:
  • Discover of command git show :)
  • Add pep8 style test
About the latter point, it's important to say that the pep8 has becomes one of the requirements to use this hook. Also, you can configure your pep8 options by typing:

git config --add hooks.pep8options "--ignore=E121,E122" # this ignores Error 121, 122

A complete list of errors and warnings of pep8 tool is available here: http://pep8.readthedocs.org/en/latest/intro.html#error-codes

PS.

I'm using this hook just from a couple of days, so it may (probably) have some bugs... feel free to contribute at https://github.com/cybercase/funproject/blob/master/experiments/pre-commit ;)

Tuesday, August 13, 2013

Pre-commit hook

The project in which I'm working has gained some young people recently.

For a couple of them it's even their first working experience so I'm really
trying to transmit them the importance of conventions and an uniform coding style.

It's usual (and perfectly normal) to find some coding style mistakes in their commits.
So, to help us happily code all together, I created a small pre-commit hook
to put in our repositories.

Right now it's just a simple no-tabs check, but with some small changes it could
check for use of deprecated functions or some forgotten import pdb; pdb.set_trace() :)
# Files extensions in hooks.notabs are separated by "|"
# Eg:
#   git config hooks.notabs py|js

exec 1>&2
notabs=$(git config hooks.notabs)
toplevel=$(echo "`git rev-parse --show-toplevel`/" | sed -e 's/[\/&]/\\&/g')
if [ -n "$notabs" ] && 
    git diff --cached -- not_a_file `git diff --cached --name-only |
      egrep '\.('$notabs')$' |
      sed 's/^/'$toplevel'/g' | xargs` |
      egrep -qn $'^\+\.*\t'
then
    echo "Error: Attempt to add TABS"
    echo 
    echo "Do you really want to break our beautiful coding conventions? :("
    echo
    echo "Remove all tabs characters and stage again your changes."
    echo
    echo "To override this hook use:"
    echo
    echo "  git commit --no-verify"
    echo
    exit 1
fi


To have it working:

  • copy this script into repository_dir/.git/hooks/pre-commit.
  • Add some file extensions to hooks.notabs variable (these must be separated by |).
  • Done!

Let's look at each parts in rigorous order of execution
  • notabs=$(git config hooks.notabs): retrieves from local config the files extensions to check
  • git diff --cached --name-only: lists names of files that have staged changes
  • egrep '\.('$notabs')$': removes files having different extensions from the ones in notabs
  • sed 's/^/'$toplevel'/g': prepend the absoulute path to each filename
  • xargs: creates a list of arguments using previous file names list 
  • git diff --cached -- not_a_file: returns staged changes for files coming from "xargs". Note that "not_a_file" is an unexistant file, specified in case that the output of xargs is empty. In fact if no files are specified the "git diff --cached" command would return staged changes for every file.
  • egrep -qn '^\+\.*\t': searches for all the lines starting with "+" and containing a tab

Note that this pre-commit hook can be easily bypassed using.

git commit --no-verify

This reflects exactly the same thought I want communicate to my collegues. Everyone should
struggle to pass the pre-commit hook checks, but no one should be forced to.

References:
[1] Customizing-Git-Git-Hooks: http://git-scm.com/book/en/Customizing-Git-Git-Hooks



Wednesday, May 22, 2013

Python 3 Metaprogramming

How can you import XML files into Python 3?
And why you would?

This blogpost doesn't answer this last question, but it's definetly going to answer the first one :)

Everything I'm writing is taken from this talk from David Beazley: youtube video.
Consider this post as a very little summary of its contents.

Let's start with the requirements:
Python 3.3
... that's all!