Vaisravana

Vaisravana

现代 Python 开发的“黄金搭档”

2025-06-20

我们来重点介绍一下 Ruff + pre-commit 这个堪称现代 Python 开发的“黄金搭档”。

这个组合的核心思想是:将代码规范化和质量检查完全自动化,并集成到你的 Git 工作流中,从而在代码被提交之前就确保其质量和风格的一致性。

第一部分:理解两位主角

1. Ruff:身兼数职的瑞士军刀 swiss-army-knife

我们已经知道 Ruff 是一个极速的代码检查(Linter)和格式化(Formatter)工具。在与 pre-commit 结合时,它的优势被发挥得淋漓尽致:

  • Linter 角色:它会检查你的代码是否存在潜在的 bug(如未使用的变量)、不规范的写法(违反 PEP 8)、以及可以改进的模式。它取代了 Flake8Pylint 等传统工具。

  • Formatter 角色:它会统一代码的格式,处理空格、换行、引号等风格问题。它旨在成为 Black 的一个更快、配置更集成的替代品。

  • Import Sorter 角色:它内置了对 import 语句的排序功能,完美取代了 isort

  • Auto-fixer 角色:最强大的是,Ruff 能自动修复它发现的绝大多数问题,无论是格式错误还是代码规范问题。

关键优势。因为它快,所以在每次提交代码时运行它,几乎感觉不到延迟,不会打断开发者的心流。

2. pre-commit:尽职尽责的代码仓库门卫 guard

pre-commit 是一个 Git 钩子(hook)管理框架。所谓“钩子”,就是在 Git 执行特定操作(如 commitpush之前之后自动触发的脚本。

pre-commit 让你能够轻松地管理这些钩子。它的工作流程是:

  1. 你在项目中配置一个 .pre-commit-config.yaml 文件,定义好要运行哪些检查工具(比如 Ruff)。

  2. 当你运行 git commit 时,pre-commit 会拦截这次提交。

  3. 它会针对你即将提交的文件(staged files)运行你定义好的工具。

  4. 如果工具检查不通过

    • 如果工具(如 Ruff)能自动修复,它会直接修改你的文件。然后 pre-commit中止本次提交,并提示你检查修改。你只需再次 git add 被修改的文件,然后重新 commit 即可。

    • 如果发现无法自动修复的严重问题,它会直接报错并中止提交,直到你手动修复问题。

  5. 如果所有工具检查都通过,你的 commit 才会成功创建。

关键优势自动化和强制性。它确保了没有任何不符合规范的代码能“溜进”你的代码历史记录中,对团队协作尤其重要。


第二部分:实战:如何配置 Ruff + pre-commit

假设你已经有一个 Git 项目,我们来一步步完成配置。

第 1 步:安装工具

在你的项目虚拟环境中安装 Ruffpre-commit

# 激活你的虚拟环境
# source .venv/bin/activate

pip install ruff pre-commit

第 2 步:配置 Ruff

Ruff 的配置通常写在项目根目录的 pyproject.toml 文件中(这是现代 Python 项目推荐的做法),或者一个独立的 ruff.toml 文件。

在你的 pyproject.toml 文件中添加以下配置:

# pyproject.toml

[tool.ruff]
# 设置代码行的最大长度,与 black 默认值保持一致
line-length = 88
# 允许 Ruff 自动修复所有安全的问题
fix = true

[tool.ruff.lint]
# 选择要启用的规则集。这组规则非常常用:
# E, F: 来自 Flake8 的核心规则 (pycodestyle, Pyflakes)
# I: import 排序 (来自 isort)
# UP: pyupgrade, 自动将代码升级到新语法
# B: flake8-bugbear, 发现潜在的逻辑错误
select = ["E", "F", "I", "UP", "B"]

# 如果有需要,可以忽略特定的规则
# ignore = ["E501"] # 例如,如果不想限制行长度

[tool.ruff.format]
# 使用双引号,而不是单引号
quote-style = "double"

提示Ruff 的规则非常多,你可以从一个基础的配置开始,然后根据团队的需求逐步增减。select = ["E", "F", "I"] 是一个很好的起点。

第 3 步:配置 pre-commit

在你的项目根目录创建一个名为 .pre-commit-config.yaml 的文件,并写入以下内容:

# .pre-commit-config.yaml

repos:
-   repo: https://github.com/astral-sh/ruff-pre-commit
    # 确保使用一个固定的版本号,以保证团队成员之间的一致性
    rev: v0.4.8 # 你可以在 Ruff 官网或 GitHub 找到最新版本号
    hooks:
        # 第一个钩子:使用 Ruff 作为 Linter (代码检查)
        -   id: ruff
            # --fix 参数让它在运行时自动修复问题
            # --exit-non-zero-on-fix 表示如果进行了修复,也算作失败,从而中止提交
            args: [--fix, --exit-non-zero-on-fix]

        # 第二个钩子:使用 Ruff 作为 Formatter (代码格式化)
        -   id: ruff-format

第 4 步:激活 pre-commit 钩子

在你的项目根目录(与 .git 目录同级)运行以下命令:

pre-commit install

这个命令会将 .pre-commit-config.yaml 中定义的钩子安装到你的本地 Git 仓库的 .git/hooks 目录中。每个克隆了该仓库的开发者都需要运行一次此命令来激活钩子


第三部分:见证奇迹的时刻 (工作流演示)

现在,你已经配置完毕。让我们看看它在实际工作中是如何运作的:

  1. 你写了一段不那么规范的代码,比如:

    # my_script.py
    import os, sys # 同行导入,不规范
    
    def my_func( name): # 参数前后缺少空格
        unused_variable = 1 # 未使用的变量
        print("hello " + name) # 使用 + 拼接字符串,可以更优
    
  2. 你将这个文件添加到暂存区并尝试提交:

    git add my_script.py
    git commit -m "Add a new feature"
    
  3. pre-commit 会立即启动,并运行 Ruff

    (base) ➜ my-project git commit -m "Add a new feature"
    Ruff: linter.............................................................Failed
    - hook id: ruff
    - files were modified by this hook
    
    Found 4 errors (4 fixable).
    - my_script.py:1:10: I001 [*] Import statements are not sorted
    - my_script.py:3:15: E231 Missing whitespace after ','
    - my_script.py:4:5: F841 [*] Local variable `unused_variable` is assigned to but never used
    - my_script.py:5:5: B034 [*] `print("hello " + name)` can be rewritten as a f-string
    [*] Found 4 fixable violations.
    
    Ruff: formatter..........................................................Failed
    - hook id: ruff-format
    - files were modified by this hook
    
    1 file reformatted
    
  4. 提交被中止了! pre-commit 告诉你自己动手修改了文件。现在你查看 my_script.py,会发现它已经被 Ruff 自动修复和格式化了:

    # my_script.py (自动修复后)
    import os
    import sys
    
    def my_func(name):
        # 注意:像 "unused_variable" 这样的逻辑问题,Ruff 默认不会自动删除,
        # 它会警告你,需要你手动处理。但格式和导入问题已经修复。
        unused_variable = 1
        print(f"hello {name}")
    

    注意:对于 "unused variable",Ruff 的 --fix 通常会移除它,这里为了演示保留。但关键是,它会指出问题。

  5. 你看到代码已经被修正,于是你接纳这些修改,然后再次提交:

    # 再次添加被自动修改的文件
    git add my_script.py
    
    # 再次提交
    git commit -m "Add a new feature"
    
  6. 这一次,pre-commit 再次运行 Ruff,发现所有问题都已解决,代码完全符合规范。

    (base) ➜ my-project git commit -m "Add a new feature"
    Ruff: linter.............................................................Passed
    Ruff: formatter..........................................................Passed
    [main a1b2c3d] Add a new feature
     1 file changed, 5 insertions(+), 5 deletions(-)
    

提交成功!

总结

Ruff + pre-commit 的组合,为你和你的团队提供了一个强大、快速、自动化的代码质量保障体系。

  • 对个人开发者:它强迫你养成良好的编码习惯,保持代码库的整洁。

  • 对团队:它解决了代码风格不一的“圣战”,统一了规范,降低了 Code Review 中因风格问题产生的沟通成本,让大家更专注于业务逻辑本身。

这套流程一旦建立,就会成为项目开发中一个默默无闻但不可或缺的守护者。强烈建议在所有新的 Python 项目中都采用它。

来源