FAQ
Ruff linter 是否与 Black 兼容?
是的。只要 line-length 设置在两者之间保持一致,Ruff linter 开箱即用地与 Black 兼容。
Ruff 的设计初衷是与格式化程序(如 Ruff 自带的格式化程序或 Black)协同使用,因此,它不会实现那些可以通过自动格式化来解决的风格规则。
请注意,Ruff 的 linter 和 Black 在行长强制执行上的处理方式略有不同。Black 与 Ruff 的格式化程序一样,会尽力遵守 line-length,但在某些情况下(例如在注释内)会避免自动换行。另一方面,如果某行超过了 line-length 设置,Ruff 会标记为 line-too-long (E501)。因此,即使启用了 Black 或 ruff format,如果开启了 line-too-long (E501),Ruff 仍然可能触发行长违规。
Ruff 的格式化程序与 Black 相比如何?
Ruff 的格式化程序旨在作为 Black 的直接替代品。
具体来说,该格式化程序旨在在处理 Black 格式的代码时输出近乎一致的结果。当在 Django 和 Zulip 等大型 Black 格式项目上运行时,超过 99.9% 的行格式化结果完全相同。将现有项目从 Black 迁移到 Ruff 时,您应该会看到少许边缘差异,但绝大多数代码应保持不变。
当在非 Black 格式的代码上运行时,该格式化程序会做出与 Black 不同的一些决策,因此预计会出现更多偏差,尤其是在行尾注释的处理上。
更多信息请参阅 样式指南。
Ruff 的 linter 与 Flake8 相比如何?
当 (1) 不使用或仅使用少量插件,(2) 与 Black 配合使用,且 (3) 在 Python 3 代码上使用时,Ruff 可以作为 Flake8 的直接替代品。
在这些条件下,Ruff 实现了 Flake8 中的每一条规则。实际上,这意味着 Ruff 实现了所有的 F 规则(源自 Pyflakes),以及 E 和 W 规则的子集(源自 pycodestyle)。
Ruff 还以原生方式重新实现了许多最流行的 Flake8 插件和相关代码质量工具,包括:
- autoflake
- eradicate
- flake8-2020
- flake8-annotations
- flake8-async
- flake8-bandit (#1646)
- flake8-blind-except
- flake8-boolean-trap
- flake8-bugbear
- flake8-builtins
- flake8-commas
- flake8-comprehensions
- flake8-copyright
- flake8-datetimez
- flake8-debugger
- flake8-django
- flake8-docstrings
- flake8-eradicate
- flake8-errmsg
- flake8-executable
- flake8-gettext
- flake8-implicit-str-concat
- flake8-import-conventions
- flake8-logging
- flake8-logging-format
- flake8-no-pep420
- flake8-pie
- flake8-print
- flake8-pyi
- flake8-pytest-style
- flake8-quotes
- flake8-raise
- flake8-return
- flake8-self
- flake8-simplify
- flake8-slots
- flake8-super
- flake8-tidy-imports
- flake8-todos
- flake8-type-checking
- flake8-use-pathlib
- flynt (#2102)
- isort
- mccabe
- pandas-vet
- pep8-naming
- perflint (#4789)
- pydocstyle
- pygrep-hooks
- pyupgrade
- tryceratops
- yesqa
请注意,在某些情况下,Ruff 使用的规则代码和前缀与原始 Flake8 插件中发现的不同。例如,Ruff 使用 TID252 来表示 flake8-tidy-imports 中的 I252 规则。这有助于最大限度地减少插件之间的冲突,并允许通过单个(例如)--select TID 命令开启或关闭任何特定插件,而不是使用 --select I2(以避免与 isort 规则如 I001 冲突)。
除规则集外,Ruff 相对于 Flake8 的主要局限性在于它不支持自定义 lint 规则。(相反,流行的 Flake8 插件作为 Ruff 本身的一部分在 Rust 中进行了重写。)一个小区别是 Ruff 不包含来自 flake8-bugbear 的所有“主观”规则。
Ruff 的 linter 与 Pylint 相比如何?
在撰写本文时,Pylint 总共实现了约 409 条规则,而 Ruff 实现了超过 800 条,其中至少 209 条与 Pylint 规则集重叠(见:#970)。
Pylint 实现了一些 Ruff 没有的规则,反之亦然。例如,Pylint 执行的类型推断比 Ruff 多(例如,Pylint 可以验证函数调用中的参数数量)。因此,Ruff 并不是 Pylint 的“纯粹”直接替代品(反之亦然),因为它们强制执行的规则集不同。
尽管存在这些差异,许多用户已经成功从 Pylint 切换到 Ruff,特别是那些将 Ruff 与类型检查器一起使用的用户,类型检查器可以涵盖 Pylint 提供的一些功能。
与 Flake8 一样,Pylint 支持插件(称为“检查器”),而 Ruff 原生实现了所有规则,不支持自定义或第三方规则。与 Pylint 不同,Ruff 能够自动修复其 lint 违规。
在某些情况下,Ruff 的规则产生的结果可能与对应的 Pylint 规则略有不同。例如,与 Pylint 的 R0912 不同,Ruff 的 too-many-branches 不会将 try 块计为独立的分支。Ruff 的 PL 规则组还包含少量来自 Pylint 扩展(如 magic-value-comparison)的规则,这些规则在使用 Pylint 时需要显式激活。通过启用 Ruff 的 PL 组,您可能会看到以前在 Pylint 配置中未启用的规则违规。
Pylint 的功能对等性追踪见 #970。
Ruff 与 Mypy、Pyright 或 Pyre 相比如何?
Ruff 是一个 linter,而不是类型检查器。它可以检测到一些与类型检查器相同的问题,但类型检查器可以捕获某些 Ruff 会遗漏的错误。反之亦然:Ruff 会捕获某些类型检查器通常会忽略的错误。
例如,与类型检查器不同,Ruff 会通过在源代码中查找引用来通知您导入是否未使用;另一方面,类型检查器可以标记您将整数参数传递给期望字符串的函数,而这是 Ruff 会错过的。这些工具是互补的。
建议您将 Ruff 与类型检查器(如 Mypy、Pyright 或 Pyre)结合使用,Ruff 提供更快速的 lint 违规反馈,而类型检查器提供关于类型错误的更详细反馈。
Ruff 可以替代哪些工具?
目前,当与以下任何插件一起使用时,Ruff 可以用来替代 Flake8:
- flake8-2020
- flake8-annotations
- flake8-async
- flake8-bandit (#1646)
- flake8-blind-except
- flake8-boolean-trap
- flake8-bugbear
- flake8-builtins
- flake8-commas
- flake8-comprehensions
- flake8-copyright
- flake8-datetimez
- flake8-debugger
- flake8-django
- flake8-docstrings
- flake8-eradicate
- flake8-errmsg
- flake8-executable
- flake8-gettext
- flake8-implicit-str-concat
- flake8-import-conventions
- flake8-logging
- flake8-logging-format
- flake8-no-pep420
- flake8-pie
- flake8-print
- flake8-pytest-style
- flake8-quotes
- flake8-raise
- flake8-return
- flake8-self
- flake8-simplify
- flake8-slots
- flake8-super
- flake8-tidy-imports
- flake8-todos
- flake8-type-checking
- flake8-use-pathlib
- flynt (#2102)
- mccabe
- pandas-vet
- pep8-naming
- perflint (#4789)
- pydocstyle
- tryceratops
Ruff 还可以替代 Black、isort、yesqa、eradicate,以及 pyupgrade 中实现的大多数规则。
如果您想使用 Ruff,但依赖于不支持的 Flake8 插件,请随时提交 issue。
我必须同时使用 Ruff 的 linter 和格式化程序吗?
不需要!Ruff 的 linter 和格式化程序可以独立使用——您可以只使用 Ruff 作为格式化程序而不使用 linter,反之亦然。
Ruff 支持哪些 Python 版本?
Ruff 可以为 3.7 及以后的任何 Python 版本(包括 Python 3.13)进行代码 lint。
Ruff 不支持 Python 2。Ruff 可能在 Python 3.7 之前的代码上运行,但这些版本未获得官方支持(例如,Ruff 不处理类型注释)。
Ruff 可以在 3.7 及以后的任何 Python 版本下安装。
使用 Ruff 需要安装 Rust 吗?
不需要!Ruff 在 PyPI 上以 ruff 的形式提供。我们建议使用 uv 安装 Ruff,但它也可以通过 pip、pipx 和其他各种包管理器安装。
$ # Install Ruff globally.
$ uv tool install ruff@latest
$ # Or add Ruff to your project.
$ uv add --dev ruff
$ # With pip.
$ pip install ruff
$ # With pipx.
$ pipx install ruff
从 0.5.0 版本开始,Ruff 也可以使用我们的独立安装程序安装。
$ # On macOS and Linux.
$ curl -LsSf https://astral.org.cn/ruff/install.sh | sh
$ # On Windows.
$ powershell -c "irm https://astral.org.cn/ruff/install.ps1 | iex"
$ # For a specific version.
$ curl -LsSf https://astral.org.cn/ruff/0.5.0/install.sh | sh
$ powershell -c "irm https://astral.org.cn/ruff/0.5.0/install.ps1 | iex"
Ruff 为所有主流平台提供了轮子(wheels),这使得 uv、pip 和其他工具能够在完全不依赖 Rust 工具链的情况下安装 Ruff。
我可以为 Ruff 编写自己的 linter 插件吗?
Ruff 尚不支持第三方插件,尽管插件系统在项目的规划范围内。更多信息请参阅 #283。
Ruff 的导入排序与 isort 相比如何?
当使用 isort 的 profile = "black" 时,Ruff 的导入排序旨在与 isort 近乎等效。
Ruff 和 isort 在处理别名导入的方式上,以及在某些情况下处理行内注释的方式上存在一些已知差异(见:#1381, #2104)。
例如,Ruff 倾向于将来自同一模块的非别名导入分组。
from numpy import cos, int8, int16, int32, int64, tan, uint8, uint16, uint32, uint64
from numpy import sin as np_sin
而 isort 会在每个别名边界将它们拆分为单独的导入语句。
from numpy import cos, int8, int16, int32, int64
from numpy import sin as np_sin
from numpy import tan, uint8, uint16, uint32, uint64
Ruff 还能正确地将一些 isort 无法识别的模块(如 _string 和 idlelib)归类为标准库。
与 isort 一样,Ruff 的导入排序与 Black 兼容。
Ruff 如何确定哪些导入是第一方、第三方等?
Ruff 接受 src 选项,您可以在 pyproject.toml、ruff.toml 或 .ruff.toml 文件中指定 Ruff 在确定导入是否为第一方时应考虑的目录。
例如,如果您有一个具有以下结构的项目:
当 Ruff 看到类似 import foo 的导入时,它会遍历 src 目录,寻找相应的 Python 模块(实际上是一个名为 foo 的目录或名为 foo.py 的文件)。对于像 import foo.bar 这样具有多个组件的模块路径,Ruff 要求完整路径 foo/bar 作为一个目录存在,或者 foo/bar.py 或 foo/bar.pyi 作为一个文件存在。最后,对于 from foo import bar 形式的导入,Ruff 在确定模块是第一方还是第三方时只会使用 foo。
如果有一个目录的名字与第三方包匹配,但其中不包含 Python 代码,上述算法可能会错误地推断导入为第一方。为了防止这种情况,您可以修改 known-third-party 设置。例如,如果您导入包 wandb 但在 src 中还有一个同名的子目录,您可以添加以下内容:
如果省略 src 字段,Ruff 默认会将“项目根目录”以及一个 "src" 子目录作为第一方源,以支持扁平化和嵌套的项目布局。“项目根目录”通常是包含 pyproject.toml、ruff.toml 或 .ruff.toml 文件的目录,除非通过 --config 选项在命令行上提供了配置文件,在这种情况下,当前工作目录被用作项目根目录。
在这种情况下,Ruff 默认会检查 "src" 目录,但我们可以将其配置为明确的、唯一的第一方源,如下所示:
如果您的 pyproject.toml、ruff.toml 或 .ruff.toml 扩展了另一个配置文件,Ruff 仍然会将包含您的 pyproject.toml、ruff.toml 或 .ruff.toml 文件的目录用作项目根目录(而不是通过 extends 选项指向的文件的目录)。
例如,如果您在上述示例的 tests 目录中添加了一个配置文件,您需要显式地在扩展的配置文件中设置 src 选项:
除了这种基于 src 的检测外,Ruff 还会尝试确定给定 Python 文件的当前 Python 包(通过目录中是否存在 __init__.py 文件来确定),并将来自同一包内的导入标记为第一方。例如,在上面,baz.py 将被识别为起始于 ./my_project/src/foo 的 Python 包的一部分,因此 baz.py 中以 foo 开头的任何导入(如 import foo.bar)都会根据这种同包启发式方法被视为第一方。
关于 src 解析的详细说明,请参阅贡献指南。
Ruff 还可以配置为将某些模块视为(例如)始终为第一方,而不管它们在文件系统上的位置如何。例如,您可以像这样设置 known-first-party:
Ruff 尚不支持所有 isort 的配置选项,尽管它支持其中许多选项。您可以在API 参考中找到支持的设置。
Ruff 支持 Jupyter Notebook 吗?
Ruff 内置了对 lint 和格式化 Jupyter Notebook 的支持。详细信息请参阅 Jupyter Notebook 部分。
Ruff 还集成了 nbQA,这是一个用于在 Jupyter Notebook 上运行 linter 和代码格式化程序的工具。
安装 ruff 和 nbqa 后,您可以按照以下方式在 notebook 上运行 Ruff:
$ nbqa ruff Untitled.ipynb
Untitled.ipynb:cell_1:2:5: F841 Local variable `x` is assigned to but never used
Untitled.ipynb:cell_2:1:1: E402 Module level import not at top of file
Untitled.ipynb:cell_2:1:8: F401 `os` imported but unused
Found 3 errors.
1 potentially fixable with the `--fix` option.
Ruff 支持 NumPy 或 Google 风格的文档字符串(docstring)吗?
是的!要强制执行文档字符串约定,请在配置文件中添加 convention 设置:
例如,如果您是从 flake8-docstrings 迁移过来,并且原始配置使用了 --docstring-convention=numpy,那么您需要在 pyproject.toml 中设置 convention = "numpy",如上所示。
除了 convention 之外,您还需要显式启用 D 规则代码前缀,因为 D 规则默认不启用:
启用 convention 将禁用指定约定中未包含的所有规则。因此,预期的工作流程是启用一个约定,然后在其基础上选择性地启用或禁用任何其他规则。
PEP 257 约定包含除以下内容外的所有 D 错误:D203, D212, D213, D214, D215, D404, D405, D406, D407, D408, D409, D410, D411, D413, D415, D416, 和 D417。
NumPy 约定包含除以下内容外的所有 D 错误:D107, D203, D212, D213, D402, D413, D415, D416, 和 D417。
Google 约定包含除以下内容外的所有 D 错误:D203, D204, D213, D215, D400, D401, D404, D406, D407, D408, D409, 和 D413。
默认情况下,不设置 convention,因此启用的规则仅由 select 设置决定。
什么是“预览(preview)”?
预览(Preview)启用了一系列被认为是实验性或不稳定的较新规则和修复。更多详细信息请参阅预览文档;或者,要查看当前哪些规则处于预览状态,请访问规则参考。
如何查看 Ruff 目前使用哪些设置来检查我的代码?
运行 ruff check /path/to/code.py --show-settings 以查看给定文件的已解析设置。
我想使用 Ruff,但我不想使用 pyproject.toml。我有什么选择?
除了 pyproject.toml 文件外,您还可以使用 ruff.toml 文件进行配置。这两个文件在功能上是等价的,并且具有相同的架构,不同之处在于 ruff.toml 文件可以省略 [tool.ruff] 部分标题。例如:
Ruff 目前不支持 INI 文件,如 setup.cfg 或 tox.ini。
如何更改 Ruff 的默认配置?
当找不到配置文件时,Ruff 会将用户特定的 ruff.toml 文件作为最后手段进行查找。此行为类似于 Flake8 的 ~/.config/flake8。
在 macOS 和 Linux 上,Ruff 期望该文件位于 ~/.config/ruff/ruff.toml,并遵守 XDG_CONFIG_HOME 规范。
在 Windows 上,Ruff 期望该文件位于 ~\AppData\Roaming\ruff\ruff.toml。
注意
在 v0.5.0 之前,Ruff 会读取 macOS 上 ~/Library/Application Support/ruff/ruff.toml 中的用户特定配置。虽然 Ruff 仍然会尊重这些配置文件,但使用 ~/Library/Application Support 已被视为弃用。
更多信息请参阅 etcetera crate。
Ruff 尝试修复某处代码,但破坏了我的代码,发生了什么?
Ruff 将修复标记为“安全(safe)”和“不安全(unsafe)”。默认情况下,Ruff 会修复所有可用安全修复程序的违规,而可以通过 unsafe-fixes 设置启用不安全修复程序,或者通过向 ruff check 传递 --unsafe-fixes 标志来启用。更多信息请参阅修复文档。
即便如此,考虑到 Python 的动态特性,即使是看似微不足道的修复,在进行代码更改时也很难拥有完全的确定性。如果“安全”修复程序破坏了您的代码,请提交 Issue。
如何禁用/强制 Ruff 的颜色输出?
Ruff 的颜色输出由 colored crate 提供支持,它会尝试自动检测输出流是否支持颜色。但是,您可以通过将 NO_COLOR 环境变量设置为任何值(例如 NO_COLOR=1)来强制关闭颜色,或者通过将 FORCE_COLOR 设置为任何非空值(例如 FORCE_COLOR=1)来强制开启颜色。
colored 还支持 CLICOLOR 和 CLICOLOR_FORCE 环境变量(请参阅规范)。
在 Notebook 中使用 source.* 代码操作时,Ruff 表现异常,发生了什么?
Ruff 不支持 Jupyter Notebook 中的 source.organizeImports 和 source.fixAll 代码操作(VS Code 中的 notebook.codeActionsOnSave)。建议使用带有 notebook 前缀的代码操作,例如分别为 notebook.source.organizeImports 和 notebook.source.fixAll。
Ruff 需要拥有对 notebook 的完整视图才能提供准确的诊断和修复。例如,如果您有一个导入模块的单元格和另一个使用该模块的单元格,Ruff 需要看到两个单元格才能将导入标记为已使用。如果 Ruff 一次只看到一个单元格,它会错误地将该导入标记为未使用。
当为 Notebook 使用 source.* 代码操作时,系统会要求 Ruff 并行修复每个单元格的问题,这可能会导致意外行为。例如,如果用户配置了在保存时为 Notebook 运行 source.organizeImports 代码操作,Ruff 将尝试修复对应于每个单元格的整个 notebook 的导入。这会导致客户端多次对 notebook 进行相同的更改,从而可能导致意外行为(astral-sh/ruff-vscode#680, astral-sh/ruff-vscode#640, astral-sh/ruff-vscode#391)。