Ruff v0.10.0 现已发布!您可以从 PyPI 或您选择的包管理器安装它

uv pip install --upgrade ruff

温馨提示:Ruff 是一个用 Rust 编写的极速 Python 代码检查器和格式化工具。 Ruff 可以替代 Black、Flake8(以及数十个插件)、isort、pydocstyle、pyupgrade 等工具,同时其执行速度比任何单个工具快数十倍甚至数百倍。

迁移到 v0.10

与 Ruff 的次要版本惯例一样,v0.10 包含的破坏性变更很少,大多数用户应该能够升级而无需对其代码或配置进行重大修改。然而,仍有几项值得单独指出的变更。

改进的 Python 版本检测

此变更以及下文描述的 PGH004 更新行为意外地从 v0.10 中省略,并已包含在 v0.11 中,该版本在 v0.10 之后不久发布。

此版本使 Python 版本检测对于拥有 pyproject.toml 文件的项目来说更加直观。

Ruff 使用您的 Python 版本来为 lint 规则、导入排序以及其他格式化决策提供更准确的建议——这仅仅是其中几项。1 如果您一直在关注最近的 preview 变更,我们还在努力更好地检测与版本相关的语法错误(例如在 Python 3.10 之前使用 match)。这些都依赖于您指定的 Python 版本以避免误报。

在 Ruff 的早期版本中,您可以通过以下方式指定您的 Python 版本:

  • target-version 选项在 ruff.toml 文件中或 pyproject.toml 文件中的 [tool.ruff] 部分。
  • 带有 [tool.ruff] 部分的 pyproject.toml 文件中的 project.requires-python 字段。

这些选项在大多数情况下都运行良好,并且仍然建议用于精确控制 Python 版本。然而,由于 Ruff 发现配置文件的方式,没有 [tool.ruff] 部分的 pyproject.toml 文件会被忽略,包括 requires-python 设置。Ruff 随后会改用默认的 Python 版本(截至本文撰写时为 3.9),这在您尝试请求其他版本时会感到意外。

在 v0.10 中,配置发现已更新以解决此问题:

  1. 如果 Ruff 找到一个没有 target-versionruff.toml 文件,它将检查同一目录中是否存在 pyproject.toml 文件,并尊重其 requires-python 版本,即使该文件不包含 [tool.ruff] 部分。
  2. 如果被检查文件所在目录中没有配置文件(ruff.toml 或带有 [tool.ruff] 部分的 pyproject.toml),Ruff 将在父目录中搜索最近的 pyproject.toml 并使用其 requires-python 设置。2

如果您已经在使用 target-version 选项或在包含 [tool.ruff] 部分的 pyproject.toml 中使用 requires-python,这些变更将不会产生影响。3

有关更多详细信息,请参阅新文档

更强大的抑制注释

行内(# noqa)和文件级(# ruff: noqa)抑制注释的解析已统一,并且整体上变得更加强大。

在以下情况下,注释将抑制比以往更多的规则:

  • 现在允许文件级抑制注释包含前导注释,例如 # Some context # ruff: noqa。以前,这种 noqa 会被静默忽略。
  • 现在允许文件级抑制注释包含尾随注释,例如 # ruff: noqa This file is rough!。而以前,这会导致错误,且抑制不生效。
  • 为了支持上述变更,像 ruff: noqa UP035noqa 后缺少冒号)这样的无效语法现在将抑制所有规则。这与现有的行内行为一致,并通过 blanket-noqa (PGH004) 进行防范。
  • 规则列表中缺失的项现在被接受但会发出警告。例如,#noqa: F401,,F841 将抑制 F401F841 但会显示警告。
  • 类似地,规则列表中缺少分隔符的情况,现在在行内和文件级注释中都可接受,但会发出警告。例如,# ruff: noqa: F401F841 将同时抑制 F401F841 但会显示警告。

在以下情况下,注释可能会抑制比以往更少的规则:

  • 现在不允许在行内抑制注释中使用无效的规则后缀,例如 # noqa: UP035abc。这会记录一个错误并且不抑制任何规则。这与文件级抑制注释的当前行为相符。
  • 现在冒号和规则列表之间允许有空格,例如 #noqa : F401。这以前会抑制所有规则,但现在只会抑制 F401,这可能才是预期的行为。

更新的 TYPE_CHECKING 行为

此版本使 Ruff 对 TYPE_CHECKING 符号的处理与 mypy 和 pyright 等其他工具保持一致。

typing.TYPE_CHECKING 是标准库 typing 模块中的一个特殊常量,可用于避免在运行时进行昂贵的导入,但仍将其包含供类型检查器(和代码检查器)使用。这通常看起来像 typing 文档中改编的这个示例:

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    import expensive_mod

def fun(arg: expensive_mod.SomeType) -> None: ...

然而,在某些情况下,typing 导入本身可能是不必要的。过去,Ruff 和许多类型检查器允许使用 if 0: ...if False: ...if 语句来替代 if TYPE_CHECKING: ...,以避免 typing 导入。这种做法已经不再受青睐,并普遍被替换为使用名为 TYPE_CHECKING 的本地符号,例如:

from __future__ import annotations

TYPE_CHECKING = False
if TYPE_CHECKING:
    import expensive_mod

def fun(arg: expensive_mod.SomeType) -> None: ...

此版本在 Ruff 中稳定了以下两项变更:

  1. 已移除对 if 0if False 替代 if TYPE_CHECKING 的支持。
  2. 已添加对任何名为 TYPE_CHECKING 的符号的支持,而不仅仅是精确的 typing.TYPE_CHECKING

作为额外的好处,此变更强化了诸如 if-else-block-instead-of-if-exp (SIM108) 等规则,这些规则以前无法折叠 if 0if False 块,以防它们实际上用于 TYPE_CHECKING

规则稳定化

以下规则已稳定,不再处于预览模式

其他行为稳定化

此版本还稳定了某些额外行为,这些行为此前仅在 preview 模式下可用:

有关其他较小的变更,请参阅 GitHub 上的完整更新日志。

规则弃用

  • suspicious-xmle-tree-usage (S320) 已被弃用,因为其在 lxml 包中检查的问题已得到解决。
  • non-pep604-isinstance (UP038) 已被弃用,因为在 isinstanceissubclass 调用中使用 PEP 604 联合语法实际上并非推荐模式,并且可能还会影响性能。

感谢!

感谢所有对 Ruff 预览模式中包含的变更提出反馈意见的人,尤其感谢我们的贡献者。与您一同构建 Ruff 是我们的荣幸!


GitHub 上查看完整更新日志。

了解更多关于 Astral — Ruff 背后的公司。

感谢 Dylan Wilson、Micha Reiser、Dhruv Manilawala 和 Zanie Blue 对这篇博文的贡献。

脚注

  1. 如果您好奇,请参阅 v0.8 发布说明以获取更具体的列表!

  2. Ruff 仅在未使用 --isolated 标志且未通过 --config--target-version 标志传递目标版本时执行此搜索。

  3. 如果您没有 project.requires-python 设置或根本没有 pyproject.toml 文件,这些变更也不会影响您。在这些情况下,Ruff 将继续使用其内部默认的 Python 3.9 版本(截至本文撰写时)。