ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ty 类型检查器 `invalid-metaclass` 规则详解:拦截无效的 `metaclass=` 实参

ty 类型检查器 `invalid-metaclass` 规则详解:拦截无效的 `metaclass=` 实参 ty 类型检查器invalid-metaclass规则详解拦截无效的metaclass实参【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff导读本文围绕 Ruff 仓库中类型检查器 ty 的invalid-metaclass静态规则展开。该规则会在静态分析阶段拦截那些语法上合法、但运行时必然抛TypeError的metaclass实参例如非可调用对象、未实例化的泛型元类等把错误前置到开发期。读完本文你将掌握该规则的触发条件、错误分类体系、诊断范围定位方式以及如何从仓库源码与 mdtest 用例中验证其行为边界与已知局限。规则概述元类实参并不任意Python 的类定义语法允许把任意表达式写在metaclass之后但从语义上说这个表达式必须满足两个硬性约束必须可调用callable被调用时必须接受与type.__new__相同的参数即类名、基类元组与类体命名空间因为类对象的创建最终会委托给它。invalid-metaclass规则正是对第一个约束及泛型实例化约束的静态检查。其官方行为描述位于 invalid-metaclass.md规则元信息登记于 diagnostic.rsdeclare_lint! { #[doc include_str!(../../resources/lint_docs/invalid-metaclass.md)] pub(crate) static INVALID_METACLASS { summary: detects invalid metaclass arguments, status: LintStatus::stable(0.0.1-alpha.1), default_level: Level::Error, } }也就是说该规则自0.0.1-alpha.1起进入稳定stable状态默认级别为error一旦命中会直接报错而不仅是告警。ty 的完整规则目录可查阅 rules.md。为什么要报错运行时的必然失败看文档给出的最小反例# TypeError: int object is not callable class B(metaclass42): ... # error42是一个合法的表达式却不是可调用对象。Python 解释器执行该语句时会在类创建阶段尝试调用元类从而抛出TypeError: int object is not callable。这类错误只有程序真正运行到那条类定义时才会暴露且经常潜伏在导入链深处。invalid-metaclass的价值就在于把这种运行时崩溃转换为可静态定位、可提前修复的编译期诊断。类似的运行时崩溃还包括对已实例化对象误用元类、未完整实例化的泛型元类等详见下文错误分类。底层实现try_metaclass与错误分类触发入口规则的检查点在static_class.rs的类语义后处理阶段。对每个类ty 先尝试解析其元类一旦失败就按错误类型分发诊断见 static_class.rs// Check that the classs metaclass can be determined without error. if let Err(metaclass_error) class.try_metaclass(db) { let invalid_metaclass_range class_node .arguments .as_ref() .and_then(|arguments| arguments.find_keyword(metaclass)) .map(Ranged::range) .unwrap_or_else(|| class.header_range(db)); match metaclass_error.reason() { MetaclassErrorKind::GenericMetaclass { /* → INVALID_METACLASS */ } MetaclassErrorKind::NotCallable(ty) { /* → INVALID_METACLASS */ } MetaclassErrorKind::PartlyNotCallable(ty) { /* → INVALID_METACLASS */ } MetaclassErrorKind::Conflict { .. } { /* → conflicting-metaclass / CONFLICTING_METACLASS */ } MetaclassErrorKind::Cycle { /* → CYCLIC_CLASS_DEFINITION */ } } }从源码结构可以清晰看出invalid-metaclass只负责其中三类错误而**元类冲突Conflict与循环继承Cycle**分别交由conflicting-metaclass与cyclic-class-definition规则处理职责边界分明。错误分类全集try_metaclass返回的错误类型定义在 class.rsMetaclassErrorKind枚举共五种变体变体含义归属规则NotCallable(Type)元类是某个不可调用的类型如intinvalid-metaclassPartlyNotCallable(Type)元类是联合类型其中部分成员不可调用invalid-metaclassGenericMetaclass元类是仍被类型变量参数化的泛型类invalid-metaclassConflict { candidate, base_metaclass, base }继承层级中出现互相不兼容的元类conflicting-metaclassCycle解析元类时检测到循环cyclic-class-definitionNotCallable与PartlyNotCallable两个分支给出的诊断消息分别为Metaclass type {ty} is not callable与Metaclass type {ty} is partly not callable而GenericMetaclass分支直接输出固定文案Generic metaclasses are not supported。三种触发场景示例从 mdtest 行为测试 metaclass.md 中可以提取出对应三种场景场景一元类类型本身不可调用def _(n: int): # error: [invalid-metaclass] class B(metaclassn): ...场景二联合类型中部分成员不可调用def _(flag: bool): m f if flag else 42 # error: [invalid-metaclass] class C(metaclassm): ...m的可能类型是函数类型 | int属于部分不可调用PartlyNotCallablety 依然选择报错。场景三带未绑定类型变量的泛型元类PEP 695class FooT: x: T # error: [invalid-metaclass] class BarT: ...对应的旧式typing.TypeVar Generic写法同样被拦截诊断消息为Generic metaclasses are not supportedfrom typing import TypeVar, Generic T TypeVar(T) class GenericMeta(type, Generic[T]): ... # error: [invalid-metaclass] Generic metaclasses are not supported class GenericMetaInstance(metaclassGenericMeta[T]): ...泛型元类边界在哪里需要强调的是GenericMetaclass只拒绝仍被类型变量参数化的元类。若泛型元类已被具体类型完全特化ty 是允许的见 metaclass.mdclass FooT: x: T class Bar(metaclassFoo[int]): ... reveal_type(Bar.__class__) # revealed: class Foo[int]诊断范围精确到关键字实参一个值得注意的实现细节是诊断位置的选择。从触发入口代码可以看出ty 会先在类定义节点的参数列表中查找名为metaclass的关键字参数arguments.find_keyword(metaclass)并把整个关键字实参的 range作为报告位置只有当类定义根本没有arguments时才回退到类头范围class.header_range(db)。mdtest 中用# snapshot: invalid-metaclass指令固化了这一输出格式见 metaclass.mddef _(n: int): # snapshot: invalid-metaclass class B(metaclassn): x 1 y 2对应的快照输出为error[invalid-metaclass]: Metaclass type int is not callable -- src/mdtest_snippet.py:3:13 | 3 | class B(metaclassn): | ^^^^^^^^^^^可见光标^^^精确覆盖了metaclassn整个关键字实参而非整个类头或整行方便开发者在长类名、多基类的复杂类定义中一眼定位问题出处。与相邻规则及已知局限invalid-metaclass并非孤立存在它与同模块内的元类相关检查共同构成一道防线conflicting-metaclass当派生类的元类不是其所有基类元类的非严格子类时触发例如显式metaclass与某个基类自带的元类产生冲突cyclic-class-definition元类解析过程遇到循环类定义时触发同时可防止循环引用造成类型检查器无限递归mdtest 中专门用A(B) → B(C) → C(A)的用例验证了这一点见 metaclass.md。在实现上MetaclassError与MetaclassErrorKind均派生于 salsa 查询结果salsa::SalsaValue因此元类解析会参与增量计算缓存重复检查同一文件不会重复推导。从源码与测试的 TODO 注释还可以了解到当前已知的局限例如元类可调用但签名与type.__new__不兼容的情况如下面这个签名不匹配的元类尚未产生诊断class SignatureMismatch: ... # TODO: Emit a diagnostic class D(metaclassSignatureMismatch): ...也就是说该规则目前覆盖的是可调用性与泛型特化完整性两类静态可判定的错误而对元类签名与调用协议的深入校验仍是待完善方向见 metaclass.md。实战小结在基于 ty 的静态检查流程中遇到error[invalid-metaclass]时可按以下顺序排查确认实参类型可调用若传入的是普通实例或值类型如int、42改用继承自type的元类联合类型若实参在分支中类型不同保证所有可能分支都是可调用类型泛型元类若元类声明了类型变量使用前必须完全特化如Foo[int]不要直接传入Foo[T]这类仍带自由类型变量的形态区分相邻错误若报错指向多个基类间的元类不兼容应查阅conflicting-metaclass的诊断说明。整体上invalid-metaclass是 ty 元类推导能力的一个小而关键的出口——它以MetaclassErrorKind五种分类为骨架把运行时才暴露的元类构造失败系统性地前置到了静态阶段并借助快照测试保证了诊断文本与定位范围的长期稳定。延伸阅读规则文档原始出处invalid-metaclass.md规则登记与元信息diagnostic.rs元类错误枚举定义class.rs检查触发与消息构造static_class.rs行为级测试与快照metaclass.md规则总览目录rules.md【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表