Skip to content

Instantly share code, notes, and snippets.

@inesusvet
Created August 1, 2017 06:36
Show Gist options
  • Save inesusvet/afa7643c8bad34832a42dfc7eb0ee781 to your computer and use it in GitHub Desktop.
Save inesusvet/afa7643c8bad34832a42dfc7eb0ee781 to your computer and use it in GitHub Desktop.
Git pre-commit hook to check code style of changed files right at commit time
from __future__ import print_function
"""
Pre-commit hook for PEP8 styling
Works well with flake8==3.0.4
"""
__author__ = 'Ivan Styazhkin <[email protected]>'
import subprocess
import sys
CHANGES_CMD = 'git diff --name-only --cached --diff-filter=ACM'.split()
GIT_HASH_CMD = 'git ls-files --stage'.split()
GIT_FILE_CONTENT_CMD = 'git cat-file blob'.split()
# Presonal preferences for ignoring
IGNORED_ERRORS = (
'F403', # 'from models import *' used; unable to detect undefined names
'F405', # assert_that may be undefined, or defined from star imports: hamcrest
)
MAX_LINE_LENGTH = 100
FLAKES_CMD = [
'flake8',
'-',
'--ignore=%s' % ','.join(IGNORED_ERRORS),
'--max-line-length=%s' % MAX_LINE_LENGTH,
]
def get_lines(stdout_text):
"""Assumes your console uses utf-8"""
return stdout_text.strip().decode('utf-8').split('\n')
def get_python_changes():
"""Returns python filenames which are staged"""
python_changes = get_lines(subprocess.check_output(CHANGES_CMD))
return [s for s in python_changes if s.endswith('.py')]
def get_staged_file_content(filename):
"""Returns staged version of the file"""
hash_line = subprocess.check_output(GIT_HASH_CMD + [filename])
_, file_hash, _, _ = hash_line.split()
return subprocess.check_output(GIT_FILE_CONTENT_CMD + [file_hash])
def main(stream):
filename_list = get_python_changes()
if not filename_list:
return
error_list = list()
for filename in filename_list:
content = get_staged_file_content(filename)
pipe = subprocess.Popen(
FLAKES_CMD,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
)
pipe.stdin.write(content)
out, _ = pipe.communicate()
if out:
error_list.extend([
line.replace('stdin', filename)
for line in get_lines(out)
])
if not error_list:
print('\033[92mCheck flake8: OK\033[0m')
return
print('\033[91mCheck %d files for flake8: %d errors\033[0m' % (len(filename_list), len(error_list)))
for error in error_list:
print(error)
return 1
if __name__ == '__main__':
exit(main(sys.stdout))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment