
ty 类型检查器规则解析invalid-legacy-type-variable——legacyTypeVar声明合法性检查全解【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff本文深入剖析 Astral ruff 仓库内ty类型检查器中的invalid-legacy-type-variable规则。该规则用于检测使用typing.TypeVar/typing_extensions.TypeVar创建旧式legacy类型变量时的各类非法声明覆盖必须立即赋值给变量不得重复定义name 参数必填且唯一bound 与 constraints 互斥等一整套声明契约。读完本文你将掌握 legacyTypeVar的全部合法性约束、每条报错在源码中的触发位置、对应的修复写法以及该规则的测试与抑制方式。规则概述它检查什么依据规则自身的文档invalid-legacy-type-variable.mdWhat it does检查是否创建了非法的 legacyTypeVar。Why is this bad创建 legacyTypeVar时必须遵守若干硬性要求违反这些要求意味着该类型变量在类型系统中并不可用——类型检查器无法把它当作真正的类型变量去参与泛型推导声明方代码也会在运行时或规范层面失效。在规则声明处diagnostic.rs该 lint 的摘要为 detects invalid legacy type variables默认级别为error见 rules.md 的规则索引页。也就是说一旦命中检查结果默认以错误呈现而不是警告。为什么需要这类检查legacy TypeVar 的声明契约legacy TypeVar 与 PEP 695 TypeVarPython 的类型变量有两种声明形态legacy旧式函数式调用T TypeVar(T, bound..., covariant...)来自 PEP 484 时代的typing.TypeVarPEP 695新式语法级声明type T[T] .../class Foo[T]: ...无需调用构造器。invalid-legacy-type-variable只针对前者。从源码可见其判断完全围绕typing.TypeVar与typing_extensions.TypeVar两个已知类展开见下文 触发路径 一节。至于 PEP 695 的TypeVar参数声明则由 infer_typevar_definition 等独立路径处理不在本规则范围。为什么非法声明必须被静态拦截TypeVar 是一个特殊构造它在类型系统中代表未知但可推导的类型其**身份identity**由声明的变量名与定义位置共同决定源码中用TypeVarIdentity::new(db, target_name, ...)建模见 typevar.rs。如果一个TypeVar(T)出现在函数注解等非赋值上下文、或者反复被赋值类型检查器就无从建立稳定的名字 → 类型变量绑定后续泛型推导、协变逆变判定都会失真。因此这类声明从一开始就应当以错误级别报出。逐个报错场景详解与修复示例源码印证规则文档给出的官方示例聚焦两个典型错误from typing import TypeVar T TypeVar(T) # okay T TypeVar(T) # error: Cannot redefine T as a type variable # TypeVar must be immediately assigned to a variable # error def f(t: TypeVar(U)): ... # ty: ignore[invalid-type-form]第二段示例其实命中了另一个规则invalid-type-form文档中用# ty: ignore[invalid-type-form]抑制意图是说明TypeVar不能内联出现在函数签名里。真正完整的报错清单远比这两个示例丰富全部可以在 legacy_typevars.md 测试文件 与 typevar.rs 推断实现 中逐条验证。下面按源码中的校验顺序逐一展开。1. 必须立即赋值给简单变量最常见from typing import TypeVar T TypeVar(T) # 合法 U: TypeVar TypeVar(U) # error: A TypeVar definition must be a simple variable assignment x (foo, TypeVar(W)) # error: A TypeVar definition must be a simple variable assignment def f(t: TypeVar(V)): ... # error由 invalid-type-form 兜底源码要求赋值目标必须是ast::Expr::Name即裸变量名否则直接报错typevar.rs。带注解的赋值U: TypeVar ...之所以非法是因为在use-def流程中它已不属于TypeVar 定义的规范位置。对应的测试断言见 legacy_typevars.md。修复去掉类型注解让TypeVar(...)成为该变量的唯一右侧表达式。2. 不得重复定义同一类型变量T TypeVar(T) T TypeVar(T) # error: Cannot redefine T as a type variable这是官方文档标注# error的第一类问题。实现上ty 会回溯当前作用域中该变量名此前的可达定义如果找到前一个对同名TypeVar的声明就报 Cannot redefine{name}as a type variable并附上Previously defined here的次级注解typevar.rs。连 if/else 等分支中同一名字的重复声明也会被识别为重定义。修复复用已声明的类型变量或改用不同的变量名。3. name 参数缺失T TypeVar() # error: The name parameter of TypeVar is required.位置参数或关键字name都没有提供时触发typevar.rs。4. name 只能给定一次T TypeVar(T, nameT) # error: The name parameter of TypeVar can only be provided once.同时给出位置参数与name关键字属于重复传参typevar.rs。5. 第一个参数必须是字符串字面量T TypeVar(T_name) # error: The first argument to TypeVar must be a string literal.name只有在是编译期可见的字符串字面量时才被接受typevar.rs。因为该名字既要与变量名比对、又要成为类型变量身份的一部分运行时求值的变量无法满足这一需求。6. 字符串名与变量名不一致mismatched-type-nameT TypeVar(Q) # warning: The name passed to TypeVar must match the variable it is assigned to注意这一场景不属于本规则而是命中mismatched-type-name默认warn级别。官方测试文件中也明确用# snapshot: mismatched-type-name标注legacy_typevars.md。但它的触发点就在infer_legacy_typevar内——字符串名与赋值目标名不一致时调用report_mismatched_type_nametypevar.rs。理解本规则时应一并记住name 必须等于它被赋给的变量名。7. bound 与 constraints 互斥T TypeVar(T, str, bytes, boundobject) # error: A TypeVar cannot have both a bound and constraintsbound关键字与位置约束参数第二个位置参数起都被视为约束同时出现时两者语义冲突typevar.rs。一个 TypeVar 要么有上界要么有约束集不能兼得。8. 恰好只有一个 constraint 是非法形态T TypeVar(T, str) # error: A TypeVar cannot have exactly one constraint T TypeVar(T, str, bytes) # 合法至少两个约束规范要求约束集至少包含两个类型typevar.rs。单个约束在语义上等价于一个上界属于用错 API。9. 协变 / 逆变 / infer_variance 组合冲突以下三种组合分别报错typevar.rsT TypeVar(T, covariantTrue, contravariantTrue) # error: A TypeVar cannot be both covariant and contravariant T TypeVar(T, covariantTrue, infer_varianceTrue) # error: A TypeVar cannot specify variance when infer_varianceTrue T TypeVar(T, contravariantTrue, infer_varianceTrue) # error同上此外covariant/contravariant/infer_variance若传入真值无法静态确定的表达式也会报 cannot have an ambiguous truthinesstypevar.rs因为变体声明必须是明确的布尔值。10. 不支持 *args / **kwargs 与未知关键字T TypeVar(*names) # error: Starred arguments are not supported in TypeVar creation T TypeVar(T, foo1) # error: Unknown keyword argument foo in TypeVar creation任意*args展开或**kwargs展开都会直接终止该 TypeVar 的推断并报错typevar.rs。对于未知关键字参数ty 的策略是报错但不中断——继续解析剩余参数尽量保留对 TypeVar 的理解以减少级联错误typevar.rs。11. default / infer_variance 的 Python 版本下限提示性错误T TypeVar(T, defaultint) # Python 3.13 时报错 T TypeVar(T, infer_varianceTrue) # Python 3.12 时报错typing.TypeVar的default参数 3.13 才加入infer_variance参数 3.12 才加入。当目标 Python 版本低于门槛时ty 会报对应错误但这是提示性错误——不会中断解析因为用户的意图是明确的运行时才真正失败typevar.rs。同时注意在.pyistub 文件或typing_extensions中这些版本门槛会被跳过因为assume_all_features self.in_stub() || is_typing_extensionstypevar.rs。规则报错场景速查表非法形态报错信息非简单变量赋值注解赋值、嵌在元组中等A \TypeVar definition must be a simple variable assignment重复定义同名 TypeVarCannot redefine \T as a type variable缺少 nameThe \name parameter of TypeVar is required.位置参数 name重复The \name parameter of TypeVar can only be provided once.第一个参数非字符串字面量The first argument to \TypeVar must be a string literal.bound 与 constraints 并存A \TypeVar cannot have both a bound and constraints恰好一个 constraintA \TypeVar cannot have exactly one constraint同时 covariant contravariantA \TypeVar cannot be both covariant and contravariant显式变体 infer_varianceTrueA \TypeVar cannot specify variance when infer_varianceTrue*args/**kwargsStarred arguments are not supported in \TypeVar creation未知关键字Unknown keyword argument \{name} in TypeVar creation源码层面的实现与触发路径Lint 声明与注册规则本体由declare_lint!宏声明并通过include_str!直接引用本文关联的文档文件作为规则说明diagnostic.rs随后在 diagnostic.rs 注册进 lint 注册表。文档注释即规则文档、源码与测试三者在同一仓库内一一对应是 ty 系列规则的标准组织方式。两条触发路径从调用点看INVALID_LEGACY_TYPE_VARIABLE存在两条触发路径这一点非常关键infer_legacy_typevar内的参数级校验主路径typevar.rs当TypeVar(...)出现在合法的定义位简单变量赋值的右侧时ty 进入专门的推断函数逐条校验上文 3–11 号场景。校验完成后若存在 bound/constraints/default还会把定义标记为deferredtypevar.rs延迟到依赖求值阶段处理避免循环依赖。通用调用绑定机制的兜底builder.rs源码注释明确说明——TypeVar、ParamSpec、NewType、TypeAliasType的正确形态推断均走专门入口不经过完整 call-binding凡是走到 call-binding 流程的这类调用都意味着有人在非法上下文中实例化它们此时统一报 ATypeVardefinition must be a simple variable assignment。有趣的是同一分支下TypeVarTuple的非法实例化也会借用本规则上报builder.rs而ParamSpec则走INVALID_PARAMSPEC。这说明本规则实际覆盖了typing.TypeVar与当前实现下TypeVarTuple两类旧式声明的形态校验。出错后的降级处理定义内部有一个error辅助函数typevar.rs一旦某条校验失败先上报诊断然后降级返回一个普通的typing.TypeVar实例类型而不是中止整个文件的推断——这样后续代码引用T时不会因为一条错误而产生海啸般的连锁报错。这是 ty 在诊断质量与错误隔离之间平衡的典型设计。测试覆盖该规则拥有完整的多层测试专项测试legacy_typevars.md 以 mdtest 格式覆盖缺少 namename 重复给定必须直接赋值给变量name 必须匹配变量名不得重定义等场景并逐一给出error[invalid-legacy-type-variable]快照含精确行列与高亮范围。通用泛型行为测试generics/legacy/variables.md 覆盖 legacy TypeVar 的合法语义约束、上界、默认值等与非法形态测试互补。这些测试通过crates/ty_python_semantic的 mdtest 框架驱动快照断言保证了报错文案、行列号、ty: ignore抑制行为的稳定性。如何消除告警遇到误报或确实需要临时放行时ty 支持行级/文件级抑制注释语法为# ty: ignore[...]方括号内填写规则名def f(t: TypeVar(U)): ... # ty: ignore[invalid-type-form]正如规则文档示例所示invalid-legacy-type-variable.md。ty 的抑制机制细节包括文件级# ty: ignore-file与范围说明可参考 ty_ignore.md 中的用例。但请仅在确有理由时使用抑制——绝大多数命中都意味着代码在声明层面确有缺陷优先按上文各场景的修复建议改正。小结invalid-legacy-type-variable是 ty 对 legacyTypeVar声明形态的入口守门员它以error为默认级别覆盖从赋值形态到参数组合的一整套声明契约配合mismatched-type-name命名一致性、invalid-type-variable-bound/invalid-type-variable-defaultbound 与默认值语义等同族规则共同保证旧式类型变量在类型系统内的身份唯一、语义自洽。理解本规则既有助于写出规范、可被类型检查器正确理解的泛型代码也是读懂 ty 对 PEP 484 泛型实现思路的绝佳入口。【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考