使用 pre-commit 自动格式化 python 代码
为了使代码风格统一,我们一般希望项目的开发人员使用同一套规则进行代码格式化,在使用 git 做版本管理的项目里,可以使用 git hooks 在提交时对代码进行处理。
关于 git hooks 的具体介绍,可以查看: Git 钩子
pre-commit 钩子在键入提交信息前运行。 它用于检查即将提交的快照,例如,检查是否有所遗漏,确保测试运行,以及核查代码。 如果该钩子以非零值退出,Git 将放弃此次提交,不过你可以用 git commit –no-verify 来绕过这个环节。 你可以利用该钩子,来检查代码风格是否一致(运行类似 lint 的程序)、尾随空白字符是否存在(自带的钩子就是这么做的),或新方法的文档是否适当。
直接对 python 源文件进行格式化的工具有很多,black、autopep8、yapf 等等,以 black 为例,可以参考官方文档 Version control integration - Black Documention 进行配置。
有点遗憾的是,这些工具都没有提供“只格式化修改的部分”这个功能。
对于有一些历史代码的项目,我们希望格式化工具只对自己改动的部分生效,而不是整个文件。因为一些上千行的历史代码经过格式化之后会产生非常多的变动,不利于代码 review。
在 black 项目里就有人提出了这样的需求:[Feature Request] Allow command line option to only reformat specific lines,而且得到了很多人的支持。目前似乎 black 的维护者不想加入这个功能,于是后面有其它的开发者基于 black 创建了一个 darker 的项目。
darker 封装了多个格式化和 lint 工具,而且提供配置可以自由选择,它找出和上次 commit 有不同的文件部分,然后分别调用格式化工具,再对结果进行整合。
下面简要列出安装和使用的步骤,更多的用法可以参考这几个工具的文档:
- 安装 pre-commit:
python -m pip install pre-commit
- 新建
.pre-commit-config.yaml
文件,用于 pre-commit 的配置:
repos:
- repo: https://github.com/akaihola/darker
rev: 1.5.0
hooks:
- id: darker
entry: bash -c 'darker "$@"; git add -u' --
additional_dependencies: ['black', 'isort', 'flake8']
- 新建
pyproject.toml
,用于 darker 的配置:
[tool.darker]
revision = "HEAD"
diff = false
check = false
isort = true
lint = [
"flake8",
]
log_level = "INFO"
[tool.black]
line-length = 120
target-version = ['py36', 'py37', 'py38']
include = '\.py$'
skip-string-normalization = true
- 运行
pre-commit install
安装 hooks - 和正常使用 git 一样,只要上面安装流程成功了即可,hooks 会自动在 commit 之前运行。
默认情况下 pre-commit 修改了文件内容时,会导致 commit 流程终止,需要重新手动 add、commit,这个是 pre-commit 故意这么设计的。
为了避免这种麻烦的操作,上面第 2 步里的 entry 我改成了bash -c 'darker "$@"; git add -u' --
,每次格式化完之后重新 add。参考: