跳到内容

Ruff Linter

Ruff Linter 是一个速度极快的 Python linter,旨在作为 Flake8 (以及数十个插件)、isortpydocstylepyupgradeautoflake 等的直接替代品。

ruff check

ruff check 是 Ruff linter 的主要入口点。它接受文件或目录列表,并检查所有发现的 Python 文件,可以选择修复任何可修复的错误。在检查目录时,Ruff 会在该目录及其所有子目录中递归搜索 Python 文件

$ ruff check                  # Lint files in the current directory.
$ ruff check --fix            # Lint files in the current directory and fix any fixable errors.
$ ruff check --watch          # Lint files in the current directory and re-lint on change.
$ ruff check path/to/code/    # Lint files in `path/to/code`.

有关支持的完整选项列表,请运行 ruff check --help

规则选择

启用规则的集合通过 lint.selectlint.extend-selectlint.ignore 设置进行控制。

Ruff 的 linter 镜像了 Flake8 的规则代码系统,其中每个规则代码都由一个一到三个字母的前缀,后跟三个数字(例如,F401)组成。前缀指示规则的“来源”(例如,F 代表 Pyflakes,E 代表 pycodestyle,ANN 代表 flake8-annotations)。

lint.selectlint.ignore 这样的规则选择器接受完整的规则代码(例如,F401)或任何有效的前缀(例如,F)。例如,给定以下配置文件

[tool.ruff.lint]
select = ["E", "F"]
ignore = ["F401"]
[lint]
select = ["E", "F"]
ignore = ["F401"]

Ruff 将启用所有带有 E (pycodestyle) 或 F (Pyflakes) 前缀的规则,但 F401 除外。有关通过 pyproject.toml 配置 Ruff 的更多信息,请参见 配置 Ruff

作为一种特殊情况,Ruff 还支持 ALL 代码,该代码启用所有规则。请注意,某些 pydocstyle 规则会冲突(例如,D203D211),因为它们代表了替代的文档字符串格式。启用 ALL 时,Ruff 将自动禁用任何冲突的规则。

如果您想知道如何配置 Ruff,这里有一些 推荐的指南

  • 首选 lint.select 而不是 lint.extend-select 以使您的规则集明确。
  • 谨慎使用 ALL。启用 ALL 将在您每次升级时隐式启用新规则。
  • 从一小部分规则 (select = ["E", "F"]) 开始,并一次添加一个类别。例如,您可以考虑扩展到 select = ["E", "F", "B"] 以启用流行的 flake8-bugbear 扩展。

例如,启用一些最流行的规则(而又不过于迂腐)的配置可能如下所示

[tool.ruff.lint]
select = [
    # pycodestyle
    "E",
    # Pyflakes
    "F",
    # pyupgrade
    "UP",
    # flake8-bugbear
    "B",
    # flake8-simplify
    "SIM",
    # isort
    "I",
]
[lint]
select = [
    # pycodestyle
    "E",
    # Pyflakes
    "F",
    # pyupgrade
    "UP",
    # flake8-bugbear
    "B",
    # flake8-simplify
    "SIM",
    # isort
    "I",
]

要解析启用的规则集,Ruff 可能需要协调来自各种来源的 lint.selectlint.ignore,包括当前的 pyproject.toml、任何继承的 pyproject.toml 文件和 CLI(例如,--select)。

在这些情况下,Ruff 使用“最高优先级”select 作为规则集的基础,然后应用 extend-selectignore 调整。 CLI 选项的优先级高于 pyproject.toml 选项,而当前 pyproject.toml 文件的优先级高于任何继承的 pyproject.toml 文件。

例如,给定以下配置文件

[tool.ruff.lint]
select = ["E", "F"]
ignore = ["F401"]
[lint]
select = ["E", "F"]
ignore = ["F401"]

运行 ruff check --select F401 将导致 Ruff 强制执行 F401,而不执行其他规则。

运行 ruff check --extend-select B 将导致 Ruff 强制执行 EFB 规则,但 F401 除外。

修复

Ruff 支持自动修复各种 lint 错误。例如,Ruff 可以删除未使用的导入、重新格式化文档字符串、重写类型注解以使用更新的 Python 语法等。

要启用修复,请将 --fix 标志传递给 ruff check

$ ruff check --fix

默认情况下,Ruff 将修复所有可用的安全修复的违规行为;要确定规则是否支持修复,请参见 规则

修复安全性

Ruff 将修复标记为“安全”和“不安全”。应用安全修复时,代码的含义和意图将得以保留,但应用不安全修复时,含义可能会更改。

具体来说,不安全修复可能会导致运行时行为发生变化、删除注释或两者兼而有之,而安全修复旨在保留运行时行为,并且仅在删除整个语句或表达式时(例如,删除未使用的导入)才删除注释。

例如,unnecessary-iterable-allocation-for-first-element (RUF015) 是一条规则,用于检查可能导致性能问题的 list(...)[0] 用法。该修复将此模式替换为 next(iter(...)),这可以显著提高速度

$ python -m timeit "head = list(range(99999999))[0]"
1 loop, best of 5: 1.69 sec per loop
$ python -m timeit "head = next(iter(range(99999999)))"
5000000 loops, best of 5: 70.8 nsec per loop

但是,当集合为空时,此引发的异常将从 IndexError 更改为 StopIteration

$ python -c 'list(range(0))[0]'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
IndexError: list index out of range
$ python -c 'next(iter(range(0)))[0]'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
StopIteration

由于异常类型的更改可能会破坏上游的错误处理,因此此修复被归类为不安全。

默认情况下,Ruff 仅启用安全修复。可以通过在配置文件中设置 unsafe-fixes 或将 --unsafe-fixes 标志传递给 ruff check 来启用不安全修复

# Show unsafe fixes
ruff check --unsafe-fixes

# Apply unsafe fixes
ruff check --fix --unsafe-fixes

默认情况下,当不安全修复可用但未启用时,Ruff 将显示提示。可以通过将 unsafe-fixes 设置设置为 false 或使用 --no-unsafe-fixes 标志来使该建议静默。

可以使用 lint.extend-safe-fixeslint.extend-unsafe-fixes 设置按规则调整修复的安全性。

例如,以下配置会将 F601 的不安全修复提升为安全修复,并将 UP034 的安全修复降级为不安全修复

[tool.ruff.lint]
extend-safe-fixes = ["F601"]
extend-unsafe-fixes = ["UP034"]
[lint]
extend-safe-fixes = ["F601"]
extend-unsafe-fixes = ["UP034"]

您也可以使用前缀来选择规则,例如,F 可用于将 Pyflakes 中所有规则的修复提升为安全。

注意

使用 json 输出格式时,Ruff 将始终显示所有修复。每个修复的安全性都在 applicability 字段下可用。

禁用修复

要限制 Ruff 应修复的规则集,请使用 lint.fixablelint.extend-fixablelint.unfixable 设置。

例如,以下配置将启用除 unused-imports (F401) 之外的所有规则的修复

[tool.ruff.lint]
fixable = ["ALL"]
unfixable = ["F401"]
[lint]
fixable = ["ALL"]
unfixable = ["F401"]

相反,以下配置将仅启用 F401 的修复

[tool.ruff.lint]
fixable = ["F401"]
[lint]
fixable = ["F401"]

错误抑制

Ruff 支持多种抑制 lint 错误的机制,无论它们是误报还是允许的违规行为。

要完全忽略 lint 规则,请通过 lint.ignore 设置将其添加到“ignore”列表中,无论是在命令行中还是在您的 pyproject.tomlruff.toml 文件中。

要内联抑制违规行为,Ruff 使用类似于 Flake8noqa 系统。要忽略单个违规行为,请将 # noqa: {code} 添加到行尾,如下所示

# Ignore F841.
x = 1  # noqa: F841

# Ignore E741 and F841.
i = 1  # noqa: E741, F841

# Ignore _all_ violations.
x = 1  # noqa

对于多行字符串(如文档字符串),noqa 指令应位于字符串的末尾(在结束的三引号之后),并将应用于整个字符串,如下所示

"""Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.
"""  # noqa: E501

对于导入排序,noqa 应位于导入块中第一行的末尾,并将应用于块中的所有导入,如下所示

import os  # noqa: I001
import abc

要忽略整个文件中的所有违规行为,请在文件中的任何位置添加行 # ruff: noqa,最好是在顶部附近,如下所示

# ruff: noqa

要忽略整个文件中的特定规则,请在文件中的任何位置添加行 # ruff: noqa: {code},最好是在顶部附近,如下所示

# ruff: noqa: F841

或者参见 lint.per-file-ignores 设置,该设置从您的 pyproject.tomlruff.toml 文件中启用相同的功能。

全局 noqa 注释必须位于其自己的行上,以消除与忽略单行违规行为的注释的歧义。

请注意,Ruff 还将尊重 Flake8 的 # flake8: noqa 指令,并将其视为等同于 # ruff: noqa

完整的抑制注释规范

完整的规范如下

  • 内联的 blanket noqa 注释由不区分大小写的 #noqa 匹配给出,在 # 符号后带有可选的空格,后跟:注释的结尾、新注释的开头 (#) 或空格后跟除 : 之外的任何字符。
  • 内联规则抑制首先通过不区分大小写的 #noqa 匹配给出,在 # 符号后带有可选的空格,在 noqa 后带有可选的空格,后跟符号 :。在此之后,我们应该有一个规则代码列表,该列表由大写 ASCII 字符后跟 ASCII 数字的序列给出,并用空格或逗号分隔。列表在最后一个有效代码处结束。我们将尝试解释缺少分隔符的规则(例如 F401F841),但在此情况下将发出警告。
  • 文件级豁免注释由区分大小写的 #ruff:#flake8: 匹配给出,在 #: 之前带有可选的空格,后跟可选的空格和不区分大小写的 noqa 匹配。之后,规范与内联情况相同。

检测未使用的抑制注释

Ruff 实现了一个特殊的规则 unused-noqa,在 RUF100 代码下,用于强制您的 noqa 指令“有效”,即它们它们忽略的违规行为实际上是在该行上触发的(因此被抑制)。要标记未使用的 noqa 指令,请运行:ruff check /path/to/file.py --extend-select RUF100

Ruff 还可以通过其修复功能删除任何未使用的 noqa 指令。要删除任何未使用的 noqa 指令,请运行:ruff check /path/to/file.py --extend-select RUF100 --fix

插入必要的抑制注释

Ruff 可以自动添加 noqa 指令到所有包含违规行为的行,这在将新代码库迁移到 Ruff 时非常有用。要自动添加 noqa 指令到所有相关行(带有适当的规则代码),请运行:ruff check /path/to/file.py --add-noqa

操作注释

Ruff 尊重 isort 的 操作注释 (# isort: skip_file, # isort: on, # isort: off, # isort: skip# isort: split),这可以有选择地为代码块和其他内联配置启用和禁用导入排序。

Ruff 还将尊重这些操作注释的变体,带有 # ruff: 前缀(例如,# ruff: isort: skip_file, # ruff: isort: on 等)。这些变体更清楚地表明操作注释是为 Ruff 设计的,但在功能上与 isort 变体等效。

与 isort 不同,Ruff 不尊重文档字符串中的操作注释。

有关更多信息,请参见 isort 文档

退出码

默认情况下,ruff check 以以下状态代码退出

  • 0 如果未发现违规行为,或者如果所有存在的违规行为都已自动修复。
  • 1 如果发现违规行为。
  • 2 如果 Ruff 由于无效的配置、无效的 CLI 选项或内部错误而异常终止。

此约定镜像了 ESLint、Prettier 和 RuboCop 等工具。

ruff check 支持两个命令行标志,这些标志会更改其退出代码行为

  • --exit-zero 将导致 Ruff 即使发现违规行为也以状态代码 0 退出。请注意,如果 Ruff 异常终止,它仍然会以状态代码 2 退出。
  • --exit-non-zero-on-fix 将导致 Ruff 如果发现违规行为以状态代码 1 退出,即使所有此类违规行为都已自动修复。请注意,即使在修复后没有剩余违规行为,使用 --exit-non-zero-on-fix 也可能导致非零退出代码。