Ruff 破坏性变更全解:从 0.0.x 到 0.16.0 的升级避坑指南
BREAKING_CHANGES.md 是 Ruff 仓库中专门记录各版本不兼容行为变更的权威清单。本文完整梳理其中从 0.0.178 到 0.16.0 的全部破坏性变更——默认规则集、默认 Python 版本、抑制注释语法、输出格式、CLI 子命令、JSON 输出结构等核心主题,并结合当前仓库源码(如 python_version.rs 中的默认版本实现、settings/mod.rs 中解析器与规则引擎的版本分离策略)说明每项变更背后的设计意图。读完本文,你可以判断一次 Ruff 升级是否会改变自己项目的行为,并知道如何用 target-version、per-file-ignores、extend-exclude 等配置项平滑过渡。
破坏性变更的核心主题:Python 版本默认值
理解 Ruff 的破坏性变更,首先要抓住一条贯穿始终的主线:默认 Python 版本的历史演进。
- 0.0.283/0.0.284 起,默认版本从 3.10 改为当时最低支持的 3.8(3.7 虽仍受支持但已 EOL,不作默认);
- 0.8.0 起,默认版本从 3.8 改为 3.9;
- 0.14.0 起,默认版本从 3.9 改为 3.10;同时未配置版本时,语法检查默认使用最新支持版本(当时为 3.14,0.12.0 时这一角色由 3.13 担任),而 lint 规则仍按最低版本处理。
当前仓库的源码印证了 0.14.0 之后的双轨策略。python_version.rs 中 PythonVersion 的 Default 实现固定返回 PY310:
impl Default for PythonVersion {
fn default() -> Self {
Self::PY310
}
}
而在 settings/mod.rs 中,TargetVersion 包装器将“未配置版本”的两种回退路径分开:parser_version() 在未设置时回退到 PythonVersion::latest(当前为 3.14,见 python_version.rs 的 latest() 常量),用于解析和语义错误检测以“最小化版本相关诊断”;linter_version() 则回退到 PythonVersion::default()(3.10),用于版本相关的 lint 规则。这正是 0.12.0/0.14.0 中“语法错误按最新版、lint 规则按最低版”策略的实现。
适用建议:无论默认值如何变化,显式设置 target-version(或 project.requires-python)是唯一可靠的做法。从源码结构看,python_version.rs 的 JSON Schema 生成(同文件 L205 起的 schemars 模块)会把所有受支持版本枚举进 ruff.schema.json,编辑器补全即来源于此。
0.16.0:默认规则集大幅扩容与输出能力增强
这是当前仓库中记录的最重量级一次破坏性变更,共六项:
-
默认规则集从 59 条扩容到 413 条。这主要是一次扩展,但同时移除了 18 条较有争议的 pycodestyle(
E)与 pyflakes(F)规则:E401、E402、E701、E702、E703、E711、E712、E713、E714、E721、E731、E741、E742、E743、F403、F405、F406、F722。如果你此前依赖“默认 = 保守”的假设,升级到 0.16.0 后首次运行ruff check会看到大量新增诊断。 -
Markdown 文件中的 Python 代码块默认参与格式化。
ruff format现在会处理.md文件里的 Python 代码块,且默认开启。 -
ruff: ignore抑制注释。现在支持两种位置的ruff: ignore注释,功能定位与noqa类似:行尾注释(如import math # ruff: ignore[F401])或紧邻诊断的前一行注释:import math # ruff: ignore[F401] # ruff: ignore[F401] import os两者都能抑制
unused-import(F401)诊断。仓库中该注释的语法解析实现在 suppression.rs,其文档注释明确区分了“独立行注释”与“行尾注释”两类形态。 -
check与format --check输出中展示修复 diff。0.16.0 起,ruff format --check会直接打印将被改写的内容:❯ ruff format --check . unformatted: File would be reformatted --> try.md:1:1 | 1 | ```python - import math 2 + import math 3 | ``` | 1 file would be reformatted -
format --check支持与 linter 相同的输出格式,包括在 CI 中渲染注解的github与gitlab格式:❯ ruff format --check --output-format github . ::error title=ruff (unformatted),file=try.md,line=2,col=8,endLine=2,endColumn=10::try.md:2:8: unformatted: File would be reformatted完整格式列表见 CLI help 或
output-format配置文档。 -
JSON 输出中部分字段可为
null。filename、location、end_location、fix.edits[].location、fix.edits[].end_location这些字段现在可能是null,而不再回退为空字符串或“第 1 行第 1 列”。如果你用脚本消费 JSON 输出,需要兼容空值。
0.15.0 与 0.14.0:2026 风格指南、块级抑制与平台变更
0.15.0 包含五项变更:
-
2026 formatter 风格指南:
ruff format现在按 2026 版风格指南排版,具体差异见 CHANGELOG 的 formatter 部分; -
linter 支持块级抑制注释,配合
ruff: enable成对使用:# ruff: disable[N803] def foo( legacyArg1, legacyArg2, legacyArg3, legacyArg4, ): ... # ruff: enable[N803] -
Alpine Docker 镜像基于 Alpine 3.23(原 3.21);Debian 镜像(
ruff:debian与ruff:debian-slim)迁移到 Debian 13 “Trixie”(原 Debian 12 “Bookworm”); -
ppc64(64 位大端 PowerPC)预编译二进制不再随发布提供,如需仍可手动构建; -
默认 Python 版本与
extend的解析顺序:Ruff 现在先解析所有extend的配置链,再回退到默认 Python 版本——这意味着被扩展配置文件中声明的target-version会正确生效。
0.14.0 如前文所述,是默认 Python 版本升到 3.10、语法错误检查改用最新支持版本(3.14)的节点;其源码依据见 settings/mod.rs 中 parser_version() 与 linter_version() 的分离实现。
0.13.0:自动补 from __future__ import annotations 与规则移除
TC001/TC002/TC003/RUF013/UP037的修复现在会自动添加from __future__ import annotations(当lint.future-annotations开启时)。这使得这些规则可以把更多 import 移入TYPE_CHECKING块(TC00x)、在 Python 3.10 之前使用 PEP 604 联合语法(RUF013)、以及解除更多注解引号(UP037)。对依赖“修复不改写 import 头”的自动化流水线,这是一个行为变化。- first-party 模块识别改用完整模块路径:Ruff 现在会验证磁盘上完整路径存在,才把 import 归类为 first-party。这降低了本地目录与第三方包同名时的误报(FAQ 中“Ruff 如何区分 first-party / third-party”一节有详细说明)。
- 已废弃规则必须用精确规则码选择:不再能通过组名或前缀激活废弃规则。由于本版本同时移除了仅剩的两条废弃规则(
PD901pandas-df-variable-name、UP038non-pep604-isinstance),对现网配置无感,但为未来废弃立下了规则。 - 移除 macOS 配置目录回退:不再查找
~/Library/Application Support/ruff/ruff.toml。自 v0.5 起该路径就已被标记废弃,统一走 XDG 规范(通常为~/.config/ruff/ruff.toml)。
0.12.0 与 0.11.0:语法错误检测增强与发布事故补救
0.12.0:
- 更多版本相关语法错误:如 Python 3.10 之前使用
match语句,以及 CPython 编译器会报告的“最后一个case分支前出现不可证伪match模式”等错误; - 未配置版本时,这类语法错误检查默认使用最新支持版本(当时为 3.13)以避免误报,lint 规则默认值不变(3.9);
- f-string 格式化更新:多行 f-string 带格式化说明符时不再在说明符后换行——Python 3.13.4 的语法变更使这种换行成为语法错误;
rust-toolchain.toml不再包含在源码发行包中:它原本用于开发/发版时指定高于 MSRV 的 Rust 版本,但留在 sdist 里会迫使下游打包者拉取相同工具链,即使其本地工具链满足 MSRV;- 移除
S320(suspicious-xmle-tree-usage)规则。
0.11.0 是对 0.10.0 的补救发布:由于发布流程失误,requires-python 推断变更未随 0.10.0 上线,0.11.0 补齐了它,并稳定了 PGH004 的预览行为。核心变更是未指定 target-version 时 Python 版本的推断方式(PR #16319):
- 此前:只有含
[tool.ruff]段的pyproject.toml的requires-python才会被读取;不含该段的pyproject.toml会被忽略,Ruff 回退到默认版本(当时 3.9),与用户预期不符; - 新行为:
- 找到不含
target-version的ruff.toml时,检查同目录pyproject.toml的requires-python(即使没有[tool.ruff]段); - 使用用户级配置时,最近父目录
pyproject.toml的requires-python优先; - 被检查文件目录内没有任何配置文件时,向父目录查找最近的
pyproject.toml并使用其requires-python。
- 找到不含
0.10.0、0.9.0 与 0.8.0:TYPE_CHECKING、noqa 语义与安装路径
0.10.0:
TYPE_CHECKING识别更宽泛:此前只认typing.TYPE_CHECKING符号,现在任意名为TYPE_CHECKING的局部变量都算;同时移除对if 0:/if False:这类旧式类型检查块的识别(PR #16669)。# noqa: TC00x等注释若依赖旧块语法,需要迁移到本地TYPE_CHECKING变量;- noqa 解析更健壮(PR #16483):文件级与行内抑制注释语法统一并对若干错误更宽容。多数情况下会“多读”注释,但个别此前能被读取的注释现在会向用户报错;
- with 语句括号修正(PR #14005):formatter 不再为“单个上下文管理器 + 行尾注释”的 with 语句添加多余括号,可能导致少量既有文件重排;
ruff:alpine默认标签升到 3.21,ruff:alpine3.20停止更新(PR #16456);RUF035(unsafe-markup-use)改码为S704(PR #15957)——按安全类规则统一编号。
0.9.0 是一次风格指南大版本:formatter 按 2025 风格指南排版,代码可能因此被重新格式化。详细差异见 CHANGELOG.md。
0.8.0:
- 默认 Python 版本 3.8 → 3.9;
- pydoclint 诊断位置变化:
pydoclint规则的诊断现在指向问题 docstring 的首行。若你在预览期启用过这些规则并用noqa压制,可能需要移动注释位置; - 独立安装脚本改用 XDG 路径:不再用
$CARGO_HOME/~/.cargo/bin,安装目标按顺序取$XDG_BIN_HOME、$XDG_DATA_HOME/../bin、~/.local/bin。用 uv 或 pip 安装的用户不受影响; - 行宽计算换用新版 unicode-width crate:极罕见情况下,含 Unicode 字符的行可能被重排或被
E501新判为超长。
0.7.0 与 0.6.0:规则改码与 Notebook 默认开启
0.7.0:
- pytest 规则
PT001、PT023默认省略无参装饰器的括号(此变更在 0.6.0 中因失误只部分落地,0.7.0 补全); useless-try-except规则从TRY302改码为TRY203,与上游 tryceratops linter 编号保持一致;lint.allow-unused-imports配置项被移除,改用lint.pyflakes.allow-unused-imports。
0.6.0 是 Notebook 用户的关键节点:
-
isort规则默认识别src布局下的 import; -
PT001/PT023无参括号省略(见上); -
默认 lint 并 format Jupyter Notebook 文件。按需用配置收敛:
[tool.ruff.lint.per-file-ignores] "*.ipynb" = ["E501"] # disable line-too-long in notebooks只想 lint 不想 format:
[tool.ruff.format] exclude = ["*.ipynb"]反过来只 format 不 lint:
[tool.ruff.lint] exclude = ["*.ipynb"]完全禁用 Notebook 支持:
[tool.ruff] extend-exclude = ["*.ipynb"]
0.5.0 则包含:macOS 遵循 XDG 规范发现用户级配置(与其他 Unix 平台一致);ALL 选择器排除废弃规则;发布压缩包多一层目录嵌套(tar 时用 --strip-components=1);发布产物文件名不再含版本号,从而支持 /latest 直链安装。
0.3.0 及更早:默认排除列表与 CLI/JSON 结构的定型
0.3.0 有三条值得注意的变更:
-
formatter 切换到 Ruff 2024.2 风格指南,稳定化差异见 CHANGELOG.md;
-
stub 文件(
.pyi)import 后固定一个空行:此前为 1~2 个空行(或isort.lines-after-imports配置值),现统一为 1 个,与 formatter 行为一致; -
build目录不再默认排除。默认排除列表(不含build)如下:.bzr、.direnv、.eggs、.git、.git-rewrite、.hg、.ipynb_checkpoints、.mypy_cache、.nox、.pants.d、.pyenv、.pytest_cache、.pytype、.ruff_cache、.svn、.tox、.venv、.vscode、__pypackages__、_build、buck-out、dist、node_modules、site-packages、venv
原因:
build是较常见的目录名,默认排除反而造成困惑。现在只有当.gitignore排除它或你自行写入extend-exclude时才会被跳过; -
ruff rule与ruff linter不再接受--format别名,请改用--output-format。
0.1.9 把 site-packages 加入默认排除(此前依赖 .venv 间接排除,但 VS Code 在非虚拟环境中运行时可能失效),列表即上表。
0.1.0 有三项结构性变更:
- 废弃的
format设置移除:format设置、--formatCLI 选项、RUFF_FORMAT环境变量不再用于输出格式(自 v0.0.291 起已弃用,format语义已转给代码格式化)。输出格式请用output-format设置 /RUFF_OUTPUT_FORMAT环境变量 /--output-format选项; - unsafe 修复默认不再应用:Ruff 把修复分为 safe/unsafe,unsafe 修复此前会展示并应用,现在默认隐藏且不应用,需
--unsafe-fixes标志或unsafe-fixes配置显式开启。这是 CI 中“升级后修复数量骤降”的最常见原因; - 移除与 formatter 冲突的规则:默认 pycodestyle 规则从整个
E前缀收窄为E4、E7、E9三个前缀,E501(line-too-long)、E101(mixed-spaces-and-tabs)等离开默认集。显式select = ["E"]的用户不受影响。
更早版本中的关键条目(均为 CLI 契约层面的收缩):
-
0.0.288:移除 emoji 标识符(如
📦 = 1)支持,现在与 CPython 一致地报语法错误;GitLab 输出改用不依赖位置的指纹,避免“前置代码一改就误报一修一新”(升级 PR 中所有存量违规会各报一次“已修复 + 新增”,属预期); -
0.0.283/0.0.284:默认目标版本 3.10 → 3.8(0.0.283 宣布、0.0.284 生效);
-
0.0.277:
.ipynb_checkpoints、.pyenv、.pytest_cache、.vscode加入默认排除,与 Black 等工具对齐; -
0.0.276 / 0.0.268:
keep-runtime-typing先移除(等价于忽略UP006/UP007),后以更严格语义恢复——仅对 Python 3.7/3.8 生效,防止 Ruff 生成 Pydantic/FastAPI 等运行期解析注解的库不支持的list[int]标注; -
0.0.267:
update-check彻底移除,配置或 CLI 中再出现该选项会直接报错; -
0.0.265:
--fix-only退出码语义与--fix对齐——默认返回 0,仅在显式--exit-non-zero-on-fix且确有修复时非零; -
0.0.260:修复(fix)表示为编辑列表。JSON 输出的
fix字段从单个 edit 变为edits数组,支持一次违规跨多处编辑:{ "message": "Remove unused import: `sys`", "edits": [ { "content": "", "location": {"row": 1, "column": 0}, "end_location": {"row": 2, "column": 0} } ] } -
0.0.246 / 0.0.245:移除
E704(pycodestyle 与 Flake8 默认也忽略它);移除未文档化的公开 RustcheckAPI,为未来稳定 API 留余地; -
0.0.238:
select/extend-select/ignore/extend-ignore语义重定义(PR #2312)——以“最高优先级来源”(CLI > 当前pyproject.toml> 继承的pyproject.toml)的select为基线,再叠加extend-select/ignore/extend-ignore。典型破坏点:配置了ignore = ["F401"]时,ruff --select F从“F 全部但排除 F401”变为“F 全部(含 F401)”,因为 CLI--select会重置解析;同版本移除UP016(remove-six-compat); -
0.0.237:
--explain、--clean、--generate-shell-completion转为子命令ruff rule/ruff clean/ruff generate-shell-completion。注意:子命令遇到不支持的参数会直接失败(不再静默忽略);ruff rule等词同时是有效文件/目录名时语义改变;脚本调用位置参数前建议加--; -
0.0.226 / 0.0.225:
PLC2201废弃并推荐SIM300;@functools.lru_cache(maxsize=None)→@functools.cache的重写从UP011拆出为独立规则UP033(相关noqa注释需改码); -
0.0.222:
--max-complexity从 CLI 移除,改在配置中设置:[tool.ruff.mccabe] max-complexity = 10 -
0.0.181:被
.ignore、.gitignore、.git/info/exclude及全局 gitignore 排除的文件默认不检查(由ignorecrate 驱动,叠加在内置exclude之上);关闭方式:respect-gitignore = false。注意以.开头的隐藏文件不默认忽略; -
0.0.178:配置解析层级化——每个 Python 文件按路径找到第一个
pyproject.toml并以其[tool.ruff]段为准,这是后续所有“配置发现”行为(包括 0.11.0 的requires-python推断)的基础。
升级实践:如何安全地跨过这些版本
综合以上各节的破坏性变更类型,升级 Ruff 时建议按以下顺序验证:
- 显式固定 Python 版本:
target-version或requires-python写清楚,避免 3.8 → 3.9 → 3.10 的默认值漂移影响UP/TC类规则判定; - 审视输出消费方:JSON 输出从单 edit 变 edit 列表(0.0.260)、字段可为
null(0.16.0)、GitLab 指纹变化(0.0.288)——消费这些输出的脚本与 CI 集成应同步审查; - 对比修复行为:
unsafe-fixes默认关闭(0.1.0)意味着自动化流水线需要显式开启;0.13.0 起TC/RUF013/UP037的修复可能连带插入from __future__ import annotations; - 核对规则码:
TRY302→TRY203、RUF035→S704、UP011→UP033(部分)等改码,以及PD901、UP038、S320、E704、UP016的移除,会影响select/ignore/noqa中写死的规则码; - 注意范围变化:0.6.0 起 Notebook 默认纳入、0.16.0 起 Markdown 代码块默认格式化、
build/site-packages等默认排除列表多次调整——先用ruff check --show-files/ruff format --check对比新旧版本的检查范围与结果,再决定是否用extend-exclude、per-file-ignores、分节exclude收敛行为。
BREAKING_CHANGES.md 本身按版本倒序组织(新 → 旧),配合 CHANGELOG.md 的逐版本细节,构成了升级决策的完整证据链:本文引用的默认版本实现(python_version.rs)、解析/规则双版本策略(settings/mod.rs)与 ruff: ignore 注释解析(suppression.rs)都可以在当前仓库中直接查证。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00