跳到内容

教程

本教程将引导您完成将 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 linter

$ 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/active,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 个 lint 规则,分布在超过 50 个内置插件中,但是确定正确的规则集将取决于您的项目需求:某些规则可能过于严格,某些规则是框架特定的,等等。

默认情况下,Ruff 启用 Flake8 的 F 规则,以及 E 规则的子集,省略了任何与格式化程序(例如 ruff formatBlack)的使用重叠的样式规则。

如果您是第一次引入 linter,默认规则集是一个很好的起点:它范围窄且重点突出,同时以零配置捕获各种常见错误(例如未使用的导入)。

如果您要从另一个 linter 迁移到 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 注释添加到相关行来忽略任何 lint 规则。例如,让我们忽略 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 命令行标志结合使用,以将 # noqa 指令添加到所有现有的 UP035 违规行为

$ 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 hook

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

Ruff 也可以集成到您选择的编辑器中。有关更多信息,请参考编辑器部分。

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