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

pip install --upgrade ruff

提醒一下:Ruff 是一个用 Rust 编写的极速 Python 代码检查工具(linter)。 Ruff 可以用来替代 Flake8(以及数十个插件)、isort、pydocstyle、pyupgrade 等,所有这些工具的执行速度都比任何单个工具快几十甚至几百倍。

GitHub 上查看完整的更新日志,或继续阅读亮点内容。

无效的 # noqa 指令现在会发出警告

此前,如果 Ruff 遇到无效的抑制注释(也称为 # noqa 指令),它会默默地忽略它,或者在某些情况下将其视为通用抑制注释(即忽略给定行上的所有违规行为)。

Ruff 现在将对遇到的每个无效抑制注释发出警告。

例如,给定

import os  # noqa: unused-import

此前,Ruff 会将其视为等同于 # noqa,并忽略该行上的所有违规。现在,Ruff 将发出警告。

warning: Invalid `# noqa` directive on line 1: expected a comma-separated list of codes (e.g., `# noqa: F401, F841`).

Ruff 的 # noqa 解析器现在还支持指令中每个字符之间的可选空格。

flake8: noqa 注释现在支持抑制特定代码

Ruff 支持使用 # flake8: noqa 和等效的 # ruff: noqa 来抑制给定文件中所有违规。

此前,Ruff 还支持 # ruff: noqa: F401 来抑制给定文件中所有 F401 违规。然而,Ruff 不支持 # flake8: noqa: F401,而是将其视为等同于 # flake8: noqa。尽管令人困惑,但此行为与 Flake8 一致,Flake8 不支持使用 # flake8: noqa 抑制特定代码。

Ruff 现在支持 # flake8: noqa: F401 作为特定代码抑制注释,以及 # ruff: noqa: F401

isort 的 known-first-party 及相关设置现在接受通配符

诸如 known-first-party 之类的设置现在除了模块名之外,还接受通配符(globs)。例如,以下设置会将任何以 my_module_ 开头的模块标记为第一方模块。

[tool.ruff.isort]
known-first-party = "my_module_*"

新规则:unnecessary-list-allocation-for-first-element (RUF015)

作用是什么?

检查可以将 list(...)[0] 替换为 next(iter(...)) 的情况。

为何重要?

调用 list(...) 会创建整个集合的新列表,这对于大型集合可能非常耗费资源。如果您只需要集合的第一个元素,可以使用 next(iter(...)) 来惰性获取第一个元素,而无需创建新列表。

请注意,从 list(...)[0] 迁移到 next(iter(...)) 会以两种方式改变您程序的行为:

  1. 首先,list(...) 会立即评估整个集合,而 next(iter(...)) 只会评估第一个元素。因此,迭代期间发生的任何副作用都将被延迟。
  2. 其次,如果集合为空,list(...)[0] 会引发 IndexError,而 next(iter(...)) 会引发 StopIteration

例如,给定以下代码片段

head = list(range(1000000000000))[0]

改为使用 next(iter(...))

head = next(iter(range(1000000000000)))

此规则可自动修复。

@@ -245,7 +245,7 @@ def _parse_setuptools_arguments(
         names = ", ".join(plat_names)
         msg = f"--plat-name is ambiguous: {names}"
         raise BuildError(msg)
-    plat_name = list(plat_names)[0]
+    plat_name = next(iter(plat_names))

@evanrittenhouse 贡献。

新规则:invalid-index-type (RUF016)

作用是什么?

检查对列表、字符串、元组、字节和推导式使用非整数或切片类型进行索引访问的情况。

为何重要?

这些类型只能使用整数或切片作为索引。使用其他类型将在运行时导致 TypeError,在导入时导致 SyntaxWarning

例如,给定以下代码片段

var = [1, 2, 3]["x"]

改为使用整数或切片作为索引。

var = [1, 2, 3][0]

@zanieb 贡献。

新规则:re-sub-positional-args (B034)

作用是什么?

检查对 re.subre.subnre.split 的调用,其中 countmaxsplitflags 作为位置参数传递。

为何重要?

countmaxsplitflags 作为位置参数传递给 re.subre.subnre.split 可能会导致混淆,因为 re 模块中的大多数方法都接受 flags 作为第三个位置参数,而 re.subre.subnre.split 具有不同的签名。

改为将 countmaxsplitflags 作为关键字参数传递。

例如,给定以下代码片段

import re

re.split("pattern", "replacement", 1)

改为将 maxsplit 作为关键字参数传递。

import re

re.split("pattern", "replacement", maxsplit=1)

此规则源自 flake8-bugbear

@charliermarsh 贡献。

新规则:unnecessary-literal-union (PYI030)

作用是什么?

检查联合类型中是否存在多个字面量类型。

为何重要?

字面量类型接受多个参数,将其指定为单个字面量会更清晰。

例如,给定以下代码片段

from typing import Literal

field: Literal[1] | Literal[2]

改为使用单个 Literal 类型。

from typing import Literal

field: Literal[1, 2]

此规则源自 flake8-pyi

@zanieb 贡献。

新规则:type-name-incorrect-variance (PLC0105)

作用是什么?

检查与关联类型参数的变体不匹配的类型名称。

为何重要?

PEP 484 建议对协变和逆变类型参数分别使用 _co_contra 后缀(而不变类型参数不应有任何此类后缀)。

例如,给定以下代码片段

from typing import TypeVar

T = TypeVar("T", covariant=True)
U = TypeVar("U", contravariant=True)
V_co = TypeVar("V_co")

改为使用 _co_contra 后缀。

from typing import TypeVar

T_co = TypeVar("T_co", covariant=True)
U_contra = TypeVar("U_contra", contravariant=True)
V = TypeVar("V")

此规则源自 pylint

@tjkuson 贡献。

新规则:typevar-bivariance (PLC0131)

作用是什么?

检查 TypeVarParamSpec 定义中类型既是协变又是逆变的情况。

为何重要?

默认情况下,Python 的泛型类型是不变的,但可以通过 covariantcontravariant 关键字参数标记为协变或逆变。虽然 API 确实允许您将类型标记为既协变又逆变,但这不受类型系统支持,应避免使用。

改为将类型的变体更改为协变、逆变或不变。如果您想描述协变和逆变,请考虑使用两个单独的类型参数。

背景:一个“不变”的泛型类型只接受与类型参数完全匹配的值;例如,list[Dog] 只接受 list[Dog],不接受 list[Animal](超类)或 list[Bulldog](子类)。这是 Python 泛型类型的默认行为。

一个“协变”泛型类型接受类型参数的子类;例如,Sequence[Animal] 接受 Sequence[Dog]。一个“逆变”泛型类型接受类型参数的超类;例如,Callable[Dog] 接受 Callable[Animal]

例如,给定以下代码片段

from typing import TypeVar

T = TypeVar("T", covariant=True, contravariant=True)

改为使用单一变体。

from typing import TypeVar

T_co = TypeVar("T_co", covariant=True)
T_contra = TypeVar("T_contra", contravariant=True)

此规则源自 pylint

@tjkuson 贡献。

错误修复

  • 支持某些多行 str.format 调用的自动修复,由 @harupy#5638 中贡献
  • 避免对后期绑定 lambda 触发 unnecessary-map (C417),由 @charliermarsh#5520 中贡献
  • 使用 .astimezone() 时避免触发 DTZ001-006,由 @dhruvmanila#5524 中贡献
  • 通过语义模型启用属性查找,由 @charliermarsh#5536 中贡献
  • 在 f-string 中重写 str(dict) 时避免语法错误,由 @charliermarsh#5538 中贡献
  • 区分运行时和类型检查时注释,由 @charliermarsh#5575 中贡献
  • 仅在启用时运行 pyproject.toml lint 规则,由 @charliermarsh#5578 中贡献
  • 重构 isort 指令跳过以使用迭代器,由 @charliermarsh#5623 中贡献
  • 允许在 dataclass 字段中实例化描述符,由 @charliermarsh#5537 中贡献
  • 重构 noqa 指令解析,脱离基于正则表达式的实现,由 @charliermarsh#5554 中贡献
  • 对无效的 # noqa 指令发出警告,由 @charliermarsh#5571 中贡献
  • 支持 # flake8: noqa 指令上的单个代码,由 @charliermarsh#5618 中贡献
  • 添加 tkinter 导入约定,由 @tjkuson#5626 中贡献
  • 如果条件依赖于列表变量,则避免 PERF401 触发,由 @dhruvmanila#5603 中贡献
  • 修复 complex-if-statement-in-stub 消息中的拼写错误,由 @charliermarsh#5635 中贡献
  • 使 TRY301 仅在 raise 抛出捕获的异常时触发,由 @evanrittenhouse#5455 中贡献
  • 在 stub 文件中跳过 flake8-future-annotations 检查,由 @charliermarsh#5652 中贡献
  • 始终允许在 stub 文件中进行 PEP 585 和 PEP 604 重写,由 @charliermarsh#5653 中贡献
  • 为 PYI016 添加对不带 |Union 声明的支持,由 @zanieb#5598 中贡献
  • flake8-self 规则中忽略 _name__value_ 访问,由 @monosans#5663 中贡献
  • 重构 repeated_keys() 以使用 ComparableExpr,由 @qdegraaf#5696 中贡献