Ruff ty 的unsupported-bool-conversion诊断__bool__实现错误的四种触发形态与源码级检测机制【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff本文围绕 ty 类型检查器的 mdtest 测试文档 unsupported_bool_conversion.md完整梳理unsupported-bool-conversion这一诊断的四类典型触发场景并深入 ty_python_semantic 的布尔求值实现讲清每种场景对应的错误类型、诊断文案与底层判定逻辑。读完后你能够准确识别并修复__bool__相关的运行时隐患理解 ty 如何从类型推断引擎内部决定一个类型能否被转换为布尔值以及如何在 mdtest 框架下验证诊断行为。诊断概览unsupported-bool-conversion检查什么ty 的官方规则文档 unsupported-bool-conversion.md 对该诊断的定义是当对象没有正确实现__bool__时检查对其进行的布尔转换。其危险性在于——如果对一个对象求真值时会抛出异常那么该对象在任何布尔上下文中都会导致运行时失败。规则文档给出的示例覆盖了if语句、and短路表达式、not一元运算和链式比较如b1 b2 b1其中比较结果的布尔转换同样依赖__bool__四类触发点。本文主体文档是该诊断的mdtest 测试文件其文件头!-- snapshot-diagnostics --是快照诊断测试的声明文件中每处# error: [unsupported-bool-conversion]注释标定了诊断必须出现的位置每个代码块的完整运行结果被固化在 snapshots 目录下的同名.snap文件中快照来源标注为 crates/mdtest/src/lib.rs。这类测试由 crates/ty_python_semantic/mdtest.py 驱动是 ty 语义分析层行为的回归验证手段。文档共列出四种可以发生unsupported-bool-conversion的方式逐一继承如下。四种触发场景完整继承自 mdtest 文档四个场景的触发语句都是同一个短路表达式10 and a and Trueand在求值左侧操作数时会隐式对其进行布尔转换bool(a)这正是该诊断的作用点。场景一有__bool__方法但参数签名错误class NotBoolable: def __bool__(self, foo): return False a NotBoolable() # error: [unsupported-bool-conversion] 10 and a and True__bool__协议只接受零个显式参数仅self这里多出的foo使其无法被解释器按协议调用。对应的快照诊断输出为error[unsupported-bool-conversion]: Boolean conversion is not supported for type NotBoolable -- src/mdtest_snippet.py:8:8 | 8 | 10 and a and True | ^ info: __bool__ methods must only have a self parameter -- src/mdtest_snippet.py:2:17 | 2 | def __bool__(self, foo): | --------^^^^^^^^^^^ Incorrect parameters | | | Method defined here主诊断指向表达式中需要求真的对象a附加的info子诊断则定位到方法定义的参数列表处并用 Incorrect parameters 与 Method defined here 两级标注呈现问题。场景二有__bool__方法但返回类型不正确class NotBoolable: def __bool__(self) - str: return wat a NotBoolable() # error: [unsupported-bool-conversion] 10 and a and True返回类型str无法赋给bool说明该方法即便能调用也不会产生合法的布尔结果。快照诊断输出error[unsupported-bool-conversion]: Boolean conversion is not supported for type NotBoolable -- src/mdtest_snippet.py:8:8 | 8 | 10 and a and True | ^ info: str is not assignable to bool -- src/mdtest_snippet.py:2:27 | 2 | def __bool__(self) - str: | -------- ^^^ Incorrect return type | | | Method defined here注意子诊断的措辞是类型关系判断str不可赋给bool而非对具体返回值的静态检查——它依据的是方法的声明返回类型。场景三__bool__属性存在但不可调用class NotBoolable: __bool__: int 3 a NotBoolable() # error: [unsupported-bool-conversion] 10 and a and True类属性__bool__被声明为一个int完全不可调用。快照诊断输出error[unsupported-bool-conversion]: Boolean conversion is not supported for type NotBoolable -- src/mdtest_snippet.py:7:8 | 7 | 10 and a and True | ^ info: __bool__ on NotBoolable must be callable此处子诊断没有定位到定义处的标注。查阅源码可以看到这是一个已知限制NotCallable分支的注释中写着 It would be nice to create an annotation here for where__bool__is defined. At time of writing, I couldnt figure out a straight-forward way of doing this. ---AG见 bool.rs。场景四联合类型中至少一个成员的__bool__不正确class NotBoolable1: def __bool__(self) - str: return wat class NotBoolable2: pass class NotBoolable3: __bool__: int 3 def get() - NotBoolable1 | NotBoolable2 | NotBoolable3: return NotBoolable2() # error: [unsupported-bool-conversion] 10 and get() and True三个成员中NotBoolable1返回类型错误、NotBoolable3属性不可调用NotBoolable2则完全合法。但只要联合中有成员不能正确实现__bool__对整个联合的布尔转换就不可靠诊断必须触发。快照输出error[unsupported-bool-conversion]: Boolean conversion is not supported for union NotBoolable1 | NotBoolable2 | NotBoolable3 because NotBoolable1 doesnt implement __bool__ correctly -- src/mdtest_snippet.py:15:8 | 15 | 10 and get() and True | ^^^^^联合场景的主诊断文案与前三者不同它不仅给出联合类型名还点名了第一个出错的成员NotBoolable1并且不附带info子诊断。源码级原理从try_bool到BoolError以上四类形态在 ty 的类型推断引擎中对应 crates/ty_python_semantic/src/types/bool.rs 中的一条清晰链路。入口选择。文件顶部为Typedb实现了三个相关方法boolL20-L28求值类型真值出错时回退为Truthiness::Ambiguous适用于类型检查之外、Python 不做隐式bool调用的场景如静态分支评估bool_if_inhabitedL39-L45对等价于Never的类型返回None供条件分析保留flag and stop()这类短路结果的语义try_boolL53-L64类型检查与隐式bool调用路径专用失败时返回Result错误——unsupported-bool-conversion正是从这里产生。__bool__调用检查try_dunders闭包L94-L202。引擎通过try_call_dunder(db, env, __bool__, CallArguments::none(), ...)模拟解释器对__bool__的零参调用然后把CallDunderError的各类失败映射为内部的BoolErrorCallDunderError形态映射结果对应 mdtest 场景调用成功但返回类型不可赋给boolreturn_type.is_assignable_to(..., KnownClass::Bool...)校验失败BoolError::IncorrectReturnType场景二CallErrorKind::BindingError按参数绑定失败BoolError::IncorrectArguments场景一CallErrorKind::NotCallable成员存在但不可调用BoolError::NotCallable场景三CallErrorKind::PossiblyNotCallable可能缺失__call__BoolError::Other—此外该闭包还处理了合法的降级路径若__bool__不可用MethodNotAvailable会检查是否为元组按元组长度决定真值、是否为final类型此时可安全地回退到__len__要求返回类型可赋给SupportsIndex否则永远为真。也就是说该诊断只报告用户显式声明了__bool__但声明得不对的情况普通对象走__len__或默认恒真的逻辑不会误报。联合类型归并try_union闭包L204-L243。对联合类型的每个成员递归求try_bool_impl只要出现任意错误就置has_errors true当所有错误都是NotCallable时上报BoolError::NotCallable否则上报BoolError::Union。这解释了场景四的诊断文案为何是 doesnt implement__bool__correctly——它来自BoolError::Union分支的诊断生成逻辑。错误枚举与诊断渲染。BoolErrorL399-L428定义了五种变体NotCallable、IncorrectArguments、IncorrectReturnType、Union、Other。fallback_truthinessL431-L439为bool等非诊断路径提供保守的真值回退。真正的诊断文案由report_diagnostic_implL461-L561生成通过context.report_lint(UNSUPPORTED_BOOL_CONVERSION, condition)注册诊断lint 定义见 diagnostic.rs其文档字符串直接内嵌了前述 lint 文档然后按变体分支输出IncorrectArguments子诊断固定文案 __bool__methods must only have aselfparameter并尝试解析__bool__成员的方法名/参数 span 以添加 Incorrect parameters 与 Method defined here 标注IncorrectReturnType子诊断文案为 {return_type}is not assignable tobool并标注返回类型 span 为 Incorrect return typeNotCallable子诊断文案为 __bool__on{type}must be callable当前无定义处标注见场景三说明Union通过first_error找出联合中第一个try_bool失败的成员生成 ...because{member}doesnt implement__bool__correctly 的单一主诊断。修复指引与验证方式对照文档的四个场景修复方式都很直接多余参数把def __bool__(self, foo)改为def __bool__(self)协议不接受额外位置参数返回类型将声明返回类型改为bool并确保实际返回值与之兼容如return wat应改为布尔逻辑;不可调用属性删除或重命名非函数的__bool__赋值如果确实想用属性表达自定义真值应考虑__bool__或__len__协议联合成员为出错的成员补齐合法的__bool__或在调用侧收窄类型类型窄化后可避免对联合整体求值。验证层面该文档本身就是可执行的回归测试修改诊断逻辑后重新运行 mdtest驱动脚本见 crates/ty_python_semantic/mdtest.py若输出的诊断格式、位置或文案与 snapshots 目录中的四个快照不一致测试即失败。规则的用户侧文档含 What it does / Why is this bad / Examples 三节完整内容位于 crates/ty/docs/rules.md该规则同时收录于 ruff.schema.json。小结unsupported-bool-conversion是一个把协议实现正确性前置到静态阶段的诊断它在and/or/not/if等隐式求真位置模拟解释器对__bool__的调用并把参数签名、返回类型、可调用性三类协议违规分别映射为带精确标注的诊断。本文四个 mdtest 场景与其快照输出、bool.rs 中try_bool → BoolError → report_diagnostic_impl的链路共同构成了该诊断从触发、判定到呈现的完整证据链。【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考