跳到内容

教程

本教程将引导你完成将 Ruff 的代码检查器(Linter)和格式化工具(Formatter)集成到项目中的过程。如需更详细的概述,请参阅 配置 Ruff

入门指南

首先,我们将使用 uv 初始化一个项目

$ uv init --lib numbers

此命令将创建一个具有以下结构的 Python 项目

numbers
  ├── README.md
  ├── pyproject.toml
  └── src
      └── numbers
          ├── __init__.py
          └── py.typed

接下来,我们将清空 src/numbers/__init__.py 中的自动生成内容,并创建 src/numbers/calculate.py,代码如下

from typing import Iterable

import os


def sum_even_numbers(numbers: Iterable[int]) -> int:
    """Given an iterable of integers, return the sum of all even numbers in the iterable."""
    return sum(
        num for num in numbers
        if num % 2 == 0
    )

然后,我们将 Ruff 添加到项目中

$ uv add --dev ruff

接着,我们可以通过 uv run ruff check 在项目中运行 Ruff 代码检查器

$ uv run ruff check
src/numbers/calculate.py:3:8: F401 [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.

注意

作为 uv run 的替代方案,你也可以通过激活项目的虚拟环境(Linux 和 macOS 上为 source .venv/bin/activate,Windows 上为 .venv\Scripts\activate)并直接运行 ruff check 来使用 Ruff。

Ruff 发现了一个未使用的导入,这是 Python 代码中的常见错误。Ruff 认为这是一个“可修复”的错误,因此我们可以通过运行 ruff check --fix 自动解决此问题

$ uv run ruff check --fix
Found 1 error (1 fixed, 0 remaining).

运行 git diff 可见以下变更

--- a/src/numbers/calculate.py
+++ b/src/numbers/calculate.py
@@ -1,7 +1,5 @@
 from typing import Iterable

-import os
-

def sum_even_numbers(numbers: Iterable[int]) -> int:
    """Given an iterable of integers, return the sum of all even numbers in the iterable."""
    return sum(
        num for num in numbers
        if num % 2 == 0
    )

注意,Ruff 默认在当前目录运行,但你可以传入特定路径进行检查

$ uv run ruff check src/numbers/calculate.py

现在我们的项目已通过 ruff check,我们可以通过 ruff format 运行 Ruff 格式化工具

$ uv run ruff format
1 file reformatted

运行 git diff 显示 sum 调用已被重新格式化,以符合默认的 88 字符行长度限制

--- a/src/numbers/calculate.py
+++ b/src/numbers/calculate.py
@@ -3,7 +3,4 @@ from typing import Iterable

 def sum_even_numbers(numbers: Iterable[int]) -> int:
     """Given an iterable of integers, return the sum of all even numbers in the iterable."""
-    return sum(
-        num for num in numbers
-        if num % 2 == 0
-    )
+    return sum(num for num in numbers if num % 2 == 0)

到目前为止,我们一直使用 Ruff 的默认配置。接下来让我们看看如何自定义 Ruff 的行为。

配置

为了确定每个 Python 文件的适当设置,Ruff 会在该文件所在的目录或任何父目录中查找第一个 pyproject.tomlruff.toml.ruff.toml 文件。

要配置 Ruff,我们将在项目根目录的配置文件中添加以下内容

[tool.ruff]
# Set the maximum line length to 79.
line-length = 79

[tool.ruff.lint]
# Add the `line-too-long` rule to the enforced rule set. By default, Ruff omits rules that
# overlap with the use of a formatter, like Black, but we can override this behavior by
# explicitly adding the rule.
extend-select = ["E501"]
# Set the maximum line length to 79.
line-length = 79

[lint]
# Add the `line-too-long` rule to the enforced rule set. By default, Ruff omits rules that
# overlap with the use of a formatter, like Black, but we can override this behavior by
# explicitly adding the rule.
extend-select = ["E501"]

再次运行 Ruff,我们发现它现在强制执行最大行宽,限制为 79

$ uv run ruff check
src/numbers/calculate.py:5:80: E501 Line too long (90 > 79)
Found 1 error.

有关支持设置的完整枚举,请参阅 设置。对于我们的项目,需要特别注意支持的最低 Python 版本

[project]
# Support Python 3.10+.
requires-python = ">=3.10"

[tool.ruff]
# Set the maximum line length to 79.
line-length = 79

[tool.ruff.lint]
# Add the `line-too-long` rule to the enforced rule set.
extend-select = ["E501"]
# Support Python 3.10+.
target-version = "py310"
# Set the maximum line length to 79.
line-length = 79

[lint]
# Add the `line-too-long` rule to the enforced rule set.
extend-select = ["E501"]

规则选择

Ruff 支持 超过 800 条代码检查规则,分布在 50 多个内置插件中。确定合适的规则集取决于你项目的需求:有些规则可能过于严格,有些则是特定框架专用的,等等。

默认情况下,Ruff 会启用 Flake8 的 F 规则以及 E 规则的子集,同时省略任何与格式化工具(如 ruff formatBlack)重叠的样式规则。

如果你是首次引入代码检查器,默认规则集是一个很好的起点:它精简且专注,能够在零配置的情况下捕获各种常见错误(例如未使用的导入)。

如果你是从其他代码检查器迁移到 Ruff,你可以启用与之前配置中等效的规则。例如,如果我们想强制执行 pyupgrade 规则,可以将配置文件设置为以下内容

[project]
requires-python = ">=3.10"

[tool.ruff.lint]
extend-select = [
  "UP",  # pyupgrade
]
target-version = "py310"

[lint]
extend-select = [
  "UP",  # pyupgrade
]

如果我们再次运行 Ruff,会发现它现在强制执行 pyupgrade 规则。特别是,Ruff 会标记出对已弃用的 typing.Iterable 的使用,建议改为 collections.abc.Iterable

$ uv run ruff check
src/numbers/calculate.py:1:1: UP035 [*] Import from `collections.abc` instead: `Iterable`
Found 1 error.
[*] 1 fixable with the `--fix` option.

随着时间的推移,我们可能会选择强制执行额外的规则。例如,我们可能希望强制要求所有函数都包含文档字符串

[project]
requires-python = ">=3.10"

[tool.ruff.lint]
extend-select = [
  "UP",  # pyupgrade
  "D",   # pydocstyle
]

[tool.ruff.lint.pydocstyle]
convention = "google"
target-version = "py310"

[lint]
extend-select = [
  "UP",  # pyupgrade
  "D",   # pydocstyle
]

[lint.pydocstyle]
convention = "google"

如果我们再次运行 Ruff,会发现它现在强制执行 pydocstyle 规则

$ uv run ruff check
src/numbers/__init__.py:1:1: D104 Missing docstring in public package
src/numbers/calculate.py:1:1: UP035 [*] Import from `collections.abc` instead: `Iterable`
  |
1 | from typing import Iterable
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP035
  |
  = help: Import from `collections.abc`

src/numbers/calculate.py:1:1: D100 Missing docstring in public module
Found 3 errors.
[*] 1 fixable with the `--fix` option.

忽略错误

任何检查规则都可以通过在相关行添加 # noqa 注释来忽略。例如,让我们忽略 Iterable 导入的 UP035 规则

from typing import Iterable  # noqa: UP035


def sum_even_numbers(numbers: Iterable[int]) -> int:
    """Given an iterable of integers, return the sum of all even numbers in the iterable."""
    return sum(num for num in numbers if num % 2 == 0)

再次运行 ruff check,我们会发现它不再标记 Iterable 导入

$ uv run ruff check
src/numbers/__init__.py:1:1: D104 Missing docstring in public package
src/numbers/calculate.py:1:1: D100 Missing docstring in public module
Found 2 errors.

如果我们想忽略整个文件的某个规则,可以在文件中的任何位置添加 # ruff: noqa: {code},最好放在顶部,如下所示

# ruff: noqa: UP035
from typing import Iterable


def sum_even_numbers(numbers: Iterable[int]) -> int:
    """Given an iterable of integers, return the sum of all even numbers in the iterable."""
    return sum(num for num in numbers if num % 2 == 0)

有关忽略错误的更深入说明,请参阅 错误抑制

添加规则

当在现有代码库中启用新规则时,你可能希望忽略该规则的 现有 所有违规情况,而仅专注于在未来强制执行。

Ruff 通过 --add-noqa 标志支持此工作流,它会根据现有违规情况为每一行添加 # noqa 指令。我们可以将 --add-noqa--select 命令行标志结合使用,为所有现有的 UP035 违规添加 # noqa 指令

$ uv run ruff check --select UP035 --add-noqa .
Added 1 noqa directive.

运行 git diff 可见以下变更

diff --git a/numbers/src/numbers/calculate.py b/numbers/src/numbers/calculate.py
index 71fca60c8d..e92d839f1b 100644
--- a/numbers/src/numbers/calculate.py
+++ b/numbers/src/numbers/calculate.py
@@ -1,4 +1,4 @@
-from typing import Iterable
+from typing import Iterable  # noqa: UP035

集成

本教程重点介绍了 Ruff 的命令行界面,但 Ruff 也可以通过 ruff-pre-commit 作为 pre-commit 钩子使用

- repo: https://github.com/astral-sh/ruff-pre-commit
  # Ruff version.
  rev: v0.15.7
  hooks:
    # Run the linter.
    - id: ruff-check
    # Run the formatter.
    - id: ruff-format

Ruff 也可以集成到你选择的编辑器中。更多信息请参阅 编辑器 部分。

有关其他集成,请参阅 集成 部分。