深入解析 Ruff 的版本管理策略:自定义版本方案、Preview 模式与稳定化机制
【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff
导读
Ruff 是一款用 Rust 编写的极速 Python 静态检查工具(linter)与代码格式化工具(formatter)。在其 API 尚未稳定之前,Ruff 采用了一套自定义的版本方案:用minor 版本号承载破坏性变更、用patch 版本号承载 bug 修复,并配套 Preview(预览)模式、规则与修复的稳定化流程,以及一套服务于 VS Code 扩展的独立版本区分规则。本文以仓库官方文档 docs/versioning.md 为骨架,结合源码逐一拆解这套版本策略的每一项约定,帮助你准确判断某个升级是否可能破坏现有配置、理解新规则与新修复的引入节奏,以及正确使用 preview 模式参与社区反馈。
一、总体设计:为什么 Ruff 不直接使用语义化版本
Ruff 目前采用自定义版本方案,核心约定是:
- minor(次版本号):用于引入破坏性变更(breaking changes);
- patch(修订号):用于修复 bug和向后兼容的增量改进;
- major(主版本号):尚未使用。Ruff 目前没有稳定的 API,一旦 API 稳定,将切换到完整的语义化版本(SemVer)方案。
也就是说,在当前阶段,0.x.y中的x(minor)承担了传统语义化版本中 major 的角色——升级 minor 版本时应当预期可能发生破坏性变化,而升级 patch 版本通常可以安全进行。
二、Crate 版本策略:哪些 crate 遵循正常策略
Ruff 以 Cargo workspace 形式组织代码(见 Cargo.toml 中的[workspace]与[workspace.dependencies]配置),各 crate 独立发布。根据官方策略,只有以下三个 crate 遵循 Ruff 的正常版本策略:
ruff(对应 crates/ruff)ruff_linter(对应 crates/ruff_linter)ruff_wasm(对应 crates/ruff_wasm)
需要特别注意:即使这三个 crate 遵循正常版本号,其 Rust 接口也不遵循语义化版本。也就是说,依赖这些 crate 的 Rust 程序,无法通过版本号判断 API 是否兼容。
其余随 Ruff 一起发布的 crate(如ruff_python_parser、ruff_python_ast、ruff_python_formatter、ruff_diagnostics、ruff_text_size等)不提供任何稳定性保证:
- 它们的 Rust 接口被视为内部实现、不稳定;
- 它们统一以
0.0.x版本发布; - 每发布一次 Ruff 版本,这些 crate 的 patch 号就递增一次,无论 crate 本身是否有任何改动。
在 Cargo.toml 的[workspace.dependencies]中可以观察到这一现象:当前仓库中ruff与ruff_linter的版本为0.16.6,而ruff_cache、ruff_db、ruff_diagnostics、ruff_python_parser等大量内部 crate 均为0.0.12。
三、Minor 版本:何时会发生破坏性变更
官方明确列出,以下情况将触发minor 版本号提升:
- 移除某个已废弃(deprecated)的选项或特性;
- 配置发生向后不兼容的变化——文档特别说明:在
1.0.0之前,这类变更"可能"出现在 minor 版本中,但一般应尽量避免; - 对某类新文件类型的支持被提升为稳定(stable);
- 停止支持某个已进入生命周期终点(EOL)的 Python 版本;
- Linter 相关:
- 某条规则被提升为稳定;
- 某条稳定规则的行为被改变,包括:
- 稳定规则的作用范围被显著扩大;
- 规则的意图(intent)发生变化;
- 注意:遵循规则原始意图的 bug 修复不属于此类;
- 稳定规则被加入默认启用集合(default set);
- 稳定规则被从默认启用集合中移除;
- 某条规则的 safe fix 被提升为稳定;
- 某条规则被废弃(deprecated);
- Formatter 相关:
- 稳定格式化风格(stable style)发生变化;
- Language server 相关:
- 移除某个已有能力(capability);
- 移除某个已废弃的 server 设置。
简而言之:升级 minor 版本前,务必阅读 changelog 与迁移指南。仓库根目录的 BREAKING_CHANGES.md 以及 CHANGELOG.md、changelogs 目录下的分版本 changelog(如 0.16.x 等各系列)都可用于核对每个版本的实际变更。
四、Patch 版本:哪些变化是安全的
以下情况触发patch 版本号提升,这类升级通常不会破坏既有配置:
- 修复 bug,包括为修复 bug 而改变行为(这类"行为修正"被明确允许在 patch 中发生);
- 以向后兼容的方式新增配置选项(不产生格式化变化,也不产生新的 lint 错误);
- 新增对某个 Python 版本的支持;
- 在 preview 模式下新增对某类文件类型的支持;
- 废弃(deprecate)某个选项或特性(注意:废弃与移除不同,移除属于 minor 变更);
- Linter 相关:
- 为规则新增 unsafe fix;
- 在 preview 模式下为规则新增 safe fix;
- 在 preview 模式下扩大规则的作用范围;
- 降低某个 fix 的适用性(applicability);
- 在 preview 模式下新增规则;
- 改变某条 preview 规则的行为;
- Formatter 相关:
- 稳定风格发生变化,但仅限于修复以下问题:防止生成无效语法、改变程序语义,或删除注释;
- preview 风格发生变化;
- Language server 相关:
- 新增对某个新能力的支持;
- 新增 server 设置;
- 废弃某个 server 设置。
由此可见,patch 升级中"行为变化"的空间被严格限定:要么是 bug 修复,要么是 preview 模式下的实验性调整,要么是保证语义等价与注释安全的格式化修正。这与第一节的总体设计完全一致。
五、最低支持的 Rust 版本(MSRV)
编译 Ruff 所需的最低 Rust 版本(MSRV)记录在仓库根目录 Cargo.toml 的[workspace.package]段落的rust-version键中,该值可能在任意一次发布(minor 或 patch)中变化。当前仓库中该值为:
[workspace.package] edition = "2024" rust-version = "1.96"官方对 MSRV 的约束是:永远不会比最新稳定 Rust 版本新出超过 2 个版本。即如果最新稳定 Rust 是1.85,则 Ruff 的 MSRV 至多为1.83(公式:MSRV ≤ N-2,N 为最新稳定版本)。
这一点只对从源码构建 Ruff 的用户有意义。从 Python 包索引(PyPI)安装 Ruff 通常安装的是预编译二进制,不需要本机编译 Rust。因此普通用户一般无需关注 MSRV;只有自行执行cargo build --release等源码构建流程时,才需要确保本机 Rust 工具链版本满足要求。Rust 工具链版本由仓库根目录的 rust-toolchain.toml 约束。
六、Preview 模式:提前体验不稳定能力
6.1 设计意图
Ruff 提供了preview(预览)模式,用于启用新的、尚未稳定的规则与特性(例如对某类新文件类型的支持)。官方文档明确了 preview 模式的两个定位:
- 目的:收集社区反馈,确认改动是净收益(net-benefit);
- 定位边界:它不是用来限制"未完成的工作"或"我们很可能移除的特性"。
但官方同时保留了重要权利:可以更改任何由 preview 模式门控的行为,包括直接移除 preview 特性或规则。这意味着 preview 模式下的任何规则、修复或风格都不具备稳定性承诺,升级时可能随时变动。
6.2 配置入口与源码实现
在配置中,preview是一个布尔开关。从 crates/ruff_workspace/src/configuration.rs 的源码可以看到:
- 配置解析后映射到
PreviewMode枚举(定义于 crates/ruff_linter/src/settings/types.rs),取值为Enabled/Disabled; preview既可以在全局层级配置(self.preview),也可以在lint、format、analyze等子配置块中单独覆盖,子配置未设置时回退到全局值(如lint.preview.unwrap_or(global_preview));- 全局 preview 开启时,还会联动影响文件包含/排除模式的默认集合(
INCLUDE_PREVIEW),并在规则表解析、格式化器 preview 风格、analyze 的 preview 行为等多个环节生效。
典型的启用方式(以pyproject.toml为例):
[tool.ruff] preview = true若只想让 linter 或 formatter 单独启用预览,可以分别在对应子块设置:
[tool.ruff.lint] preview = true [tool.ruff.format] preview = true此外,源码中大量以is_*_enabled(settings)命名的辅助函数(见 crates/ruff_linter/src/preview.rs)为每条具体规则独立判断 preview 是否生效。该文件的文档注释说明了一个设计巧思:这些命名函数便于在规则从 preview 提升到 stable 时直接删除函数,然后让 Rust 编译器指出所有需要清理的调用点。
七、规则稳定化(Rule Stabilization)流程
官方对新规则与既有规则的处理,遵循以下指导原则:
- 新规则必须先在 preview 模式下引入;
- 新规则至少要在 preview 模式中停留一个 minor 版本,才能被提升为 stable。文档给出的关键示例:
- 若规则在 patch 版本
0.6.1加入,则最早要到0.8.0才具备稳定化资格(因为0.6.1的下一个 minor 是0.7.0,而0.6.1所在 minor 序列的"下一 minor"计数规则要求跳过0.7.0直接看0.8.0);
- 若规则在 patch 版本
- 稳定规则的行为不应在 patch 版本中被显著改变;
- 规则的稳定化可能被延迟,以便将多条规则"打包"进同一次 minor 发布(批量提升);
- 并非所有 preview 规则都必须在某次 minor 发布中完成提升。
这套流程解释了为什么 Ruff 的 changelog 中经常出现"将若干规则从 preview 提升为 stable"的批量条目——这正是第 4 条策略的体现。结合上一节的 preview 机制,规则的生命周期可以概括为:新增(preview)→ 至少一个 minor 版本观察 → (可批量)提升为 stable。
八、修复稳定化(Fix Stabilization):三级适用性
Ruff 的自动修复(fix)分为三个适用性级别:
| 级别 | 含义 | 何时应用 |
|---|---|---|
| Display(显示) | 永不应用,仅展示给用户 | 仅作为提示展示 |
| Unsafe(不安全) | 需要用户显式选择加入后才应用 | 可能不是用户本意,或可能改变运行时行为 / 删除注释 |
| Safe(安全) | 可以自动应用 | 确定符合用户意图,或保持代码语义不变 |
这套模型在源码中有精确对应:Applicability枚举定义于 crates/ruff_diagnostics/src/fix.rs(第 14–32 行),三个变体DisplayOnly、Unsafe、Safe的文档注释与上表语义一致,并且Fix::applies通过self.applicability >= applicability的比较来决定某级别下该修复是否生效。
关于修复稳定化的版本约定:
- 修复可以以较低适用性引入,随后提升到更高适用性(如 Unsafe → Safe);
- 降低某个修复的适用性不构成破坏性变更(因此出现在 patch 升级的合法变更清单中);
- 某个修复的适用性可能因preview 模式开启与否而变化。
对照第三节、第四节的清单可以看到:为规则新增 unsafe fix、在 preview 下新增 safe fix、降低 fix 适用性都属于 patch 变更;而将某个 safe fix 提升为 stable 则属于 minor 变更。
九、VS Code 扩展的特殊版本方案
VS Code 官方对扩展的 pre-release(预发布)支持存在限制(具体见 VS Code 扩展发布文档中关于 prerelease extensions 的说明)。为了在不依赖 pre-release 标签的情况下区分稳定版与预览版,Ruff 采用了"偶数/奇数 minor 版本号"方案:
- 稳定版:minor 版本号使用偶数,例如
2024.30.0、2024.32.0、2024.34.0……; - 预览版:minor 版本号使用奇数,例如
2024.31.0、2024.33.0、2024.35.0……。
这与 Ruff 主程序0.x.y的版本号体系相互独立,仅用于 VS Code 扩展的发布渠道管理。对于希望在 VS Code 中优先体验新规则/新特性的用户,可以选择奇数 minor 的扩展版本。
十、实践要点小结
结合本文的全部约定,面向不同角色的使用建议可以归纳为:
普通用户(通过 pip/uv/Homebrew 等安装预编译包):
- 升级patch版本通常安全,但仍建议阅读 changelog,因为"修复 bug 的行为变化"被允许在 patch 中发生;
- 升级minor版本前,重点核对 BREAKING_CHANGES.md 与对应版本的 changelogs,确认是否存在配置变更、稳定规则行为改变或 EOL Python 版本停止支持等破坏性变更;
- 不需要关心 MSRV,除非从源码自行编译。
希望提前验证新规则的开发者:在配置中开启
preview = true,但需要接受 preview 规则/行为随时可能变动甚至被移除的事实,不要将其作为长期依赖。规则与修复的贡献者:新规则一律从 preview 引入;修复按 Display → Unsafe → Safe 的适用性阶梯推进,Safe 的提升属于 minor 变更;规则稳定化至少等待一个 minor 版本并可批量进行。
VS Code 用户:偶数 minor 为稳定渠道、奇数 minor 为预览渠道,按需选择。
通过理解这套版本约定,你就能把 Ruff 的每次升级风险控制在可预期的范围内,并充分利用 preview 模式参与到新特性的反馈与验证中。
参考文档与源码路径
- 本文主体依据:docs/versioning.md
- MSRV 定义:Cargo.toml(
[workspace.package].rust-version,当前为1.96) - 版本号信息生成:crates/ruff/src/version.rs(
VersionInfo与 git 提交信息格式化) - Preview 配置解析:crates/ruff_workspace/src/configuration.rs、crates/ruff_linter/src/settings/types.rs
- Preview 门控辅助函数:crates/ruff_linter/src/preview.rs
- Fix 适用性枚举:crates/ruff_diagnostics/src/fix.rs
- 历史变更记录:CHANGELOG.md、changelogs、BREAKING_CHANGES.md
【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考