Skip to content

Instantly share code, notes, and snippets.

@lentil
Created February 3, 2011 22:55
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 23 You must be signed in to fork a gist
  • Save lentil/810399 to your computer and use it in GitHub Desktop.
Save lentil/810399 to your computer and use it in GitHub Desktop.
PEP8 pre-commit hook in Python
#!/usr/bin/env python
from __future__ import with_statement
import os
import re
import shutil
import subprocess
import sys
import tempfile
def system(*args, **kwargs):
kwargs.setdefault('stdout', subprocess.PIPE)
proc = subprocess.Popen(args, **kwargs)
out, err = proc.communicate()
return out
def main():
modified = re.compile('^[AM]+\s+(?P<name>.*\.py)', re.MULTILINE)
files = system('git', 'status', '--porcelain')
files = modified.findall(files)
tempdir = tempfile.mkdtemp()
for name in files:
filename = os.path.join(tempdir, name)
filepath = os.path.dirname(filename)
if not os.path.exists(filepath):
os.makedirs(filepath)
with file(filename, 'w') as f:
system('git', 'show', ':' + name, stdout=f)
output = system('pep8', '.', cwd=tempdir)
shutil.rmtree(tempdir)
if output:
print output,
sys.exit(1)
if __name__ == '__main__':
main()
@kgn
Copy link

kgn commented Jun 29, 2011

Thanks for this, it helped me get going with git hooks! After some digging and looking at other examples I found a cleaner way to get the modified and added files: https://gist.github.com/1055050

@lentil
Copy link
Author

lentil commented Jun 30, 2011 via email

@kevincal
Copy link

kevincal commented May 5, 2012

!/bin/sh

FILES=$(git diff --cached --name-status | grep -v ^D | awk '$1 $2 { print $2}' | grep -e .py$)
if [ -n "$FILES" ]; then
pep8 -r $FILES
fi

@cowlicks
Copy link

cowlicks commented Apr 1, 2014

#!/usr/bin/env bash

git diff --cached | pep8 --diff

@dmwelch
Copy link

dmwelch commented Aug 12, 2014

@cowlicks Short and sweet (+), but it doesn't handle new files. Combining @kevincal and your script would be optimal IMHO:

#!/usr/bin/env bash

FILES=($(git diff --cached --name-status | grep -v ^D | awk '$1 $2 { print $2}' | grep -e .py$))
for FILE in ${FILES[*]}; do
    pep8 --diff ${FILE}
done

@umbrae
Copy link

umbrae commented Apr 8, 2015

For anyone else that stumbles onto this, @cowlicks looks to have the better approach. pep8 --diff takes input from stdin, and would indeed catch new files because in order to be committed they would need to be git added to begin with, which means they would show up in git diff --cached. The pre-2014 comments were all before pep8 had diff support, I believe.

@noamhasingit
Copy link

Here is my adjustment to @cowlicks with the following changes:

  1. support windows gawk instead of awk
  2. replace pep8 with pycodestyle which is the new style replacing pep8
    Add this to your .git/hooks/pre-commit project:

FILES=($(git diff --cached --name-status | grep -v ^D | gawk '$1 $2 { print $2}' | grep -e .py$))
for FILE in ${FILES[*]}; do
pycodestyle ${FILE}
done

@peterjc
Copy link

peterjc commented Oct 24, 2018

A custom script still has its uses (e.g. if you need to run other linting checks outside of the Python style checks), but nowadays I typically use the command line tool flake8 (which incorporates the pep8 aka pycodestyle checks, and has a number of plugins available adding even more linting checks) with a configuration file as needed (e.g. setting the max-line-length), and its default git pre-commit hook, setup as follows:

$ flake8 --install-hook git
$ git config --bool flake8.strict true

See http://flake8.pycqa.org/en/latest/user/using-hooks.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment