首页
/ Ruff 0.4.x 系列技术回顾:全新手写解析器、内置 Rust 语言服务器与规则体系扩容

Ruff 0.4.x 系列技术回顾:全新手写解析器、内置 Rust 语言服务器与规则体系扩容

2026-09-07 09:16:41作者:劳婵绚Shirley

Ruff 的 0.4.x 版本系列(0.4.0 ~ 0.4.10)是该项目迈向现代化解析与编辑器体验的关键里程碑:它以全新手写解析器替换旧实现,显著提升 lint/format 全链路速度,并首次把用 Rust 编写的 ruff server 语言服务器内置进分发包中、在系列尾声将其推进到 Beta。本文以仓库中的 changelogs/0.4.x.md 为主线,逐版本梳理解析器重写、语言服务器演进、新规则与规则变更、CLI/配置变化、性能优化与破坏性变更,并对照当前仓库源码给出可深入阅读的实现与测试位置,帮助你在升级、规则选型和编辑器集成的决策中掌握完整的版本事实。

版本系列总览

0.4.x 包含 0.4.0 至 0.4.10 共 11 个补丁/次版本,主线可用一句话概括:换引擎(新解析器)+ 换服务端(内置 Rust LSP)+ 大规模扩充规则与配置能力。各版本的关键主题如下:

版本 核心主题
0.4.0 全新手写解析器(lint/format 提速 20–40%);ruff server Alpha 随分发包内置;RUF029 unused-async 等预览规则;新增 RUFF_OUTPUT_FILE 环境变量
0.4.1 pylint 补齐 invalid-hash-returned/invalid-index-returned;多项解析器边界修复
0.4.2 FURB192 建议用 min/max 替代 sorted()[0]/[-1]%s % var 不安全修复(UP031);Server 增加 noqa hover 与通用配置项
0.4.3 PEP 696(类型参数默认值)语法支持;RUF101 redirected-noqaFURB116 fstring-number-format;Server 支持自定义 TOML 配置、遵循 per-file-ignores
0.4.4 PYI059PYI062PLW1514pathlib.Path.open 未指定编码);Server 配置文件支持 ~ 展开
0.4.5 ruff server 进入 Beta;Server 支持 Jupyter Notebook 与 noqa code action;ruff config 新增 --output-format
0.4.6 破坏性变更:GitLab fingerprint 改为项目相对路径、最低 Windows 10;UP040TypeAliasType)、ASYNC116
0.4.7 PYI064/066/057;默认启用 F822__init__.py);新增 Vim/Kate 配置指南
0.4.8 lexer/parser 同步重构带来约 10% lint 微基准提升;B901PYI063--output-format 支持 RDJson
0.4.9 C0206FURB154E203 修复与 ruff format 行为对齐;Server 增加 ruff.printDebugInformation
0.4.10 解析器 re-lexing 错误恢复;CPY001 检查范围扩至 4096 字节;E999 上报全部语法错误

下文按技术主题(而非单纯版本顺序)组织,方便对照你自己的关注点阅读。

0.4.0 的核心:全新手写解析器

重写动机与收益

Ruff 0.4.0 发布时官方宣布,新解析器相比旧实现快 2 倍以上,折算到所有 lint 与 format 调用上带来 20–40% 的整体提速。由于解析是 lint、格式化、import 排序等所有前端工作的公共底座,这项替换直接影响几乎每一次 Ruff 调用。

说明:以上性能数字来自官方 changelog 声明(见 changelogs/0.4.x.md),作为版本事实引用;具体基准数据需以官方后续发布为准。

仓库中的实现落点

在当前的仓库中,这一代解析器位于 ruff_python_parser crate,核心代码集中在 crates/ruff_python_parser/src/parser/(含 mod.rsexpression.rsstatement.rspattern.rs 等分模块),其公共入口由 crates/ruff_python_parser/src/lib.rs 暴露(如 parseparse_moduleparse_expression 系列)。解析器的词法与语法层协议在 crates/ruff_python_parser/src/lexer.rs 与 parser 模块之间衔接;大量的语法行为由 crates/ruff_python_parser/tests/snapshots/crates/ruff_python_parser/src/parser/snapshots/ 下的快照测试固化。

0.4.x 后续对解析器的持续打磨

解析器重写并非一次完成,0.4.x 后半程继续修复了一批边界情况:

  • 0.4.1:token source 出现 “gap” 时使用空 range(避免越界/错误定位);match 语句解析期望缩进的 case 块而非直接以 match 语句处理。
  • 0.4.8:重构 lexer 与 parser,使二者保持同步推进(synchronicity),在部分微基准上 lint 性能再提升约 10%。
  • 0.4.10:引入 re-lexing 逻辑以改进错误恢复——解析遇到非法 token 时可重新切词,让后续语法错误也能被正确报告。
  • 其他贯穿性解析修复包括 0.4.0 对带括号 with 项(if/二元表达式场景)与 FOR_TARGET 上下文重置的处理等。

配套地,0.4.5 还更新了 CONTRIBUTING.md 以反映新解析器的工作方式,说明该实现已成为后续开发的默认前端。

内置 Rust 语言服务器 ruff server:从 Alpha 到 Beta

0.4.x 的另一条主线是 ruff server——一个直接编译进 Ruff 分发包的 Rust 语言服务器,可服务于任何实现了 Language Server Protocol(LSP)的编辑器。它取代了原先独立的 Python 版 ruff-lsp 集成路径。

架构与开箱特性

官方说明其采用受 rust-analyzer 启发的多线程、无锁架构。0.4.0(Alpha)时已具备的能力包括:

  • 自动对 Python 文件做 lint 并展示可用的 quick-fix;
  • 格式化 Python 文件,支持 range formatting(区域格式化);
  • 内置快捷命令:ruff.applyAutofixruff.applyFormatruff.applyOrganizeImports
  • 支持 source.fixAllsource.organizeImports 等标准 source action;
  • 项目配置变更后自动重载。

以上命令与 code action 的当前实现可对照 crates/ruff_server/src/server.rs 查看:SupportedCommandruff.applyAutofix/ruff.applyFormat/ruff.applyOrganizeImports 与 LSP 的 source.fixAllsource.organizeImports 能力映射起来,后续 0.4.9 又补充了 ruff.printDebugInformation 调试命令(见同文件的 Debug 分支)。

配置与编辑器集成逐步成熟

Server 的配置解析与编辑器集成本身也是一个渐进过程,0.4.x 内经历了多次迭代:

  • 0.4.0:启用 ruff 专属 source action;打开文件对应的配置被修改时刷新诊断;重要错误以弹窗展示;引入可直接配置 linter/formatter 的 server 设置;为每个文档单独解析配置;并为 Neovim 编写首个配置指南。
  • 0.4.2:修复 Neovim/Helix 下诊断缺失的问题;对 noqa 代码提供 hover 文档;用新 server 设置承载常见 Ruff 配置项。
  • 0.4.3:补充 Helix 配置指南;修复 shutdown 后挂起;无本地配置时回退读取用户配置目录中的 TOML;遵循 per-file-ignores;支持自定义 TOML 配置文件;新增“项目配置优先于编辑器配置”的设置项。
  • 0.4.4:配置文件路径支持 ~ 展开;修复 Neovim 关闭后 server 挂起;没有文件级配置时默认使用编辑器设置。
  • 0.4.5(Beta 里程碑):官方宣布 ruff server 进入 Beta,功能集与 ruff-lsp 对齐(lint、format、代码修复),但性能更好且无需额外安装。同时新增 Jupyter Notebook 文件支持noqa 注释 code action;修复自动配置重载。
  • 0.4.6:配置发现遵循 exclude;初始化选项为空时回退默认设置;.pyi 正确按 stub 文件处理;向上级目录搜索配置;空的 code action 过滤器不再误返回 notebook source action。
  • 0.4.7:遵循文件排除规则;支持尚不存在于磁盘的文档(未保存文件);新增 Vim 与 Kate 配置指南。
  • 0.4.8:对含语法问题的文档执行格式化时不再反复弹出错误提示。
  • 0.4.9:在 server capabilities 中声明支持的命令;优先使用真实文件路径;对不可用文档执行命令时给出更清晰的错误;引入 ruff.printDebugInformation;tracing 系统遵循 log/trace 级别并支持写日志文件。
  • 0.4.10:为 Helix 与 Neovim 文档补充 tracing 配置指南;延迟 notebook 单元格删除避免误报错误。

上述编辑器集成指南在当前仓库中的对应物是 crates/ruff_server/docs/setup/NEOVIM.mdcrates/ruff_server/docs/setup/HELIX.mdcrates/ruff_server/docs/setup/VIM.mdcrates/ruff_server/docs/setup/KATE.md;面向用户的统一安装说明见 crates/ruff_server/README.mddocs/editors/setup.md。从 Python 版 ruff-lsp 迁移到 ruff server 的操作要点记录在 docs/editors/migration.md

0.4.x 新增规则详解

整个 0.4.x 周期持续以“预览(preview)→ 视反馈调整 → 稳定”的节奏扩充规则集。以下按代码前缀分组列出系列内新增或显著增强的规则。

ruff(内置规则)

  • RUF029 unused-async(0.4.0 预览):检测声明为 async 却既没有 await、也没有使用 async-with/async-for 等需要 async 上下文的函数。实现与行为说明见 crates/ruff_linter/src/rules/ruff/rules/unused_async.rs,其文档注释中给出典型例子:
# 误报场景:声明 async 却同步执行
async def foo():
    bar()

该规则使用 AsyncExprVisitor 做源码序遍历:一旦发现 await 表达式、async withasync for 或异步推导即停止;不会深入内嵌函数/类的方法体去“蹭”异步特征。0.4.1 起忽略 stub 函数,0.4.2 修复了异步推导的误报(并考虑推导体中的 async 表达式),使得判定逻辑更贴近语义。此外其元数据标注 preview_since = "v0.4.0"、属于 Pedantic 类别,测试快照见 crates/ruff_linter/src/rules/ruff/snapshots/unused-async_RUF029.py.snap

  • RUF101 redirected-noqa(0.4.3 预览):检测被重定向(redirected)规则代码的 noqa 注释——即用旧的、已被重定向的规则代号写的抑制注释。实现见 crates/ruff_linter/src/rules/ruff/rules/redirected_noqa.rs

  • RUF100 unused-noqa(0.4.3 增强):除了“未使用”的 noqa,现在也把重复的 noqa 代码(同一行同一代码写了多次)识别进来。

pylint

0.4.x 大力补齐了 pylint 的“返回值类型协议”系列规则:

版本 规则 代码
0.4.0 invalid-bytes-returned E0308
0.4.0 invalid-length-returned E0303
0.4.0 self-cls-assignment W0642
0.4.1 invalid-hash-returned PLE0309
0.4.1 invalid-index-returned PLE0305
0.4.4 unspecified-encoding(扩展到 pathlib.Path.open PLW1514
0.4.9 consider-dict-items C0206

配套的规则行为修正还包括:0.4.0 在 stub 场景下对 invalid-bool/invalid-str-return-type 放行;0.4.1 允许 __str____len__ 等特殊方法声明为 NoReturn 风格;0.4.3 让 PLR0206(property 之外的属性赋值)也覆盖带 variadic 参数的 property;0.4.4 的 too-many-branchesPLR0912)把 with 语句计入分支。这些规则与快照可在 crates/ruff_linter/src/rules/pylint/rules/ 与对应 snapshots 中追溯。

flake8-pyi(stub 文件规则)

版本 规则 代码
0.4.4 generic-not-last-base-class PYI059
0.4.4 duplicate-literal-member PYI062
0.4.7 pep484-style-positional-only-parameter(0.4.8 落地) PYI063
0.4.7 invalid-...PYI064/PYI066/PYI057

其中 PYI059PYI062 的实现可直接阅读 crates/ruff_linter/src/rules/flake8_pyi/rules/generic_not_last_base_class.rscrates/ruff_linter/src/rules/flake8_pyi/rules/duplicate_literal_member.rs

refurb

版本 规则 代码
0.4.2 min/max over sorted() FURB192
0.4.3 fstring-number-format FURB116
0.4.9 repeated-global FURB154

FURB116 会建议把 bin(...)[2:]hex(...)[2:]oct(...)[2:] 这类“调用 + 去前缀切片”替换为等价的 f-string 格式说明符,例如(示例来自 crates/ruff_linter/src/rules/refurb/rules/fstring_number_format.rs):

print(bin(1337)[2:])  # 触发
print(f"{1337:b}")    # 建议改写

该源码还标注其修复仅对整数字面量标记为安全,其余场景只给出提示,避免运行时行为改变。此外 0.4.3 对 reimplemented-operatorFURB118)做了两处收紧:忽略方法定义、且诊断范围用函数范围定位;0.4.9 修复 operator.itemgetter 建议在参数为 tuple 时的误判。

其余新增/显著变化

  • flake8-async:0.4.6 新增 ASYNC116——asyncio.sleep 间隔大于 24 小时时几乎等同于“永久睡眠”,通常应使用永久等待原语。
  • pygrep_hooks:0.4.8 扩展 PGH004,可通过文件级 pragma 检查 blanket ignore;0.4.3 还修复了末行 noqa 无换行时 blanket-noqa 的 panic。
  • pycodestyle:0.4.0 起,跟随在“dummy body”函数/方法之后的 def 不再触发 E3 空行类规则;0.4.4 判定空行规则时忽略行尾注释;0.4.5 让 E27 系列考虑软关键字;0.4.9 让 E203 的修复与 ruff format 输出保持一致;0.4.10 让 E999 上报全部语法错误(而非只报第一个)。
  • pyflakes:0.4.5 建议把未使用的 import 绑定加入 __all__;0.4.7 起 F822__init__.py默认启用
  • mccabe/pylint 复杂度类:0.4.6 起 C901PLR0912PLR0915match-case 计入分支/复杂度,C901 把不可反驳(irrefutable)的 pattern 视为 if..else 结构。
  • pyupgrade:0.4.6 新增对 typing.TypeAliasType 用法的 UP040;0.4.6 让 UP032 在转换到 f-string 时移除空串;0.4.2 的 UP031 可对 "%s" % var 提供(不安全的)修复。
  • flake8-bandit:0.4.0 允许静态 Request 参数的 urllib.request.urlopen;0.4.6 让 S310requests.request 也告警“未设置 timeout”。
  • numpy:0.4.6 为 NumPy 2.0 迁移规则补齐缺失函数;0.4.8 依据 NumPy 2.0 更新 NPY001
  • flake8-bugbear:0.4.0 将仅含 raise NotImplemented 的函数体视为 stub(B006);0.4.3 让 B024 忽略非抽象类属性;0.4.4 让 B019 忽略枚举类;0.4.8 新增 B901 return-in-generator
  • isort/标准库数据:0.4.5 扩充标准库模块集合(加入 _string 等),配合 crates/ruff_python_stdlib 维护的标准库清单使用。

配置与 CLI 变化

RUFF_OUTPUT_FILE 环境变量(0.4.0)

ruff check 的输出文件参数现在可通过环境变量覆盖,便于 CI 等场景免参数化配置。在 crates/ruff/src/args.rs 中可见其定义方式:

/// Specify file to write the linter output to (default: stdout).
#[arg(short, long, env = "RUFF_OUTPUT_FILE")]
output_file: Option<PathBuf>,

即:不传 --output-file 时,若设置了 RUFF_OUTPUT_FILE,结果将写入该路径而非 stdout;0.4.6 还保证写入 --output-file 时自动创建中间目录。同文件亦可看到配套的 RUFF_OUTPUT_FORMAT 环境变量绑定到 --output-format

CLI 形态增强

  • ruff config --output-format(0.4.5):查看解析后配置的命令支持指定输出格式。
  • RDJson 输出(0.4.8)--output-format 新增 RDJson 序列化,便于机器消费与持续集成;ruff config 与诊断输出共用同一套格式枚举(见 crates/ruff/src/args.rsOutputFormat 的定义)。
  • diff 视图(0.4.9):diff 输出对不可打印字符做了安全处理。

其他配置相关修复

  • 0.4.0:--show-settings 对规则的展示方式改进;0.4.2 起内部使用 matchit 解析 per-file 设置,提升多项目/多配置文件场景的查找性能。
  • per-file-ignores 的语义在 0.4.x 被多次修正:0.4.0 与 0.4.2 保证 RUF100 在整行 blanket # noqa 场景下尊重 per-file-ignores,0.4.8 扩展到 blanket 与 redirected noqa 规则。

破坏性变更与平台支持

0.4.x 期间宣布了两项面向使用方的破坏性变更:

  1. Windows 最低版本提升到 Windows 10(0.4.3 提高要求,0.4.6 正式落实)。如果你的 CI 还在维护 Windows 8/8.1 等旧环境,升级 0.4.x 前需确认系统版本。
  2. GitLab fingerprint 使用项目相对路径计算(0.4.6)。这影响 GitLab 集成中诊断去重/注释匹配的稳定性——指纹从此与仓库内的文件路径一致,跨仓库拷贝同一文件不再产生“相同”指纹。

发布工程方面:为兼容 macOS 11,0.4.2 起改用 macos-12 构建发布 wheel;0.4.3 为 macOS 单独构建 ARM wheel,改善 Apple Silicon 上的原生体验。

性能与运行时优化汇总

除了解析器重写这一最大头,0.4.x 还包含多条定向优化:

  • RuleTable::any_enabled 查询加速、linter 对内置符号的处理改进与 BuiltinTypeChecker 推断增强(0.4.0);
  • matchit(路由匹配库)加速 per-file 设置解析(0.4.2);
  • isort 模块名避免多余分配(0.4.3);
  • lexer/parser 同步化重构带来约 10% lint 微基准提升(0.4.8);
  • 类与函数按脚本名解析、__all__ 赋值(如 __all__ = builtins.list([...]))被正确识别等运行时正确性修复(0.4.0、0.4.4)。

Formatter 侧同样收获了一批稳定性修复:0.4.3 在存在 format specifier 时避免跨行表达式、0.4.5 修复 quote-style = preserve 下的多行引号误报、0.4.7 修正 stub 函数尾部注释的放置、0.4.9 修复纯零宽字符行的格式化不稳定。相关实现与快照分布在 crates/ruff_python_formatter 及其 tests/snapshots 中。

如何验证与跟进这些能力

  • 逐规则行为验证:每条规则都配有快照测试,例如 RUF029crates/ruff_linter/src/rules/ruff/snapshots/ 中的 unused-async_RUF029.py.snap。运行 cargo test 即可在本地复现这些输出。
  • 预览规则开关:0.4.x 中新规则默认处于预览状态,需通过 --preview 标志或配置开启;规则实现的元数据(如 preview_sincestable_since、类别)直接在源码的 ViolationMetadata 中标注,可作为判断“某规则何时引入/转正”的第一手依据。例如 RUF029 标为 preview_since = "v0.4.0",而 FURB116 的当前源码已标为 stable_since = "0.13.0"——可见 0.4.3 只是它的预览起点,后续版本才转稳定。
  • 语言服务器:直接阅读 crates/ruff_server/README.mdcrates/ruff_server/src/server.rs,可看到命令注册、code action 映射与配置发现逻辑;编辑器侧指南统一收录于 docs/editors/
  • 配置项全集:CLI 参数与环境变量的权威来源是 crates/ruff/src/args.rs(含 RUFF_OUTPUT_FILE 等 env 绑定),文档化版本见 docs/configuration.md

小结

Ruff 0.4.x 在 11 个版本中完成了三项奠基性工作:解析器重写让全链路的性能底座更硬;ruff server 从 Alpha 走到 Beta,把 LSP 能力收敛进主二进制并逐步覆盖多编辑器与 Notebook 场景;规则体系的大规模扩容与修正则让 lint 在 pylint/pyi/refurb 等生态上的覆盖率显著提升。对于升级决策,0.4.6 的 GitLab fingerprint 与 Windows 10 最低版本两项破坏性变更值得优先评估;对于规则使用者,记住“预览规则需显式开启、行为以源码元数据与快照为准”能帮你少踩升级的坑。

登录后查看全文
热门项目推荐
相关项目推荐