使用git hook实现提交前自动检查Python代码语法

客户端项目钩子

git hook 可以实现在提交代码前执行代码检查等操作,下面实现在执行git commit时检查 Python 代码:

在项目的 .git/hooks/ 创建 pre-commit文件,写入如下内容:

#!/usr/bin/python
# coding=utf-8
""" 代码提交前实现自动语法检测 """

import os
import sys
import subprocess


def syntax_checker():
    """
        pyflakes 与 pep8 进行语法与代码风格检查。
        有严重语法错误时提交会被直接拒绝;
        如果有风格问题,会询问提交人员是否继续进行提交
    """
    errors = ''
    warning = ''
    info = ''
    pep8_info = ''

    # 获取有改动的文档
    staged_cmd = 'git diff --staged --name-only HEAD'
    proc = subprocess.Popen(staged_cmd, shell=True, stdout=subprocess.PIPE)
    with proc.stdout as std_out:
        for staged_file in std_out.readlines():
            staged_file = staged_file.strip()

            if not is_python_file(staged_file):
                continue

            stdout, stderr = process('pyflakes %s' % staged_file)
            warning += stdout
            errors += stderr

            # stdout, _ = process('pep8 %s' % staged_file)
            # pep8_info += stdout

            stdout, _ = process('cat %s' % staged_file)
            for i, line in enumerate(stdout.split('\n'), start=1):
                line = line.strip()
                if line.startswith('#'):
                    continue
                if 'print' in line:
                    info += "{}:{}: It seems you have commit test code: '{}'\n".format(
                        staged_file, i, line)
                if line.startswith('if __name__'):
                    break

    failed = False
    if is_error(errors) or is_warning(warning) or is_warning(
            pep8_info) or is_warning(info):
        failed = True
    return failed


def is_python_file(filename):
    if filename.endswith('.py') and os.path.exists(filename):
        return True

    return False


def process(cmd):
    """ 执行命令, 返回标准输出与错误信息 """

    proc = subprocess.Popen(
        cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    return proc.communicate()


def is_error(errors):
    """ 严重错误,如语法错误等 """
    failed = False

    if errors:
        failed = True

        print("\033[0;31mError:\033[0m You cann't commit, please fix the errors "
              "or run `pyflakes xxx.py` view details:"
              "\n-------------------------------------------")
        print errors

    return failed


def is_warning(warning):
    """ 代码规范建议信息 """
    failed = False

    if warning:
        print '\033[0;33mWarning:\033[0m', warning
        # 从终端输入
        sys.stdin = open('/dev/tty')
        input_row = raw_input("Encounter some non-standard syntax! "
                "Do you want to continue commit? [Y/n]: ")
        while True:
            # input_row = sys.stdin.readline().strip().lower()
            input_row = input_row.strip().lower()

            if input_row in ('y', 'yes'):
                failed = False
                break
            elif input_row in ('n', 'no'):
                failed = True
                break
            input_row = raw_input("Do you want to continue commit? [Y/n]: ")

    return failed


failed = syntax_checker()
sys.exit(1 if failed else 0)

全局钩子

如果想让钩子对所有的项目目录生效,只需要把pre-commit移到 ~/.git/hooks目录下(没有就创建一个),然后执行命令

git config --global core.hooksPath ~/.git/hooks

效果

代码有错误时直接禁止提交:

image-20200524194156483

代码语法不规范时,提示建议信息:

image-20200524194636731

 


关注微信公众账号「曹当家的」,订阅最新文章推送

Table of Contents