本文主要介绍如何利用 Python 的 pre-commit 包定义 Git pre-commit 钩子,从而自动化代码质量检查的过程,并介绍了这项技术对数据科学家的作用。原文:Pre-Commit & Git Hooks: Automate High Code Quality
什么是预提交(Pre-Commit)?
Pre-commit 是一个 Python 软件包,能够帮助我们更容易创建预提交钩子(pre-commit hook)。钩子是 git 原生的东西,是在执行特定 git 命令前运行的脚本。
可以在仓库的 .git/hooks
目录中找到钩子,该目录由 git 自动创建。在这个目录中,可以找到类似下面这样的文件:
applypatch-msg.sample pre-commit.sample prepare-commit-msg.sample
commit-msg.sample pre-merge-commit.sample push-to-checkout.sample
fsmonitor-watchman.sample pre-push.sample update.sample
post-update.sample pre-rebase.sample
pre-applypatch.sample pre-receive.sample
复制代码
.sample
扩展名会阻止执行这些钩子。要启用钩子,请删除 .sample
扩展名并编辑文件。
不过这么做既繁琐又对用户不友好,而且很难通过版本控制进行管理,这就有 pre-commit 的用武之地了,它为 commit
命令创建钩子,可以自动检测代码中的任何问题,并使脚本的创建工作天衣无缝。
它会创建一个在调用 git commit
命令时自动执行的配置文件。配置文件中的任何检查失败,都会终止提交,从而始终确保代码库的质量和一致性。
有关 git 钩子的更多信息,请参阅 Git Hooks 官方文档。
数据科学为什么需要 pre-commit 钩子?
作为数据科学家,为什么也需要学习 git 钩子?现在社区中出现了一种趋势,认为精通软件工程越来越重要,而能够将自己的模型部署到生产中是非常有价值的事情。
而利用 git 钩子和 pre-commit 是确保稳健部署机器学习模型,同时保证代码质量的一种技术。机器学习模型比较繁琐,因此任何能让工作流程自动化并在模型投入生产前捕捉潜在错误的方法都是有价值的。
要了解有关全栈数据科学的更多信息,请查看:The predictable rise of full stack data science、The 4 hats of a full stack data scientist。
使用方法
安装
安装 pre-commit 非常简单,和通过 pip 安装其他 Python 库一样。我个人是用 Poetry,但都能正常工作。
pip install pre-commit # pip
poetry add pre-commit # I personally use poetry
复制代码
运行该程序即可确认已安装。
配置文件
进入代码仓库根目录,在项目中创建 .pre-commit-config.yaml
。运行以下命令即可完成创建:
touch .pre-commit-config.yaml
复制代码
该文件将定义在提交代码前要运行的内容。示例如下:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 22.10.0
hooks:
- id: black
- repo: https://github.com/PyCQA/flake8
rev: v6.0.0
hooks:
- id: flake8
additional_dependencies: [flake8-docstrings, flake8-import-order]
args: ['--max-line-length=88']
复制代码
这里 repos
表示包含要运行的钩子的仓库列表。第一个是包含钩子的 pre-commit-hooks
仓库:
id: check-yaml
id: end-of-file-fixer
id: trailing-whitespace
在这个例子里,id
是执行的特定预提交钩子的唯一标识符。
然后,我们对 black
和 flake8
代码包采用相同的流程。
执行
定义配置文件后,需要执行 pre-commit install
,让 pre-commit 知道你想使用指定的脚本在该仓库上执行预提交检查。
然后,修改代码并尝试提交,应该会看到预提交钩子开始工作。
也可以执行命令 pre-commit run --all-files
来查看 pre-commit 正在做什么。
如果想在不运行钩子的情况下提交代码,可以执行 git commit --no-verify
绕过钩子。
好了,我们举个例子!
示例
我在 Medium-Articles 仓库中安装了 pre-commit,用它来展示一个例子。
在仓库中,有以下 Makefile 文件
SHELL=/bin/bash
PHONY: install
install:
poetry install
PHONY: lint
lint:
poetry run black .
poetry run isort .
复制代码
其中定义了两个命令 install
和 lint
来设置环境,确保代码没有问题。
该仓库的 .pre-commit-config.yaml
如下所示,与之前展示的模板略有不同。
repos:
- repo: local
hooks:
- id: lint
name: lint
entry: make lint
language: python
types: [python]
stages: [commit]
复制代码
在本例中,钩子所在的仓库是本地的,即在我的项目中。该钩子将执行 entry
里定义的 make lint
命令,作为钩子的一部分,该命令已在 makefile 文件中定义。
仓库里有一个代码文件是这样的(relu.py
):
# Import packages
import numpy as np
import os
import plotly.express as px
# relu function
def relu(x):
return np.maximum(0, x)
# Generate data
x = np.linspace(-5, 5, 100)
y = relu(x)
# Graph
fig = px.line(x=x, y=y)
fig.update_layout(template="simple_white", font=dict(size=18), title_text="ReLU", width=650, title_x=0.5, height=400,)
if not os.path.exists("../images"):
os.mkdir("../images")
fig.write_image("../images/relu.png")
fig.show()
复制代码
我们尝试提交这段代码,看看会发生什么。
(medium-articles-py3.11) egorhowell@Egors-MBP Medium-Articles % git add .
(medium-articles-py3.11) egorhowell@Egors-MBP Medium-Articles % git commit -m "testing pre-commit"
[INFO] Initializing environment for local.
[INFO] Installing environment for local.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
lint.....................................................................Failed
- hook id: lint
- files were modified by this hook
poetry run black .
Skipping .ipynb files as Jupyter dependencies are not installed.
You can fix this by running ``pip install "black[jupyter]"``
reformatted /Users/egorhowell/Repos/Medium-Articles/Data Science Basics/relu.py
reformatted /Users/egorhowell/Repos/Medium-Articles/Time Series/Exponential Smoothing/holt_winters.py
All done! ✨ 🍰 ✨
2 files reformatted, 67 files left unchanged.
poetry run isort .
Fixing /Users/egorhowell/Repos/Medium-Articles/Time Series/Exponential Smoothing/holt_winters.py
Skipped 2 files
make: Nothing to be done for `Data Science Basics/relu.py'.
复制代码
钩子失败了,它重新格式化了 relu.py
。现在这个文件是这样的:
# Import packages
import os
import numpy as np
import plotly.express as px
# relu function
def relu(x):
return np.maximum(0, x)
# Generate data
x = np.linspace(-5, 5, 100)
y = relu(x)
# Graph
fig = px.line(x=x, y=y)
fig.update_layout(
template="simple_white",
font=dict(size=18),
title_text="ReLU",
width=650,
title_x=0.5,
height=400,
)
if not os.path.exists("../images"):
os.mkdir("../images")
fig.write_image("../images/relu.png")
fig.show()
复制代码
可以看到,预提交钩子成功运行了!
总结和进一步思考
Pre-commit 是一个实用的 Python 软件包,能改善 git 钩子的工作流程并简化脚本。它旨在通过自动化代码检查流程来保持软件的高质量并消除错误风险。作为数据科学家,越来越多参与到模型部署中,可以通过 git 钩子和 pre-commit 等技术来确保模型安全部署到生产环境。
你好,我是俞凡,在 Motorola 做过研发,现在在 Mavenir 做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI 等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。为了方便大家以后能第一时间看到文章,请朋友们关注公众号"DeepNoMind",并设个星标吧,如果能一键三连(转发、点赞、在看),则能给我带来更多的支持和动力,激励我持续写下去,和大家共同成长进步!
评论