ARTICLE DETAIL

资讯详情

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

ruff `ty` 类型检查器的 `missing-argument` 规则:在运行前静态捕获缺失必选参数的函数调用

ruff `ty` 类型检查器的 `missing-argument` 规则:在运行前静态捕获缺失必选参数的函数调用 ruffty类型检查器的missing-argument规则在运行前静态捕获缺失必选参数的函数调用【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff本文围绕 ruff 仓库中ty类型检查器子项目的一项静态检查规则missing-argument展开它用于在函数/方法调用处检测“调用方遗漏了某个必选无默认值参数”的问题。读者读完本文后将能精确理解该规则的触发条件、诊断输出形态、底层参数绑定实现原理、边界情况联合类型、ParamSpec、functools.partial等以及如何在代码中抑制它从而在自己的类型检查流程中用好这一防线。规则要做什么根据规则的文档 missing-argument.md 的说明What it does检查一次调用中是否缺失了必需参数Checks for missing required arguments in a call。Why is this bad调用时漏掉必选参数会在运行时抛出TypeError。换言之该规则关心的是“调用形状call shape”问题被调用方签名要求提供某些参数而调用方没有给出。此类错误在纯动态运行前是不可见的ty类型检查器则把这一运行时异常前移到静态分析阶段直接报出。在代码层面该规则以MISSING_ARGUMENT静态 lint 的形式在 types/diagnostic.rs 中注册其定义要点包括文档正文直接通过include_str!(../../resources/lint_docs/missing-argument.md)内联自上述文档文件摘要detects missing required arguments in a call状态stable自0.0.1-alpha.1起默认级别为Error即默认启用时属于错误级诊断诊断代码为error[missing-argument]。触发场景最简单的完整示例文档给出的最小触发示例是一个“定义了必选参数、调用时却完全没传”的函数调用def func(x: int): ... # TypeError: func() missing 1 required positional argument: x func() # error注意第 15 行的注释同样的调用一旦真的运行CPython 会抛出TypeError: func() missing 1 required positional argument: x。missing-argument规则所做的就是在静态阶段预先识别出“这个调用一定会缺参数”让错误在编辑器/CI 中即可见而不是等到运行时才炸。这里有一个关键界定只有“必选”参数缺失才报错。所谓必选参数从实现角度看是指“没有默认值、且调用中未被匹配到”的参数。参数带默认值时调用方不传是合法写法规则会放行。规则的适用面不止普通函数调用从仓库中围绕该规则展开的 mdtest 用例missing_argument.md可以看到凡是“存在签名、需要绑定实参”的调用场景都在规则覆盖范围内包括模块级函数调用f()缺少必选参数a联合类型union上调用h f if coinflip() else g; h(b56)会同时针对两个 union 分支报告绑定方法调用Foo().method()——self已由实例自动绑定真正缺的是剩余必选参数a协议Protocol成员的调用p.method()缺value。此外构造函数调用同样参与参数绑定ty内部以ConstructorCallableKind区分构造调用见 types/call/bind.rs因此“缺参实例化”同样会被规则捕获。需要说明的是该类场景下规则的触发依赖被调用方具备可解析的签名。对于def g(a, b): ...这类签名明确的可调用对象绑定器可以精确判定缺了哪个参数并逐个报出。命中后的诊断输出长什么样mdtest 快照记录了该规则的完整诊断形态。以最简单的f()为例error[missing-argument]: No argument provided for required parameter a of function f -- src/main.py:3:1 | 3 | f() # snapshot | ^^^ info: Parameter declared here -- src/module.py:1:7 | 1 | def f(a, b42): ... | ^诊断信息包含几个值得注意的设计消息模板No argument provided for required parameter a of function f。当有多个必选参数同时缺失时会按英文复数规则切换为No arguments provided for required parameters ...单复数处理逻辑见 types/call/bind.rs。被调用方描述消息尾部会根据被调对象类型给出of function f、of bound method Foo.method等描述即实现中的callable_description。Info 级子诊断主诊断之外还会附带一条Parameter declared here的子诊断直接把光标指向参数在定义处的位置src/module.py:1:7的a。这一“调用处报错 定义处溯源”的组合让使用者无需跳转就能知道漏了签名里的哪个形参。实现上通过callable_ty.parameter_span(...)反查被调用签名的参数源码位置完成types/call/bind.rs。联合类型的调用逐分支诊断当一个可调用值是联合类型时例如h f if coinflip() else g调用可能对某几个分支合法、对另几个分支缺参。此时ty会为联合中的每个不兼容分支各报一条missing-argument诊断并附加两条说明性子诊断error[missing-argument]: No argument provided for required parameter a of function f info: Union variant def f(a, b42) - Unknown is incompatible with this call site info: Attempted to call union type (def f(a, b42) - Unknown) | (def g(a, b) - Unknown)mdtest 文档missing_argument.md 开头特别解释了这一设计取舍对联合类型规则不为每个分支附加Parameter declared here这样的“定义处”子诊断而是采用独立的Union variant ... is incompatible信息——因为若每个分支都带定义处定位输出会过度冗长。底层实现参数绑定阶段如何判定“缺失”missing-argument的判定发生在ty的参数绑定argument binding流程中。核心代码位于 types/call/bind.rs可拆成三层来理解1. 逐个形参检查匹配情况L5590-L5623绑定器维护每个形参的matched状态与suppress_missing_error标志。循环遍历所有形参若matched为真说明调用中已有实参命中该形参跳过若带suppress_missing_error用于部分特殊调用场景跳过若该形参是*args/**kwargs变参且不存在ParamSpec语境跳过——变参天然不是必填项若形参有默认值param.has_default()跳过——可选参数可以不传其余形参即“必选且未匹配”被收集进missing列表。末尾若missing非空则向错误列表压入一条BindingError::MissingArguments同时携带缺失参数集合以及若有相关的ParamSpec信息types/call/bind.rs。2. 错误变体的数据结构L8877-L8883/// One or more required parameters (that is, with no default) is not supplied by any argument. MissingArguments { parameters: ParameterContexts, /// If the missing arguments are for a ParamSpec, this contains the ParamSpec typevar. paramspec: OptionBoundTypeVarInstancedb, },BindingError枚举还包含InvalidArgumentType、UnknownArgument、TooManyPositionalArguments、ParameterAlreadyAssigned等兄弟变体types/call/bind.rs它们共同构成调用形状校验的错误体系missing-argument只是其中的“缺参”一维。3. 诊断的最终生成L9388-L9431绑定错误随后在诊断阶段被翻译为用户可见的 lint 输出命中MissingArguments时以整个调用含全部实参的 range为报告位置调用report_lint(MISSING_ARGUMENT, range)按缺失参数数量选择单/复数模板拼接of callable描述并附加定义处子诊断若缺失与ParamSpec相关还会追加一条解释These arguments are required because ParamSpec ... could represent any set of parameters at runtime。特殊情形详解ParamSpec*args与**kwargs也会“必选”常规签名中*args/**kwargs从不被视为必选但当被调用方是携带ParamSpec的泛型可调用对象时情况反转。专门用例 missing_argument_paramspec.md 说明了原因对于ParamSpec可调用对象*args与**kwargs都是必需的因为底层可调用对象的签名未知。from typing import Callable def decorator**P - Callable[P, None]: def wrapper(*args: P.args, **kwargs: P.kwargs) - None: func() # error: [missing-argument] func(*args) # error: [missing-argument] func(**kwargs) # error: [missing-argument] return wrapper这里func的真实签名被P抽象化运行时P可能展开为任意参数集合——只传*args不传**kwargs或反之都无法保证调用合法因此三种写法都会命中missing-argument。BindingError::MissingArguments中专门保留paramspec字段就是为了在报错时解释“为什么这里*args/**kwargs是必选的”见上文 L9420-L9428 的附加子诊断。functools.partial延迟绑定不误报用functools.partial构造偏函数时未绑定的必选参数会被推迟到调用阶段因此不应在构造partial的当场报缺参。绑定器的is_relevant_for_partial_application专门做了过滤MissingArguments不在“与偏应用相关的错误”集合中即partial(f, 1)这类“参数仍留待后续补齐”的写法不会触发缺参误报而partial(f, x)这类类型不匹配仍会立即报告types/call/bind.rs。关键字传参的排他性规则对“必选”的判定只看该形参是否被任意实参位置或关键字匹配到。因此def g(a, b): ...; g(1, b2)合法而g(1)会缺b在调用h(b56)见联合类型用例时规则能区分出f与g各自缺的是a。位置参数与关键字参数的匹配、位置专用参数positional-only的约束等则由同一绑定器的其它路径与规则如positional-only-parameter-as-kwarg负责不在此规则范围内。在实际项目中启用与抑制启用与默认行为该规则属于ty类型检查器规则集在 types/diagnostic.rs 处的register_lint(MISSING_ARGUMENT)完成注册是稳定规则且默认级别为Error因此只要运行ty对代码做类型检查并开启默认规则缺参调用就会直接以错误呈现。它与missing-type-argument类型表达式缺泛型实参、missing-typed-dict-key构造TypedDict缺必填键等“缺东西”类规则同源相邻但适用范围互不重叠后者分别针对类型标注与TypedDict构造。抑制方式对个别确属“故意先留空”的调用可通过行内类型忽略注释抑制。仓库自身代码及测试中就大量使用该写法示例见 fixes.rsf( # ty: ignore[missing-argument] g(missing)) # ty: ignore[unresolved-reference, missing-argument]# ty: ignore[...]内列出需要忽略的规则名多个规则用逗号分隔括号写在与报错行对齐的位置即可。missing-argument属于可忽略的 lint意味着它不会阻断那些“明知暂时缺参、稍后补齐”的中间态代码。与相邻规则如何分工一次调用中可能同时存在多种形状问题ty用一个错误枚举 一组 lint 分别覆盖理解分工有助于排查“为什么报的是这条而不是那条”规则诊断代码捕获的问题参考实现/文档missing-argument必选参数未被提供lint_docs/missing-argument.mdinvalid-argument-type提供了实参但类型不可赋值给形参标注types/call/bind.rsunknown-argument关键字实参名在签名中不存在types/call/bind.rstoo-many-positional-arguments位置实参个数超出签名容量types/call/bind.rsparameter-already-assigned同一形参被多次赋值types/call/bind.rspositional-only-parameter-as-kwarg位置专用参数被以关键字方式传入types/call/bind.rsmissing-type-argument泛型类型表达式缺少类型实参lint_docs/missing-type-argument.md例如func(x)不会触发missing-argument参数给了而会触发invalid-argument-typefunc(unknownx)触发unknown-argumentfunc(x, x)触发parameter-already-assigned。这些规则与缺参规则共享同一套BindingError错误模型与报告机制完整规则清单可对照 lint_docs 目录。测试与回归保障该规则的正确性由两层测试守护mdtest 文档化测试以 missing_argument.md 与 missing_argument_paramspec.md 为载体代码块中嵌入# snapshot/# error: [missing-argument]断言标记运行 mdtest 时自动把实际诊断与内联快照比对防止消息措辞、定位偏移或触发条件随重构漂移。绑定器单元级回归规则触发逻辑判定必选、跳过默认值/变参、汇总MissingArguments错误在 types/call/bind.rs 中被参数化驱动配合 types/infer/tests.rs 中对诊断文本No argument provided for required parameter ...的断言确保单参数、多参数等组合的覆盖。小结missing-argument是ty类型检查器调用形状校验体系中最直观也最高频命中的一类诊断把“TypeError: missing required positional argument”从运行时提前到静态阶段。它的核心价值在于三点判定准确——只对无默认值且未匹配的形参报错天然兼容可选参数、变参与偏函数延迟绑定可解释性强——通过Parameter declared here与联合类型Union variant ... is incompatible等子诊断精确定位责任覆盖面完整——函数、方法、构造、Protocol、联合类型与ParamSpec泛化调用全部纳入统一绑定模型。若想在团队代码库中尽早拦截此类调用错误只需启用ty类型检查并保留该规则的 Error 级默认设置即可让“漏参”在编辑阶段无所遁形。【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表