#!/usr/bin/env python
"""
A pre-commit hook for git that uses Pylint for automated code review.

Passes code with pylint score of 9/10 or better.

This script must be located at $REPO/.git/hooks/pre-commit and be
executable.

Pylint will use $REPO/.git/hooks/pylintrc.

This scirpt is based on:
    http://fitzgeraldnick.com/weblog/9/
"""

# Python imports
from subprocess import Popen, PIPE
import sys
import os
import re

# Threshold to pass Pylint test.
PYLINT_PASS_THRESHOLD = 9

def is_py_script(filename):
    """Returns True if a file is a python executable."""
    
    if not os.access(filename, os.X_OK):
        return False
    elif os.path.isfile(filename):
        if filename.endswith(".py"):
            return True
        with open(filename) as f:
            first_line = f.readline()
            if first_line.startswith("#!") and "python" in first_line:
                return True
    else:
        return False
def main():
    """Checks your git commit with Pylint!"""

    #
    # Path to pylintrc
    #
    pylintrc = os.path.join(os.getcwd(), ".git", "hooks", "pylintrc")
    #
    # if pylintrc not present use default:
    #
    if os.path.isfile(pylintrc):
        syntax_checker = "pylint --rcfile=%s -f text " % pylintrc
    else:
        syntax_checker = "pylint -f text "        
    #
    # Gets filename of every locally modified file.
    #
    sub = Popen("git diff-index --name-only HEAD".split(), stdout=PIPE)   
    #sub = Popen("git diff --staged --name-only HEAD".split(), stdout=PIPE)
    #
    # Read the list of files 
    #
    diff_stdout = sub.communicate()[0]
    #
    # Filter out non-python or deleted files.
    #
    py_files_changed = [ file
                        for file in [ f.strip()
                                      for f in diff_stdout.splitlines()
                                    ]
                        if is_py_script(file)
                       ]

    #
    # Pylint each file, collect the results, and display.
    #
    results = {}
    for file in py_files_changed:
        print "syntax_checker:%s, file:%s" % (syntax_checker, file)
        pylint = Popen( ("%s %s" % (syntax_checker, file)).split(), stdout=PIPE)
        output = pylint.communicate()[0]
        print "%s" % output
        #
        # Record the rating
        #
        results_re = re.compile(r"Your code has been rated at ([\d\.]+)/10")
        results[file] = float(results_re.findall(output)[0])
    #
    # Summary of the results (if any files were checked).
    #
    if len(results.values()) > 0:
        print "==============================================="
        print "Final Results:"
        for file in results:
            result = results[file]
            grade = "FAIL" if result < PYLINT_PASS_THRESHOLD else "PASS"
            print "[ %s ] %s: %.2f/10" % (grade, file, result)
    #
    # If any of the files failed the Pylint test, exit nonzero and stop the
    # commit from continuing.
    #
    if any([(result < PYLINT_PASS_THRESHOLD)
            for result in results.values()]):
        print "git: fatal: commit failed, Pylint tests failing."
        sys.exit(1)
    else:
        #I'd like to add a final choice to the user,
        #but this would make the hook platfrom specific
        #sys.stdin = open('/dev/tty');
        #do_we_continue = raw_input("Do you wish to commit? (y/n):  ")
        #if do_we_continue == 'y':
        sys.exit(0)
        #else:
        #    sys.exit(1)

if __name__ == "__main__":
    main() 
