Ruff UP051 规则深度解析:deprecated-abc-decorator自动迁移废弃的 abc 装饰器
【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff
本文以 Ruff 仓库中的 mdtest 测试文档crates/ruff_linter/resources/mdtest/pyupgrade/deprecated-abc-decorator.md为主体,结合 pyupgrade 规则族的源码实现,完整讲解UP051(deprecated-abc-decorator)规则的启用配置、诊断与自动修复行为,以及其底层的限定名解析与导入处理机制。读完后你将掌握如何为旧版@abc.abstract*装饰器配置自动化迁移,并理解 Ruff 规则从 AST 检查到 safe fix 生成的完整链路。
规则背景:Python 3.3 起废弃的abc别名装饰器
Python 的abc模块自 3.3 版本起废弃了三个专用抽象装饰器:
abc.abstractclassmethodabc.abstractstaticmethodabc.abstractproperty
它们的推荐替代方式是叠加普通装饰器与abc.abstractmethod。官方文档注释中给出的迁移示例如下(来自规则自身的 doc 注释,见 deprecated_abc_decorator.rs):
迁移前:
import abc class Foo(abc.ABC): @abc.abstractclassmethod def class_method(cls, arg1): ... @abc.abstractstaticmethod def static_method(arg1): ... @abc.abstractproperty def prop(self): ...迁移后:
import abc class Foo(abc.ABC): @classmethod @abc.abstractmethod def class_method(cls, arg1): ... @staticmethod @abc.abstractmethod def static_method(arg1): ... @property @abc.abstractmethod def prop(self): ...Ruff 的 pyupgrade 规则族将其编码为UP051规则(DeprecatedAbcDecorator),规则与代码的映射登记在 codes.rs:
(Pyupgrade, "051") => rules::pyupgrade::rules::DeprecatedAbcDecorator,从违规元数据可以看到,该规则自0.15.21版本进入 preview 通道,分类为Suspicious(可疑用法):
#[violation_metadata(preview_since = "0.15.21", category = Category::Suspicious)] pub(crate) struct DeprecatedAbcDecorator { from: &'static str, to: &'static str, }启用配置:preview 模式下的 select
mdtest 文档头部的 TOML 配置块完整定义了该规则的启用方式:
[lint] preview = true select = ["UP051"]两个要点需要注意:
preview = true是必需的。因为UP051仍处于 preview 阶段(preview_since = "0.15.21"),不开启 preview 时select该规则不会生效。规则稳定后此限制会解除,当前使用时必须以仓库实际状态为准。select = ["UP051"]按规则 ID 精确启用。也可以用前缀select = ["UP"]启用整个 pyupgrade 规则族,但会连带开启大量其他规则;针对单一迁移目标,精确选择UP051更可控。
诊断输出与自动修复效果
mdtest 文档的核心用例是“基础替换”(Basic replacements)场景,三个废弃装饰器被完整覆盖:
import abc class Foo(abc.ABC): @abc.abstractclassmethod # snapshot: deprecated-abc-decorator def class_method(cls, arg1): ... @abc.abstractstaticmethod # snapshot: deprecated-abc-decorator def static_method(arg1): ... @abc.abstractproperty # snapshot: deprecated-abc-decorator def prop(self): ...文档内嵌的快照块逐条记录了 ruff 对该片段的诊断与修复 diff。以第一个装饰器为例:
error[UP051]: Use `@classmethod` and `@abstractmethod` instead of `abstractclassmethod` --> src/mdtest_snippet.py:4:5 | 4 | @abc.abstractclassmethod # snapshot: deprecated-abc-decorator | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Replace with `@classmethod` and `abstractmethod` | 3 | class Foo(abc.ABC): - @abc.abstractclassmethod # snapshot: deprecated-abc-decorator 4 + @classmethod 5 + @abc.abstractmethod # snapshot: deprecated-abc-decorator 6 | def class_method(cls, arg1): ... |三条诊断分别对应:
| 原装饰器 | 诊断消息(message) | 修复结果 |
|---|---|---|
@abc.abstractclassmethod | Use@classmethodand@abstractmethodinstead ofabstractclassmethod | 原行拆为@classmethod+@abc.abstractmethod两行 |
@abc.abstractstaticmethod | Use@staticmethodand@abstractmethodinstead ofabstractstaticmethod | 原行拆为@staticmethod+@abc.abstractmethod两行 |
@abc.abstractproperty | Use@propertyand@abstractmethodinstead ofabstractproperty | 原行拆为@property+@abc.abstractmethod两行 |
修复语义上等价于在方法上“叠加”两个装饰器:先应用classmethod/staticmethod/property,再叠加abc.abstractmethod,这正是 Python 官方推荐的等价写法。诊断消息与修复标题由违规结构体的消息格式宏生成(见 deprecated_abc_decorator.rs):
fn message(&self) -> String { format!("Use `@{to}` and `@abstractmethod` instead of `{from}`") } fn fix_title(&self) -> String { format!("Replace with `@{to}` and `abstractmethod`") }该违规类型实现了AlwaysFixableViolationtrait,即“可自动修复”契约——在识别到模式的前提下总是伴随修复方案。
检测原理:基于限定名解析的装饰器匹配
规则入口函数在 deprecated_abc_decorator.rs 中,遍历函数定义的装饰器列表:
/// UP051 pub(crate) fn deprecated_abc_decorator(checker: &Checker, decorator_list: &[Decorator]) { for decorator in decorator_list { // Look for, e.g., `import abc; @abc.abstractclassmethod`, etc. for (from, to) in [ ("abstractclassmethod", "classmethod"), ("abstractstaticmethod", "staticmethod"), ("abstractproperty", "property"), ] { if checker .semantic() .resolve_qualified_name(&decorator.expression) .is_some_and(|qualified_name| qualified_name.segments() == ["abc", from]) { // ... 报告诊断并生成修复 } } } }关键设计在于它不匹配字面量文本,而是调用语义模型上的resolve_qualified_name把装饰器表达式解析为全限定名,再与["abc", "abstractclassmethod"]之类的段序列比对。这意味着以下等价写法都能被识别,而不只是import abc后的@abc.abstractclassmethod:
from abc import abstractclassmethod后使用@abstractclassmethod;import abc as abc_mod后使用@abc_mod.abstractclassmethod。
只要限定名最终解析到标准库abc模块下的同名属性即命中,注释中也明确了这一意图(Look for, e.g., import abc; @abc.abstractclassmethod, etc.)。
调用时机位于 AST 分析器对函数定义语句的分发处(见 statement.rs):
if checker.is_rule_enabled(Rule::DeprecatedAbcDecorator) { pyupgrade::rules::deprecated_abc_decorator(checker, decorator_list); }即该规则只在规则被启用时对def语句的decorator_list执行检查,未启用时零开销跳过。
修复实现:导入管理与 safe fix
修复逻辑是整个规则中最有工程价值的部分,同样位于 deprecated_abc_decorator.rs。它由四个编辑步骤组成:
获取缩进:
indentation_at_offset计算装饰器所在行的缩进。若无法确定缩进(None),则跳过生成 fix,保证不产出破坏格式的补丁:let indentation = indentation_at_offset(decorator.range().start(), checker.source()); let Some(indentation) = indentation else { continue; };确保内置装饰器可用:
get_or_import_builtin_symbol(to, ...)为classmethod/staticmethod/property解析绑定名(它们是内置符号,无需真正 import,此调用主要用于得到正确的引用形式)。插入第一行装饰器:在装饰器起点插入
@{binding}\n{indentation},即把原来的一行拆成两行。替换原装饰器为
abc.abstractmethod:get_or_import_symbol(&ImportRequest::import("abc", "abstractmethod"), ...)确保文件中存在abc.abstractmethod的可用绑定(必要时生成/复用导入语句),然后把原装饰器表达式区间整体替换为该绑定。若文件中原本没有
import abc,这一步会自动补上导入,使得修复后的代码可直接运行。
最终所有编辑被打包为一个Fix::safe_edits(安全修复),用户执行ruff check --fix即可无风险落地。try_set_fix的使用模式意味着:任何一步无法安全完成时,规则仍会报告诊断,只是不带自动修复——诊断与修复解耦,是 Ruff 规则体系的通用约定。
mdtest 验证机制:测试文档本身就是测试用例
本 mdtest 文档并非普通说明文档,而是 Ruff 的文档驱动测试(mdtest)用例。其结构约定为:
- 文件头部 TOML 块声明该用例的 lint 配置(
preview = true、select = ["UP051"]); # snapshot: deprecated-abc-decorator行内注释标记需要断言的诊断位置;- 文末
```snapshot块保存 ruff 实际输出的诊断与修复 diff 快照。
当源码或规则行为变化导致输出不一致时,mdtest 框架会报告快照不匹配,从而保证“文档里演示的行为”与“规则实际行为”永远同步。该机制的实现位于 ruff_mdtest 与 mdtest 两个 crate,crates/ruff_linter/resources/mdtest/下按规则族(pyupgrade、pyflakes、pylint 等)组织了大量同类用例,deprecated-abc-decorator.md是其中 UP051 的行为基准。
小结与适用前提
- 适用对象:仍在使用
@abc.abstractclassmethod、@abc.abstractstaticmethod、@abc.abstractproperty的存量 Python 代码。这三个装饰器自 Python 3.3 起标记废弃,迁移到@classmethod/@staticmethod/@property叠加@abc.abstractmethod的写法既消除废弃用法,也是官方推荐形式。 - 启用前提:当前仓库版本中该规则处于 preview 通道(
preview_since = "0.15.21",分类Suspicious),必须preview = true并select = ["UP051"]才能生效。 - 行为保障:修复以 safe fix 提供,处理了
import abc/from abc import .../ 别名导入等多种引用形式;在缩进无法确定或导入解析失败时只报诊断不出补丁,不会破坏源文件。 - 验证路径:可在 deprecated-abc-decorator.md 中对照完整诊断快照,在 deprecated_abc_decorator.rs 中查阅检测与修复的完整实现,并在 codes.rs 中确认规则 ID 映射。
【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考