Ruff 0.11.x 版本演进全解析:Python 版本推断新机制、PGH004 稳定化与 Compile-time Syntax Errors 体系
本文围绕 Ruff 官方变更记录 changelogs/0.11.x.md 展开,完整覆盖 0.11.0 至 0.11.13 共 14 个版本的核心变更。读完本文,你将掌握 0.11.0 中 requires-python 推断这一破坏性变更的确切语义与源码实现、PGH004 稳定化后的检测范围,以及贯穿整个 0.11.x 系列的 compile-time syntax errors(编译期语法错误)检测体系是如何逐版本铺开的,并了解 --exit-non-zero-on-format 等 CLI 新选项的实际行为。
0.11.0 的由来:一次发布流程修正
0.11.0 并不是一个常规的迭代版本,而是对 0.10.0 的补发(follow-up release)。变更记录开篇明确说明:由于发布流程中的一个失误,requires-python 推断相关的变更没有包含进 0.10.0,因此 Ruff 0.11.0 补上了这一变更,同时完成了 PGH004 preview 行为的稳定化。也就是说,升级 0.11.0 时实际引入的“新行为”有两块:Python 版本推断机制的变更(破坏性变更)与 blanket-noqa 检测范围的扩大。
破坏性变更:target-version 缺失时的版本推断
在此之前,指定 Python 版本只有两条途径:
- 在
ruff.toml(或pyproject.toml的[tool.ruff]段)中显式配置target-version; - 在带有
[tool.ruff]段的pyproject.toml中依赖project.requires-python字段。
问题出在 Ruff 的配置发现机制上:一个不含 [tool.ruff] 段的 pyproject.toml 会被整个忽略,其中的 requires-python 也随之被忽略,Ruff 会回退到默认 Python 版本(记录写作时为 3.9)——当你明明声明了目标版本时,这个行为出人意料。
0.11.0 更新后的配置发现规则如下(变更记录原文列出三条,当前 docs/configuration.md 中“Inferring the Python version”一节的完整表述为四条):
- 如果直接传入了配置文件,Ruff 不会尝试推断缺失的
target-version; - 如果在文件系统层级中发现配置文件(如
ruff.toml没有target-version),Ruff 会检查同一目录下的pyproject.toml,并尊重其requires-python字段——即使该文件不含[tool.ruff]段; - 如果使用用户级配置(user-level configuration),则取父目录中最近的
pyproject.toml的requires-python字段优先; - 如果被检查文件所在目录没有任何配置文件(
ruff.toml或带[tool.ruff]段的pyproject.toml),Ruff 会向上查找最近的pyproject.toml并使用其requires-python。
源码实现:apply_fallbacks 与回退版本推导
从源码结构看,这套逻辑的落点在 ruff_workspace crate:
- configuration.rs 中的
apply_fallbacks方法:当配置来源为ConfigurationOrigin::Ancestor(即从父目录层级发现)且target_version为空时,调用pyproject::find_fallback_target_version(dir)推导回退版本; - pyproject.rs 中的
find_fallback_target_version沿路径祖先目录逐级查找,找到第一个含requires-python的pyproject.toml即返回; - 关键的版本提取逻辑在 pyproject.rs 的
get_minimum_supported_version:它从VersionSpecifiers中只筛选==、===、~=、>=、>等下界类操作符,取各操作数版本截断到 major.minor 后的最小值,再映射到PythonVersion枚举。例如requires-python = ">=3.13"会推导出py313。
需要注意的细节:load_options(pyproject.rs)在解析 pyproject.toml 时,如果其中已显式给出 target_version,则显式配置优先,不会再去读 requires-python。也就是说,target-version 依旧是“细粒度控制”的第一选择,推断只作为兜底。
PGH004(blanket-noqa)稳定化
blanket-noqa 规则(PGH004)在 0.11.0 稳定化,行为变化是:同时检测文件级的 blanket noqa 注释,而不仅仅是行级注释。即 # noqa 整行/整文件地关闭所有规则的行为现在也会被告警。配合 0.11.5 中 RUF100 对“带具体规则码的未使用文件级 noqa 指令”的检测修复,0.11.x 系列对 noqa 卫生管理的整体覆盖明显增强。
贯穿系列的 Preview 主线:compile-time syntax errors
0.11.x 系列最具系统性的一条线索是 [syntax-errors] 类别的 preview 功能——把 CPython 在编译期才会报错的语法问题,前移到 lint 阶段检测。按版本梳理其演进:
- 0.11.1:一批针对“低版本 Python 使用高版本语法”的检测先行落地,包括 PEP 701 f-string 在 Python 3.12 之前、括号化上下文管理器(PEP 634/614 相关)在 3.9 之前、星号注解(PEP 646)在 3.11 之前、
for语句迭代子句中的元组解包在 3.9 之前等;同时改进了 pre-PEP-614 装饰器语法错误的消息与范围(range); - 0.11.2:修复 3.11 之前可变参数(variadic)注解上的误报;
- 0.11.3:正式宣布 Start detecting compile-time syntax errors(开始检测编译期语法错误),并补充 match 模式相关问题(映射模式重复键、类模式重复属性、
case模式中多重赋值等)、__debug__赋值/删除检测; - 0.11.4:补充注解(annotations)中的非法语法检测、
match模式中的重复键/属性;同期还实现了RUF102(invalid-rule-code); - 0.11.5:将注解检查扩展到
await表达式与带注解赋值,新增同步推导式中出现异步推导式的检测; - 0.11.8:新增模块级
nonlocal声明、单星号赋值x = *y检测,并将“未加括号的 except 元组”语法错误限定为 3.14 之前的行为; - 0.11.9:两项关键调整——版本相关的语法错误默认按最新受支持 Python 版本判定,以及为 Python 3.14 实现 deferred annotations;
- 0.11.13(其他变更):parser 与 formatter 双双支持 Python 3.14 的 t-strings(模板字符串)。
这条线索的意义在于:Ruff 的 Rust 解析器在解析成功之后,还维护了一套 semantic_errors 检查层(可参见 ruff_python_parser 与 error.rs 的目录结构),0.11.x 系列把它逐步暴露为可启用的 preview 诊断,让“这份代码在目标 Python 版本下根本编译不过”的问题在 lint 阶段即可被捕获。由于均处于 preview 状态,启用方式是在配置或 CLI 中开启 preview 模式;正式稳定化前的行为仍可能调整。
值得逐条关注的规则与修复变更
Airflow 规则(AIR3xx)的大规模演进
flake8-airflow 插件是 0.11.x 中改动最密集的插件,核心是 Airflow 3 的模块迁移检测(AIR301/AIR302 等):从 0.11.1 起陆续补充 chain、chain_linear、cross_downstream 检测,0.11.3 引入 AIR312 拆分、规则编号多次迁移(AIR301→AIR002、AIR302→AIR301、AIR303→AIR302 等,使用这些编码时注意版本差异),0.11.10–0.11.13 则持续补全 autofix(含针对 Airflow 3 中重命名场景的不安全修复)并扩展到单个符号级别的模块路径检查。如果你的项目依赖 Airflow 且使用这些规则,升级时应留意规则码映射变化。
CLI:--exit-non-zero-on-format(0.11.1)
这是一个 CI 场景下很实用的新选项:只要格式化实际修改了任何文件,即使全部文件都格式化成功,ruff format 也返回非零退出码。实现位于 args.rs,定义为 FormatCommand 的布尔参数,并带有历史别名 --exit-non-zero-on-fix。其典型用法是让 CI 以“检查+失败”的方式守护格式一致性,而不是静默修改工作区。
配置与 CLI 的其他新增
- 0.11.3:stdin 传入的
pyproject.toml现在能被正确解析检查(此前作为普通文本处理会导致误判);flake8-import-conventions的默认别名新增numpy.typing as npt; - 0.11.8:新增禁用
typing_extensions相关导入建议的选项(对应 configuration.rs 中LintConfiguration的typing_extensions字段,0.11.9 还修复了该设置缺少combine调用的 bug);配置选项中新增 Python 3.14(target-version可用py314); - 0.11.9:
ruff analyze graph允许传入虚拟环境。
规则行为变更与自动修复安全性收紧
0.11.x 系列体现了一个清晰的工程取向——把更多自动修复标记为“不安全(unsafe)”以避免破坏性变更,例如:
- 0.11.7:
FURB161(refurb)修复除整数字面量与布尔量外一律不安全;PLR1730在删除注释时不安全; - 0.11.10:
PIE804在字典含注释时修复标记为不安全; - 0.11.12/0.11.13:
UP010、UP004、UP050在删除注释时标记为不安全; - 同期也有“放宽”方向:0.11.7 中
E712(冗余布尔比较)获得自动修复,PERF401允许用推导式替换列表函数调用。
若你的 CI 使用 --fix 而非 --unsafe-fixes,这些安全性调整直接影响修复覆盖面,升级后值得对 diff 做一次抽查。
代表性 bug 修复
变更记录中篇幅最大的部分是 bug fixes,其中几类修复模式值得关注:
- 文件描述符(fd)参数导致的误报:0.11.8–0.11.10 集中修复了
flake8-use-pathlib一族规则(PTH104/PTH116/PTH123/PTH208等)在函数参数是文件描述符时仍建议Path方法的误报,0.11.10 进一步推广为“对所有带dir_fd参数的os.*函数抑制诊断”; - 自动修复产生非法代码:如
SIM905的rsplit修复会产生反转的列表字面量(0.11.10 修复)、UP018修复丢失空格或括号(0.11.7/0.11.9 修复)、FURB129修复生成非法语法(0.11.12 修复); - 稳定性:0.11.1 中 Server 端在有版本特定语法错误时允许
FixAll操作;0.11.3 中“从已删除目录运行 Ruff 时panic!”改为正常报错。
升级建议
- 如果从 0.10.x 或更早版本升级到 0.11.x,首要关注的是未配置
target-version的项目:新推断机制可能改变 Ruff 实际使用的目标版本(尤其是目录中存在pyproject.toml但无[tool.ruff]段、且声明了requires-python的场景),从而改变部分版本相关规则(如UP系列 pyupgrade 规则)的触发行为。可用ruff check --show-settings查看解析后的target-version验证; - 使用 Airflow 相关规则码的项目,需对照 0.11.3 起的规则码迁移记录核对配置;
- 依赖
ruff format退出码的 CI 脚本,可考虑改用--exit-non-zero-on-format获得“有文件被改动即失败”的语义; - preview 相关的 syntax-errors 检测在整个 0.11.x 系列中持续变动且默认不启用,建议在稳定化之前不要在非 preview 流程中依赖其输出。
本系列变更记录的完整明细(含全部 PR 编号)见 changelogs/0.11.x.md,版本推断的现行文档表述见 docs/configuration.md。
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