Git Pre-Commit Python Syntax

All of us programmers have made this mistake before: we’ve submitted code that we thought was correct only to have it fail during compilation because of a simple syntax error. Just between you and me (and the rest of the interwebs), I did this last week. Then I thought to myself that it would be good to come up with a tool to help ensure it didn’t happen again. I’m proud to say that on the Git front, I have fixed my problem.

Enter Git Hooks, a way to add scripts to various parts of the Git workflow. Using what is called the pre-commit hook, I was able to write up a quick bash script that goes through all the changed files in Git staging area, check if they are Python files (based on the .py file extension), and then run those Python files through Pylint to look for various errors, including syntax and type errors.

So here is my git hook:

  1. #!/bin/bash
  2.  
  3. files_modified=`git diff-index --name-only HEAD`
  4.  
  5. for f in $files_modified; do
  6. if [[ $f == *.py ]]; then
  7. pylint -E $f
  8. if [ $? != 0 ]; then
  9. echo "Code fails pylint check."
  10. exit 1
  11. fi
  12. fi
  13. done
  14. exit

Feel free to copy this and modify it for your own needs. My gift to you; no charge. But an occasional “Thank you Bryce” email is always nice. I also accept lavish gifts of cash or ThinkGeek merchandise.

One quick thing before I let you get back to your busy lives. If you have an older version of Pylint than 0.24, the “-E” flag might need to be changed to “-e”.

pylint 0.23 error

Thanks for the script!

There is a bug in 0.23. so with the change -E to -e I had the message:

pylint.utils.UnknownMessage: No such message id BEAD/PRUFER.PY
Code fails pylint check.

So if you have message like this, install pylint e.g. with pip.

It is important to be in the root directory of the git repository, if you run this bash script.

Arpad, Thank for those

Arpad,

Thank for those contributions. I'm not sure it's a bug so much as a configuration change. On my ubutu 10.10 I had to make that change, but for my OSX machine I was able to install the newer version and that's why I published with the capital "E" flag.

I will try to look into a modification to the script to see about having it work in a subdirectory of the git repo. I'm not sure how "vital" that is, but I can see it being an annoyance to have to go back to the top of the git repo in order for it to function.

Hi, just stumbled over this

Hi,
just stumbled over this when searching for something else, we have a very similar script using pyflakes for the development of Unknown Horizons. It also checks for newly added "print" lines, as these are often just for debugging. If errors are detected a tkinter popup is shown that lets you decide if you want to cancel the commit, console IO is not possible in hooks, therefore this gui version is used.

Implemented in python, newest version always here:
https://github.com/unknown-horizons/unknown-horizons/blob/master/development/pre-commit

Cheers!

Hey Nihathrael, Thanks for

Hey Nihathrael,

Thanks for the post and the link. That is one extremely impressive pre-commit script.

sh or bash?

Maybe it's only me, but had to change #!/bin/sh to #!/bin/bash on my debian squeeze to get it working.

I got this error before change:

.git/hooks/pre-commit: 13: [[: not found

I'm sorry to hear that the

I'm sorry to hear that the script didn't work for you Fizyk. I know it's not much of a consolation, but it seems to work just fine on my OSX box. I wonder if it might be some different versions of /bin/sh. For my machine I have version 3.2.8. Is your version different?

Change for portability

Note that you are using bash-specific shell syntax, so you really should change the snippet to begin '#!/bin/bash' - which will also work fine on Mac OS X.

It just so happens that Mac OS X uses bash as the default shell, so /bin/sh will get you a bash interpreter, but other Unices (such as Debian) use a different default shell.

Basically this works for you only by happy coincidence, technically what you've done is incorrect (or at least not portable).

Great script otherwise though - thanks.

Matt is correct. /bin/sh on

Matt is correct. /bin/sh on MacOSX is it's own executable. That is why I was able to get the version number when trying to help Fizyk with his/her problem. I know on various linux boxes that /bin/sh is a sym link to /bin/bash and that could be the root of Fizyk's problem.

On recent Debians /bin/sh

On recent Debians /bin/sh isn't bash at all, it's ash - a lightweight Bourne compatible shell which doesn't support bash extensions. That's why the script as written won't work on Debian, whereas if you said #!/bin/bash it would.

Basically if you are using bash-specific shell extensions it is good practice to specify this by explicitly invoking bash in the header (and arguably it is incorrect to do otherwise).

Sean, I've taken your advise

Sean, I've taken your advise an updated the script to use /bin/bash instead of /bin/sh. Thank you for taking the time to explain why the /bin/sh won't work for Debian machines.

Great. For the record the

Great. For the record the default shell on recent Debians is called 'dash' not 'ash' - I misremembered, but otherwise it's as I described. Thanks again for the scriptlet, a useful addition to my repos.

Reading this info So i am

Reading this info So i am happy to convey that I've a very good uncanny feeling I discovered just what I needed. I most certainly will make sure to don’t forget this web site and give it a glance on a constant basis.

no, /bin/sh is the executable

no, /bin/sh is the executable your shell will use to execute the script. If you're using bash as your shell, it needs to be /bin/bash

Maybe pyflakes instead of pylint?

Pyflakes is a lot faster than pylint - as as you are only checking for the existence of errors - then it probably has all the functionality that you need at this point. (Pylint does a lot more - so for example if you wanted to enforce style guidelines - you would need pylint)

Hey Mark, thank you for the

Hey Mark, thank you for the recommendation for Pyflakes. I didn't know about it till you said something and I will definitely look into that project deeper.

Why use bash and not python?

Why use bash and not python?

Because it would be more

Because it would be more complex and crappy in Python, using subprocess pipes, string parsing and so on. it's much simpler and much more straightforward in shell script.

To be honest I didn't think

To be honest I didn't think about trying to put this in python. It's certainly possible to do and it would possibly be a good second part to this post. Thank you for the idea.

But what if the check script

But what if the check script contains syntax errors? ;-)

If you made it this far down into the article, hopefully you liked it enough to share it with your friends. Thanks if you do, I appreciate it.

Bookmark and Share