pre-commit用法

1 背景:

当前的一些Python项目使用Make调用flake8工具来进行代码格式检查,即使检查出不符合规范的代码仍需要手动定位到指定文件进行格式化操作,且这些操作并不是强制执行,这就导致开发人员经常性的忘记检查代码规范就提交了一个commit,并且可以成功将不合规范的代码推到远程分支上,然后git commit log上充斥着大量的“fix: flake8”,“fix: 规范”等毫无意义的信息(题外话:包括一些接连几条重复的log,这可以通过git commit --amend来追加修改),看起来难受,也增大了blame的难度,浪费时间。
此外,Makefile中的flake8忽略了“F401’module imported but unused’”是不应该的,请只导入需要的包(其实这在PyCharm和VSCode中都会置灰提示)。
安装 pre-commit

2 使用pre-commit:

为了解决这些问题以及带来更多自动化,可以通过使用githooks提供的pre-commit 在每一次commit前自动进行代码检查和格式化,如果pre-commit返回了错误状态码,则该次提交将不能通过。幸运的是,当前流行的一个使用Python编写的同名工具 pre-commit 使得只需要在项目根目录下添加一个.pre-commit-config.yaml文件并写上相应的配置即可使用很多代码检查和格式化工具,实现自动代码检查和格式化。
对于Python项目,可使用的工具有:

  • trailing-whitespace:将一行代码末尾的空格移除(E225)
  • end-of-file-fixer:文件最后一行总是空行(W292)
  • flake8:Python代码风格规范检查
  • autopep8:格式化工具,根据PEP8对代码进行格式化
  • black:格式化工具,算是Python官方维护的项目,当下的流行选择,符合PEP8规范且更加严格,自称为不妥协的代码格式化工具
  • The uncompromising Python code formatter
    black格式化后的代码更加干净利落,层次分明。p.s. 字符串强制使用双引号。
  • isort:格式化工具,根据PEP8对import按照系统>第三方>本地的顺序分组排序
    除了上面列举的,对于Django项目甚至还可加入一个检测改动了ORM的model却没有执行python manage.py makemigrations的hook。
  1. 安装(可全局安装)
pip install pre-commit
  1. 之后在项目中设置hook脚本
pre-commit installs
  1. 写 .pre-commit-config.yaml配置文件
    一个供参考bmiss的.pre-commit-config.yaml如下:
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
# pre-commit==2.13.0
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.0.1
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer

  - repo: https://github.com/PyCQA/isort
    rev: 5.8.0
    hooks:
      - id: isort

  - repo: https://github.com/PyCQA/flake8
    rev: 3.9.2
    hooks:
      - id: flake8

  - repo: https://github.com/psf/black
    rev: 21.6b0
    hooks:
      - id: black


exclude: |
  (?x)^(
    etc|
    .*?/migrations|
    bmiss/settings/instance.*|
    .*?proto.*
  )

用pre-commit install安装git hooks到你的.git/目录
如果想要自定义一些配置的时候需要写.flake8的配置文件
ignore忽略
exclude除了这写文件夹之外

[flake8]
# E203 whitespace before ':'
# E266 Too many leading '#' for block comment
# E501,B950 Line too long
# W503 Line break occurred before a binary operator
# W504 line break after binary operator
# F405;403 Variable may be undefined; import * unable to detect undefined names
# C901 Function is too complex
# '# noqa' ignore in-line error
# '# flake8: noqa' ignore entire file
ignore = E203, E266, E402, E501, W503, W504, B950, F405, F403, C901
max-complexity = 50
select = B,C,E,F,W
exclude =
    migrations
    static
    taps_proto

如果你觉得没有必要强制要求不能定义变量而不使用(从输出可以看出这个规范的编号为F841),可以在项目根目录建一个 .flake8配置文件,如下图。更加详细的配置请看官方文档。
安装之后第一次使用会初始化python环境并根据repo指向从对应地址拉取所用到的工具,之后便可以正常使用。
示例:
某变动的文件代码为

import os
import sys
​
​
def function(very_long_variable_name, this, that):
    if very_long_variable_name is not None and \
            very_long_variable_name.field > 0 or \
            very_long_variable_name.is_debug:
        z = 'hello ' + 'world'
        return
    else:
        world = 'world'
        a = 'hello {}'.format(world)
        f = rf'hello {world}'
        if (this
                and that): f = 'hello ''world'# FIXME: https://github.com/python/black/issues/26
        return f

执行git commit -a -m “feat: Add a function function”
输出如下
(helloworld) ➜ [/Users/edy/projects/helloworld] git:(master) ✗ git commit -a -m “feat: Add a function function”
Trim Trailing Whitespace…Passed
Fix End of Files…Failed

  • hook id: end-of-file-fixer
  • exit code: 1
  • files were modified by this hook

    Fixing test-pre-commit.py

    isort…Passed
    flake8…Failed
  • hook id: flake8
  • exit code: 1

    test-pre-commit.py:1:1: F401 ‘os’ imported but unused
    test-pre-commit.py:2:1: F401 ‘sys’ imported but unused
    test-pre-commit.py:13:9: F841 local variable ‘a’ is assigned to but never used
    test-pre-commit.py:16:17: E127 continuation line over-indented for visual indent
    test-pre-commit.py:16:26: E701 multiple statements on one line (colon)
    test-pre-commit.py:16:47: E261 at least two spaces before inline comment

    black…Failed
  • hook id: black
  • files were modified by this hook

    reformatted test-pre-commit.py
    All done!
    1 file reformatted.
    代码提交失败,同时文件代码被格式化为
import os
import sys
​
​
def function(very_long_variable_name, this, that):
    if (
        very_long_variable_name is not None
        and very_long_variable_name.field > 0
        or very_long_variable_name.is_debug
    ):
        z = "hello " + "world"
        return z
    else:
        world = "world"
        a = "hello {}".format(world)
        f = rf"hello {world}"
        if this and that:
            f = "hello " "world"  # FIXME: https://github.com/python/black/issues/26
        return f

根据提示移除未使用的导入和变量后再此提交git commit即可成功。
这样,通过pre-commit减轻了因开发者个人导致的代码格式不规范继而影响多人协作开发的问题,所有人的代码格式都将由black来控制。

Black is the uncompromising Python code formatter. By using it, you
agree to cede control over minutiae of hand-formatting. In return,
Black gives you speed, determinism, and freedom from pycodestyle
nagging about formatting. You will save time and mental energy for
more important matters.

3 使用帮助

文件首行使用"# flake8: noqa"可以忽略整个文件的flake8检查,这对那些只供导入的__init__.py很有用,但不该滥用
行注释“# noqa”可以忽略该行flake8检查
.pre-commit-config.yaml文件中“exclude”参数可以排除检查的文件,“files”则是检查哪些文件

你可能感兴趣的:(Django问题总结,python,django)