首页
/ Ruff ty 类型检查器 invalid-type-form 诊断全解析:无效类型表达式检测与错误恢复机制

Ruff ty 类型检查器 invalid-type-form 诊断全解析:无效类型表达式检测与错误恢复机制

2026-09-09 16:53:42作者:段琳惟

导读

invalid-type-form 是 Ruff 内置类型检查器 ty(位于 crates/ty_python_semantic)用于检测非法类型表达式的核心 lint:凡是不能合法地作为类型表达式解读的 Python 语法,都会触发该诊断并给出可操作的修复建议。本文以 ty 的官方测试文档 invalid.md 为骨架,结合源码深入讲解 ty 如何判定非法类型形式、如何做错误恢复避免级联报错,以及针对集合字面量、tuple 特化、Literal 包装等常见场景提供的 unsafe fix 修复策略,帮助开发者理解 ty 的类型系统边界并写出可被类型检查器正确理解的注解。

一、什么是 invalid-type-form:从 lint 定义说起

invalid-type-form 是 ty 对"无法被合法解读为类型表达式的表达式"发出的错误诊断。在 lint 文档 中,其用途被描述为:

  • 它做什么:检查那些被当作[类型表达式]使用、却无法合法地按类型表达式解读的表达式;
  • 为什么是错误:这类表达式无法被 ty 理解,某些情况下还会在运行时抛错;
  • 示例
from typing import Annotated

# Int literals are not allowed in this context in type expressions
a: list[1]  # error
# `Annotated` expects at least two arguments
b: Annotated[int]  # error

从源码看,该 lint 在 diagnostic.rs 中通过 declare_lint! 宏声明,默认级别为 Level::Error,状态为 stable;并在 同文件第 127 行 注册进 lint 注册表。在 type inference 过程中,核心函数是 type_expression.rs 中的 infer_type_expression / infer_type_expression_no_store,其内部对非法节点调用 report_invalid_type_expression,后者直接上报 INVALID_TYPE_FORM 并附加上类型表达式规范的参考链接说明。

所有非法形式在类型系统中被建模为 InvalidTypeExpression 枚举的各类变体(见 types.rs),包括 InvalidType(普通非法类型)、InvalidBareTypeVarTupleDeprecatedTypedDictConcatenateRequiresOneArgumentRequiresTwoArguments 等,由特殊形式推断(special_form.rs)和类型表达式推断共同产生。

二、第一类无效类型:把"变量"当类型用

2.1 为什么 type[int]Literal[True] 不能直接作为注解

文档开篇给出了一组非常典型的反例:把运行期对象当作类型注解使用,ty 一律报 invalid-type-form

import typing
from ty_extensions import AlwaysTruthy, AlwaysFalsy
from typing_extensions import Literal, Never

class A: ...

def _(
    a: type[int],      # 变量的类型是 `type[int]`
    b: AlwaysTruthy,   # 运行期总是为真的标记类型
    c: AlwaysFalsy,    # 运行期总是为假的标记类型
    d: Literal[True],  # 字面量类型对象
    e: Literal["bar"],
    f: Literal[b"foo"],
    g: tuple[int, str],
    h: Never,
    i: int,
    j: A,
):
    def foo(): ...
    def invalid(
        a_: a,  # error: [invalid-type-form] "Variable of type `type[int]` is not allowed in a parameter annotation"
        b_: b,  # error: [invalid-type-form]
        ...
        k_: i,  # error: [invalid-type-form] "Variable of type `int` is not allowed in a parameter annotation"
        l_: j,  # error: [invalid-type-form] "Variable of type `A` is not allowed in a parameter annotation"
    ):
        ...

这些例子揭示了 ty 的一条核心规则:注解位置只接受"类型表达式",不接受"求值结果为类型对象的表达式"type[int] 是一个对象,Literal[True] 也是对象——它们可以作为值存在,但不能直接出现在参数注解里。有趣的是,ty 会为常见错误给出专门的子诊断(详见下文"模块字面量"一节),并让所有这类参数的类型都回退为 reveal_type(...) # revealed: Unknown

2.2 推导式与生成器:容器构建表达式全部非法

受 Python typing 官方一致性测试套件(conformance test suite)启发,ty 明确拒绝将推导式或生成器表达式用作类型注解:

B = [x for x in range(42)]
C = {x for x in range(42)}
D = {x: y for x, y in enumerate(range(42))}
E = (x for x in range(42))

def _(
    b: B,  # error: [invalid-type-form]
    c: C,  # error: [invalid-type-form]
    d: D,  # error: [invalid-type-form]
    e: E,  # error: [invalid-type-form]
):
    reveal_type(b)  # revealed: Unknown
    ...

这些列表/集合/字典推导式与生成器表达式的求值结果是容器对象而非类型,因此一律视为无效类型形式。

三、第二类无效类型:非法的 AST 节点

3.1 字面量、运算、调用、yield/await 等一律禁止

ty 的文档给出了一组"百科全书式"的非法 AST 节点清单(均位于同步/异步函数的嵌套函数参数中,以规避无关的语法错误):

async def outer_async():  # avoid unrelated syntax errors on `yield` and `await`
    def _(
        a: 1,                  # "Int literals are not allowed in this context in a parameter annotation"
        b: 2.3,                # "Float literals are not allowed in parameter annotations"
        c: 4j,                 # "Complex literals are not allowed in parameter annotations"
        d: True,               # "Boolean literals are not allowed in this context in a parameter annotation"
        e: int | b"foo",       # unsupported-operator + "Bytes literals are not allowed..."
        f: 1 and 2,            # "Boolean operations are not allowed in parameter annotations"
        g: 1 or 2,             # "Boolean operations are not allowed in parameter annotations"
        h: (foo := 1),         # "Named expressions are not allowed in parameter annotations"
        i: not 1,              # "Unary operations are not allowed in parameter annotations"
        j: lambda: 1,          # "`lambda` expressions are not allowed in parameter annotations"
        k: 1 if True else 2,   # "`if` expressions are not allowed in parameter annotations"
        l: await baz(),        # "`await` expressions are not allowed in parameter annotations"
        m: (yield 1),          # "`yield` expressions are not allowed in parameter annotations"
        n: 1 < 2,              # "Comparison expressions are not allowed in parameter annotations"
        o: bar(),              # "Function calls are not allowed in parameter annotations"
        p: int | f"foo",       # unsupported-operator + "F-strings are not allowed..."
        q: [1, 2, 3][1:2],     # "Only simple names and dotted names can be subscripted in parameter annotations"
        r: list[T][int],       # "Only simple names and dotted names can be subscripted in parameter annotations"
        s: list[list[T][int]], # "Only simple names and dotted names can be subscripted in parameter annotations"
    ):
        reveal_type(a)  # revealed: Unknown
        reveal_type(e)  # revealed: int | Unknown
        reveal_type(p)  # revealed: int | Unknown

值得注意的细节:

  • 错误恢复是部分进行的e: int | b"foo" 中,b"foo" 是非法的,但 int 一侧仍然推断成功,因此最终类型是 int | Unknown——而不是整个注解都变成 Unknownp 同理。
  • 下标操作只能作用于简单名字和点分名字list[T][int] 这种"对下标结果再次下标"的写法被拒绝,因为 list[T] 不是一个可继续下标的名称形式。

3.2 非法二元/一元运算符

文档还系统测试了所有非法的运算符形式,其中 +-*@/%**//<<>>^ 都报 "Invalid binary operator X in type annotation",而 5 & 3~3 由于同时是整数位运算,额外叠加报 "Int literals are not allowed...":

def invalid_binary_operators(
    a: "1" + "2",  # "Invalid binary operator `+` in type annotation"
    b: 3 - 5.0,    # "Invalid binary operator `-` in type annotation"
    ...
    d: Mat(4) @ Mat(2),  # "Invalid binary operator `@` in type annotation"(即使 Mat 重载了 __matmul__)
    ...
    l: 5 & 3,      # int literal 错误 ×2
    m: ~3,         # int literal 错误
):
    ...

这里的 Mat 类重载了 __matmul__ 并返回 int,但 ty 依然拒绝 @——说明类型表达式上下文中的运算符合法性由语法形式决定,与运算符重载结果无关。

四、错误恢复:避免级联诊断的精妙设计

文档用专门一节阐述了 ty 的错误恢复策略,这是该 lint 工程质量的关键体现。

4.1 外层节点非法时抑制内层

# error: [invalid-type-form]          (只报这一个)
x: [[int]]

[[int]] 外层是列表字面量,内层还有一个非法列表字面量 [int]。ty 只报告外层的 invalid-type-form,忽略内层的非法列表,从而避免同一声明产生两条重叠的诊断。

4.2 运行期错误仍然上报

错误恢复并不豁免所有内层问题:如果非法 AST 节点内部存在真正的运行期错误(比如未定义名字),这些"比类型规范洁癖更严重"的问题仍然上报:

# error: [invalid-type-form] "List literals are not allowed in this context in a type expression"
# error: [unresolved-reference] "Name `foo` used when not defined"
x: [[foo]]

4.3 字符串注解内的未解析引用不再报

反过来说,如果检测到字符串注解本身就是非法类型形式,ty 会抑制其中未解析引用的诊断——因为字符串化的注解根本不会在运行期求值,报未定义名字只会增加噪音:

# error: [invalid-type-form] "List literals are not allowed in this context in a type expression"
x: "[[foo]]"   # 不再额外报 "Name `foo` used when not defined"

从源码看,这与 ignore_runtime_errors 逻辑相关:当表达式处于延迟(deferred)状态或位于 stub 文件、TYPE_CHECKING 块中时,运行期错误被有意忽略(见 type_expression.rs)。

4.4 字符串注解中的非法下标操作数不得求值

一个更微妙的场景:字符串注解中的非法下标操作数不得被求值——特别是 lambda 默认值、函数式 TypedDict 参数不应产生级联的未解析引用诊断,赋值表达式(walrus)不应导致 panic。运行时文件与 stub 文件行为一致:

from typing_extensions import TypedDict

# error: [invalid-type-form] "Only simple names and dotted names can be subscripted in type expressions"
a: "(lambda value=missing: None)[int]"
# error: [invalid-type-form] "Only simple names and dotted names can be subscripted in type expressions"
b: "(lambda value=(name := int): None)[int]"
# error: [invalid-type-form] "Only simple names and dotted names can be subscripted in type expressions"
c: "TypedDict('T', {}, extra_items=missing)[int]"

即:虽然 missingname 从未定义,但 ty 只报一条 invalid-type-form,绝不会叠加 unresolved-reference 或崩溃。

4.5 字符串注解中的非法下标参数

即使下标操作数是合法名字,当它不可特化(non-generic)时,ty 同样拒绝特化且不求值其参数,同时保留 type[...] 参数原有的回退类型;stub 文件使用相同的错误恢复:

from typing import Any, Tuple

# error: [invalid-type-form] "Non-generic class `int` cannot be specialized"
a: "int[(name := missing)]"
# error: [invalid-type-form] "Non-generic class `int` cannot be specialized"
b: "type[int[(name := missing)]]"
# error: [invalid-type-form] "Named expressions are not allowed"
c: "type[(name := missing)]"
# error: [invalid-type-form] "Named expressions are not allowed"
d: "type[Any[(name := missing)]]"
# error: [invalid-type-form] "`lambda` expressions are not allowed"
e: "type[Tuple[lambda default=(name := missing): None]]"
# error: [invalid-type-form] "`lambda` expressions are not allowed"
f: "type[lambda default=(name := missing): None]"

4.6 求值注解:完整上报

与字符串注解相反,对于会被求值的注解,ty 会把非法类型参数和求值过程中遇到的错误一并报告:

[environment]
python-version = "3.13"
# error: [invalid-type-form] "Non-generic class `int` cannot be specialized"
# error: [unresolved-reference] "Name `missing` used when not defined"
a: int[(name := missing)]

# error: [invalid-type-form] "Named expressions are not allowed"
# error: [unresolved-reference] "Name `other_missing` used when not defined"
b: type[(other := other_missing)]

# error: [invalid-type-form] "Function calls are not allowed"
# error: [unresolved-reference] "Name `missing_call` used when not defined"
c: type[missing_call()]

五、tuple 特化的两条专门规则

5.1 不允许出现多个解包的可变长元组

当一个 tuple 特化中出现两个及以上解包的变长(variadic)元组时,ty 报错——因为 *tuple[int, ...] 的长度不定,无法与另一个变长元组并列摆放:

[environment]
python-version = "3.11"
from typing import TypeVarTuple, Unpack

Ts = TypeVarTuple("Ts")

def f(
    x: tuple[*tuple[int, ...], *tuple[str, ...]],   # error: "Multiple unpacked variadic tuples are not allowed in a `tuple` specialization"
    x2: tuple[Unpack[tuple[int, ...]], Unpack[tuple[str, ...]]],  # error(Unpack 写法同样拒绝)
    y: tuple[*tuple[int, ...], str, int, *tuple[str, ...]],      # error
    y2: tuple[Unpack[tuple[int, ...]], str, int, Unpack[tuple[str, ...]]],  # error
    # 只要解包的不是变长元组就没问题:
    z: tuple[*tuple[int, ...], *tuple[str]],   # OK
    z2: tuple[Unpack[tuple[int, ...]], Unpack[tuple[str]]],  # OK
):
    reveal_type(x)   # revealed: tuple[int | str, ...]
    reveal_type(z)   # revealed: tuple[*tuple[int, ...], str]

T1 = tuple[int, *Ts, str, *Ts]  # error(同一 TypeVarTuple 出现两次也是多个变长解包)

def func3(t: tuple[*Ts]):
    t5: tuple[*tuple[str], *Ts]      # OK
    t6: tuple[*tuple[str, ...], *Ts] # error

观察 revealed 类型可发现 ty 的合并策略:两个变长元组解包时报错,但如果错误恢复后仍要给出类型,则合并为 tuple[int | str, ...];而 z 这类合法写法被精确表示为 tuple[*tuple[int, ...], str]

5.2 省略号 ... 只能出现在二元特化的第二位

tuple[int, ...] 是合法写法,但 ... 一旦出现在其他位置即报错:

t1: tuple[int, ...]      # OK
t2: tuple[int, int, ...] # error: "`...` can only be used as the second element in a two-element `tuple` specialization"
t3: tuple[...]           # error(同上)
t4: tuple[..., int]      # error(同上)
t5: tuple[int, ..., int] # error(同上)
t6: tuple[*tuple[str], ...]        # error: "`...` cannot be used after an unpacked element"
t7: tuple[*tuple[str, ...], ...]   # error(同上)

def invalid_typevartuple_ellipsis(
    starred: tuple[*Ts, ...],            # error: "`...` cannot be used after an unpacked element"
    unpacked: tuple[Unpack[Ts], ...],    # error(同上)
) -> None: ...

六、字符串注解与集合字面量:同样的非法性,同样的恢复

文档确认:字符串(前向引用)注解中的非法 AST 节点同样会被拒绝,错误消息与直接注解完全一致,且错误恢复后类型回退为 Unknown

async def outer_async():
    def _(
        a: "1",          # "Int literals are not allowed in this context in a parameter annotation"
        b: "2.3",        # "Float literals are not allowed in parameter annotations"
        e: "1 and 2",    # "Boolean operations are not allowed in parameter annotations"
        g: "(foo := 1)", # "Named expressions are not allowed in parameter annotations"
        i: "lambda: 1",  # "`lambda` expressions are not allowed in parameter annotations"
        k: "await baz()",# "`await` expressions are not allowed in parameter annotations"
        n: "bar()",      # "Function calls are not allowed in parameter annotations"
        o: "[1, 2, 3][1:2]",  # "Only simple names and dotted names can be subscripted in parameter annotations"
        p: list[int].append,  # "Only simple names, dotted names and subscripts can be used in parameter annotations"
        q: list[list[int].append],  # 同上,但恢复类型为 list[Unknown]
    ):
        reveal_type(p)  # revealed: Unknown
        reveal_type(q)  # revealed: list[Unknown]

一个值得注意的差异:在字符串注解里访问属性 list[int].append 也是非法的,只能使用"简单名字、点分名字和下标",这与直接注解中"只能下标简单名字和点分名字"的措辞略有不同。

集合类 AST 节点(字典/集合/推导式/生成器)同样是非法参数注解,且 ty 会给出贴心提示:

[environment]
python-version = "3.12"
def _(
    a: {1: 2},                       # "Dict literals are not allowed in parameter annotations"
    b: {1, 2},                       # "Set literals are not allowed in parameter annotations"
    c: {k: v for k, v in [(1, 2)]},  # "Dict comprehensions are not allowed in parameter annotations"
    d: [k for k in [1, 2]],          # "List comprehensions are not allowed in parameter annotations"
    e: {k for k in [1, 2]},          # "Set comprehensions are not allowed in parameter annotations"
    f: (k for k in [1, 2]),          # "Generator expressions are not allowed in parameter annotations"
    g: [int, str],                   # "List literals are not allowed in this context in a parameter annotation"
    h: (int, str),                   # "Tuple literals are not allowed... Did you mean `tuple[int, str]`?"
    i: (),                           # "Tuple literals are not allowed... Did you mean `tuple[()]`?"
):
    reveal_type(a)  # revealed: Unknown
    ...

# 类型参数默认值 / 类型别名默认值中的集合字面量同样非法
class name_0[name_2: [int]]:   # "List literals are not allowed... Did you mean `list[int]`?"
    pass

class name_4[name_1: [{}]]:    # "List literals are not allowed in this context in a type expression"
    pass

七、常见错误的专用诊断与 unsafe fix

除通用错误外,ty 还针对高频错误提供了专用子诊断与自动修复(unsafe fix)

7.1 把模块当类型用

Python 里"本想用模块中的类,却写了模块名"非常常见。ty 为此给出友好的子诊断,在 foo.pyPIL/Image.pybar.py 三个文件的组合中验证了直接 import 与 from PIL import Image 两种形态:

# foo.py
import datetime
def f(x: datetime): ...  # error: [invalid-type-form]

# PIL/Image.py
class Image: ...

# bar.py
from PIL import Image
def g(x: Image): ...  # error: [invalid-type-form]

7.2 集合字面量修复:[int]list[int]

当集合字面量的意图类型清晰时,ty 不仅报错,还提供"替换为下标内建类型"的修复。以下行为全部有快照(snapshot)佐证:

非法写法 建议替换 说明
[int](单元素列表) list[int] 参数注解与返回注解均提供修复
[int, str](多元素列表) 意图有歧义,不提供建议
()(空元组) tuple[()] 空元组的类型
(int,)(单元素元组) tuple[int] 定长单元素元组
(int, str)(多元素元组) tuple[int, str] 对应元素类型的定长元组
{int: str}(单条目字典) dict[int, str]
{str}(单元素集合) set[str]

以列表修复为例,快照中完整呈现了诊断、Did you mean \list[int]`? 提示、help: Replace with `list[...]`以及note: This is an unsafe fix and may change runtime behavior的完整输出(例如将x: [int]改写为x: list[int]`)。所有修复都标注为 unsafe fix,因为把容器字面量改写为下标表达式会改变运行期行为。

括号保留策略

重写元组字面量时,ty 会保留首尾元素的括号(包括嵌套括号和整个元组的外层括号):

# fmt: off
first: ((int), str)      # → tuple[(int), str]
last: (int, (str))       # → tuple[int, (str)]
single: (((int)),)       # → tuple[((int))]
outer: (((int), (str)))  # → (tuple[(int), (str)])

字典的键与值、集合元素、列表元素同样保留括号;对于要求括号的表达式(如 yield),修复也会保留括号以避免产生语法错误:

def generator():
    yielded_key: {(yield int): str}      # → dict[(yield int), str]
    yielded_value: {int: (yield str)}    # → dict[int, (yield str)]
    yielded_element: {(yield int)}       # → set[(yield int)]

7.3 修复的版本与作用域限制

ty 对修复的可用性做了非常细致的约束,每一条都有专门的测试快照:

  • Python 3.9 及以上才提供集合字面量修复:内建容器类型在 Python 3.8 上不可下标,因此在 python-version = "3.8" 环境下只报错、不提供会把代码改成 list[...] 的修复。
  • Python 3.10 时对 starred 元素不提供修复[*types](*types,){*types} 在 3.11 之前无法改写为下标(下标内不支持 starred),此时仅提示 Did you mean \list[tuple[Unknown, ...]]`?` 之类。
  • 多行集合字面量不提供修复:多行字面量内部可能含有注释,替换分隔符会丢失注释,因此拒绝自动修复(list.pytuple.pydict.pyset.py 四个文件分别覆盖)。
  • 类属性不遮蔽方法体中的内建名class Container: set = 42 中的 set 在方法体内不可见,不影响 value: {int} 被改写为 set[int]
  • 嵌套注解作用域中的类属性遮蔽内建:泛型类型别名的类型参数作用域可以访问外层类的属性,因此 class C: list = 42type Alias[T] = [int] 仍会提供 list[int] 修复(文档以 TODO 注明这其实是 visible_ancestor_scopes 跳过类作用域导致的一个已知误报)。
  • 项目级 __builtins__.pyi 覆盖:如果项目提供了 list: object__builtins__.pyi,则 [int] 不再提供修复(因为改写后的 list 不再指向内建),而未被覆盖的 set 仍可修复。
  • 字符串注解不提供修复:从带引号注解解析出的集合字面量没有可直接重写的源范围,因此 "[int]""(int, str)" 等只报错、不修复。

7.4 callable 特殊诊断

小写 callable(内建可调用对象类型)被用作类型表达式时,参数与返回注解各报一条 invalid-type-form

def decorator(fn: callable) -> callable:  # error: [invalid-type-form] ×2
    return fn

八、只在 Literal[...] 里合法的节点:自动包裹修复

x: 42 是非法的——要把 x 限制为值 42,应写 x: Literal[42]。ty 对整数字面量、bytes 字面量、布尔字面量,以及无法解析为前向注解的字符串,提供把值包裹进 Literal[...] 的 unsafe fix(必要时自动导入 Literal)。此功能在 python-version = "3.8" 下验证(typing.Literal 自 3.8 起可用)。

8.1 修复形态

def bad(
    a: 42,                 # → a: Literal[42]
    b: b"42",              # → b: Literal[b"42"]
    c: True,               # → c: Literal[True]
    d: "invalid syntax",   # invalid-syntax-in-forward-annotation → d: Literal["invalid syntax"]
): ...

注意 d 走的是另一个 lint invalid-syntax-in-forward-annotation(前向注解语法错误),其修复同样是把原始字符串包裹进 Literal

8.2 保留字面量原始拼写

修复会保留数字的字面量拼写、bytes 的转义序列和字符串定界符,即使其语义值不同:

def bad(
    integer: (0x_FF),      # → (Literal[0x_FF]),提示文本中显示值 255
    data: b"\x41",         # → Literal[b"\x41"]
    quoted: "False",       # → "Literal[False]"(前向注解内包裹)
    string: """can't parse this""",  # → Literal["""can't parse this"""]
): ...

8.3 智能导入复用与遮蔽处理

  • 复用已有别名from typing import Literal as L 时修复写作 L[True]import typing as t 时写作 t.Literal[42]

  • Literal 被遮蔽时改用限定名def qualified(Literal: int): 内修复插入 import typing 并写作 typing.Literal[True]

  • 两个名字都被占用时放弃修复def unavailable(Literal: int, typing: int): 内只报错、不提供修复。

  • Python 3.7 的特殊处理typing 中没有 Literal。仅凭 typeshed 里 vendored 的 typing_extensions stub 不足以支撑修复——修复不引入来自 typing_extensions 的导入,除非:

    1. typing_extensions 被声明为直接依赖(通过 [dependency-metadata] 中的 projects / distributions / module-owners 配置);
    2. 该模块确实存在于 site-packages.venv);
    3. 安装的版本确实导出 Literal 符号。

    三个条件缺一不可:缺少运行期导出(空 typing_extensions.py)时不给修复;模块不在 site-packages 时不给修复;父项目声明了依赖而嵌套项目未声明时,嵌套项目同样不给修复。这些案例分别由 /.venv/<path-to-site-packages>/typing_extensions.pymain.py / member/main.py 的组合测试覆盖。

九、这些测试是怎么跑起来的:mdtest 框架

invalid.md 是 ty 的 mdtest 测试语料:crates/ty_python_semantic 目录下的 mdtest.py 负责提取 Markdown 中的 Python 代码块(以及 runtime.py / stub.pyi / main.py 等命名文件)并运行类型检查。代码中的三类断言语法值得关注:

  • # error: [invalid-type-form] "具体消息":断言此行触发 invalid-type-form,且消息与双引号内容匹配;
  • reveal_type(x) # revealed: Unknown:断言推断出的类型;
  • # snapshot: invalid-type-form 配合 <!-- snapshot-diagnostics --> 与独立的 \``snapshot代码块:把完整诊断快照写入测试输出,验证Did you mean ...?help:note: This is an unsafe fix...` 等细节。

[environment] TOML 块用于声明测试的 python-version(3.7 到 3.13 均有覆盖)、虚拟环境路径与 [dependency-metadata] 依赖元数据——这正是文档中大量"按 Python 版本条件化"行为的来源。同目录下的 annotations/ 还包含 literal.mdunion.mdstring.mdgeneric_alias.mdcallable.md 等 20 余个专题测试文件,invalid.md 是其中覆盖最广的一份。

十、总结:ty 对无效类型表达式的处理原则

纵观整份测试文档,可以提炼出 ty 处理 invalid-type-form 的四条设计原则:

  1. 语法形式优先:类型表达式只认语法形态(名字、下标、联合、可选等),不认求值结果——type[int]Literal[True]、推导式、调用、yield/await 全都被拒;
  2. 分级报错:外层非法时抑制内层诊断,但真正的运行期错误(未定义名字)仍上报;字符串注解内部的未解析引用则一律静默;
  3. 可操作修复:能确定意图的就提供 unsafe fix——集合字面量改写为 list[...] / tuple[...] / dict[...] / set[...],字面量值包裹为 Literal[...],并充分考虑 Python 版本、名称遮蔽、依赖元数据等约束;
  4. 优雅恢复:即使节点非法,也要给出尽可能精确的回退类型(如 int | Unknownlist[Unknown]),保证类型检查器不会因为一处注解崩坏而放弃整个文件。

对于开发者而言,这份文档的实用价值在于:它精确划定了 ty(以及 typing 规范)认可的"类型表达式语法"边界。当你看到 invalid-type-form 报错时,优先怀疑注解里混入了运行期值而非类型——并用 Did you mean 提示与 unsafe fix 快速修正即可。

相关文件索引

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.15 K
2.78 K
kernelkernel
deepin linux kernel
C
34
18
docsdocs
暂无描述
Markdown
904
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
931
1.86 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
862
1.36 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.95 K
1.03 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.38 K
1.47 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
535
605
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
549
398
leetcodeleetcode
🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解
Markdown
77
23