从 mypy / pyright 迁移到 ty:规则映射、严格模式与迁移实战指南
【免费下载链接】tyAn extremely fast Python type checker and language server, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ty2/ty
迁移到新类型检查器时,最大的痛点往往不是工具本身,而是你早已习惯的那套“错误码方言”:# type: ignore[code]、disable_error_code、reportXyz = "none"……这些都要逐一重新学习。本文以 ty 官方迁移指南为核心,系统对比 mypy / pyright / basedpyright 与 ty 的规则体系,给出可直接落地的严格模式配置,并附上完整的三方规则映射表。读完你可以在一个下午内完成项目迁移,并借机把类型检查严格度提升到高于以往的水平。
迁移速览:三种检查器的“方言”对照
ty 的项目定位是“用 Rust 编写、极快的 Python 类型检查器与语言服务器”,它不是一个兼容 mypy 配置的克隆品,而是建立在同一套 PEP 484 类型体系之上、拥有自己规则命名与严重度体系的新工具。因此迁移的第一课,是把你在 mypy / pyright 里学到的配置词汇翻译成 ty 的词汇。
抑制注释(suppression comments)的对应关系
| 检查器 | 行内抑制语法 |
|---|---|
| mypy | # type: ignore[code] |
| pyright | # pyright: ignore[reportXyz] |
| ty | # ty: ignore[rule] |
ty 的抑制注释细节见 docs/suppression.md,这里给出最常用的几种形态:
- 抑制单行违规:
a = 10 + "test" # ty: ignore[unsupported-operator] - 抑制跨行违规(放在违规语句的首行或末行均可):
sum_three_numbers( # ty: ignore[missing-argument] 3, 2 ) # 或放在末行 sum_three_numbers( 3, 2 ) # ty: ignore[missing-argument]- 一行内抑制多条规则,用逗号分隔:
sum_three_numbers("one", 5) # ty: ignore[missing-argument, invalid-argument-type] - 在文件开头(任何 Python 代码之前)放一行独立注释,可抑制整个文件的特定规则:
# ty: ignore[invalid-argument-type]ty 还兼容 PEP 484 标准的type: ignore注释。值得特别注意的是它的混合能力:
# 忽略下一行所有类型错误 sum_three_numbers("one", 5) # type: ignore # 同一个注释里同时写 mypy 错误码和 ty 规则 sum_three_numbers("one", 5, 2) # type: ignore[arg-type, ty:invalid-argument-type]其中type: ignore[ty:<rule>]只抑制匹配的 ty 规则,不带ty:前缀的错误码会被忽略——这使你可以在一行注释里同时兼容多个类型检查器,非常适合迁移过渡期“mypy/ty 并存”的场景。
两个与迁移直接相关的细节:
- 与其他工具的注释共存:一行上可以同时挂多个注释,例如
result = calculate() # ty: ignore[invalid-argument-type] # fmt: skip。 @no_type_check:ty 支持用typing.no_type_check装饰器抑制函数体内的全部违规,但不支持装饰整个类。
如果你的项目里还留着大量旧式# type: ignore注释,可以留意respect-type-ignore-comments配置项(默认true):将其设为false后,type: ignore会被当作普通注释,必须改用ty: ignore才能抑制错误,适合希望完全切到 ty 语法的团队。配置方式见 docs/reference/configuration.md。
全局禁用规则的对应关系
- mypy 的
disable_error_code = [...]与 pyright 的reportXyz = "none",在 ty 中统一对应为把规则级别设为"ignore":
[tool.ty.rules] possibly-unresolved-reference = "warn" division-by-zero = "ignore"在 pyproject.toml 中 ty 使用[tool.ty.rules]顶层键;如果你用独立的ty.toml,则写作[rules]。可用的严重度只有三档:
ignore:禁用该规则warn:启用,产生警告级诊断error:启用,产生错误级诊断
注意:pyright 的"information"级别与 basedpyright 的"hint"级别在 ty 中没有直接对应——迁移时统一用warn代替。默认情况下,只要存在warn或error级诊断,ty 就以退出码 1 结束;若希望只有警告时仍以 0 退出,可设[tool.ty.terminal] error-on-warning = false(详见 docs/reference/configuration.md)。
命令行层的“方言”
除了配置文件,ty 的命令行也提供了规则级别的快捷控制(详见 docs/reference/cli.md):
ty check --error=all # 把所有规则设为 error 级别 ty check --warn=rule-name # 把指定规则设为 warn,可重复传 ty check --ignore=rule-name # 禁用指定规则,可重复传这些选项等价于 mypy 命令行中的--disable-error-code/--enable-error-code之类的能力,且--config选项传入的单个设置优先级高于所有配置文件。
无类型代码的检查策略差异
这是迁移中最容易踩坑的行为差异,三条规则必须记住:
- ty 没有对应
disallow_untyped_defs(mypy)/no-untyped-def、reportMissingParameterType、reportUnknownParameterType(pyright)的规则。ty 不会对未标注的函数参数、返回值或变量报错,而是把这类符号的类型推断为Unknown(详见 docs/reference/typing-faq.md 中 “Why doesn't ty warn about missing type annotations?” 一节)。如果你需要强制补全注解,等价物在 Ruff 侧:flake8-annotations(ANN)规则组,例如ANN001(函数参数缺注解)、ANN201(公共函数缺返回注解)等。 - ty 无条件检查无注解函数的函数体,因此不存在与 mypy
check_untyped_defs对应的 ty 规则——它本来就是 ty 的默认行为,且目前不可配置。pyright 侧的对等概念是analyzeUnannotatedFunctions = true(这也是 pyright 的默认值)。 - ty 没有
--check-untyped-defs或strictListInference这类开关,因为它们对应的行为(检查无注解函数体、对列表做元素级推断)同样是 ty 的默认行为。例如在 pyright 的非严格模式下[1, "foo"]会被推断为list[Unknown],而 ty 直接推断为list[int | str]。
更严格的检查:ty 的默认严格度与推荐配置
mypy 和 pyright 的strict模式不只是“多开几个错误码”,它还会根本性地改变类型推断与检查的工作方式:mypy 的 strict 包含--check-untyped-defs(否则无注解函数完全不被检查),pyright 的 strict 包含strictListInference(否则列表字面量被推断为list[Unknown])。
ty 的默认模式在诸多方面已经比两者的默认(甚至 strict)模式更严格,原因有二:
- 那些在 mypy/pyright 中需要显式开启的行为(如检查无注解函数体)是 ty 的默认行为且不可配置;
- 几乎所有 ty 规则默认都是开启的,默认关闭的规则通常是因为“过于主观”或“误报较多”。
注意:ty 目前没有名为--strict的标志(见 docs/reference/typing-faq.md 的 “Does ty have a strict mode?” 一节),但你可以用配置近似出其他检查器的--strict。
推荐配置一:近似 mypy / pyright 的--strict
[tool.ty.rules] dynamic-function-decorator-return = "error" missing-type-argument = "error" possibly-unresolved-reference = "warn" unsound-return-statement = "error" [tool.ruff.lint] extend-select = ["ANN", "PYI"] preview = true这份配置的作用拆解如下:
- 开启 ty 中默认关闭的四条规则:
dynamic-function-decorator-return(对应 mypyuntyped-decorator/ pyrightreportUntypedFunctionDecorator)、missing-type-argument(对应 mypytype-arg/ pyrightreportMissingTypeArgument)、possibly-unresolved-reference(对应 mypypossibly-undefined/ pyrightreportPossiblyUnboundVariable)、unsound-return-statement(对应 mypyno-any-return); - 把 Ruff 的默认规则扩展到
ANN(flake8-annotations)与PYI(flake8-pyi)两个类别——它们都专注于“更有效地给代码加注解”; - 开启 Ruff preview 模式,使
PYI033(legacy type comment,对应 pyrightreportTypeCommentUsage)同时检查.py文件。
推荐配置二:比 strict 更严格
[tool.ty.rules] blanket-ignore-comment = "error" dynamic-function-decorator-return = "error" missing-type-argument = "error" possibly-unresolved-reference = "warn" unsound-assignment = "error" unsound-return-statement = "error" unsound-yield = "error" unsupported-dynamic-base = "warn" # NOTE: 以下规则已知存在大量(基本无法避免的)误报,启用风险自负! division-by-zero = "warn" possibly-missing-attribute = "warn" possibly-missing-import = "warn" [tool.ty.analysis] strict-equality-semantics = true strict-generic-narrowing = true [tool.ruff.lint] extend-select = ["ANN", "PYI", "PGH003"] preview = true与第一份配置相比,它额外启用了:
blanket-ignore-comment(对应 mypyignore-without-code/ basedpyrightreportIgnoreCommentWithoutRule),要求所有ignore注释都写明规则名;Ruff 侧的PGH003同样禁止裸# type: ignore;unsound-assignment(仅针对变量)与unsound-yield(这两条在 mypy / pyright 中没有直接对应,见映射表);unsupported-dynamic-base(目前标记为warn,因其可能误报);- 两条
[tool.ty.analysis]严格化开关,它们的语义如下(完整说明见 docs/reference/configuration.md):
strict-equality-semantics(默认false):控制相等性检查的类型推断与收窄行为。默认情况下 ty 会做出若干符合直觉但不完全 sound 的假设,例如:
from typing import Literal def parse(value: str) -> Literal["a"] | None: # 开启 strict-equality-semantics = true 后,这里不发生收窄, # 并在 return 语句上报错。 if value == "a": return value return None不 sound 的原因在于:Literal["a"]只能被恰好是str的实例占据,而str的子类(以及StrEnum)默认与"a"比较相等,却不属于Literal["a"]。开启该选项后,ty 对相等性推断更保守:不把str收窄为Literal["a"],也不会假设子类不会覆写__eq__/__ne__(从而不再把Foo | None在x == other后收窄为Foo)。该选项同样影响in检查与match值模式中的收窄。
strict-generic-narrowing(默认false):控制未特化泛型类在isinstance()/issubclass()、match类模式、TypeIs检查中的收窄方式。开启后,isinstance(value, list)会把object收窄为Top[list[Unknown]](所有可能list特化的无限并集,迭代元素类型为object);关闭时使用渐进式泛型收窄,尽可能保留原类型参数——例如把Sequence[int]收窄为list[int],无特化可循时才收窄为list[Unknown]。
文中PGH003、ANN、PYI等 Ruff 规则均属于 Ruff lint 范畴,与 ty 的规则体系互补:ty 负责类型正确性诊断,Ruff 负责注解完整性与代码风格。若你尚未在项目中引入 Ruff,可参考仓库根目录的 pyproject.toml 中的[tool.ruff.lint]配置进行扩展。
规则映射总表:mypy / pyright → ty / Ruff
映射表是迁移时的核心查表工具。阅读方法如下:
- ty or Ruff rule 列:优先给出 ty 规则名(完整清单见 docs/reference/rules.md,在
[tool.ty.rules]下配置);若该检查没有 ty 规则而 Ruff 提供了等价覆盖,则给出 Ruff 规则或规则组; - Mypy error code 列:传给
# type: ignore[<code>]或disable_error_code的错误码。部分 ty 规则会以 mypy 的兜底错误码(misc、assignment、valid-type)形式出现,这类映射是故意放宽的; - Pyright diagnostic 列:
pyrightconfig.json或[tool.pyright]中的report*设置。
同一诊断可能在多行出现(对应不同子情形);空白单元格表示该检查器没有直接对应物(要么不产生该诊断,要么已被折叠进其他 ty 规则对应的更宽泛类别中)。
有 ty 规则的映射
| ty / Ruff 规则 | mypy 错误码 | pyright / basedpyright 诊断 |
|---|---|---|
| abstract-and-final-method | misc | |
| abstract-method-in-final-class | misc | reportGeneralTypeIssues |
| call-abstract-method | reportAbstractUsage | |
| call-non-callable | operator、misc | reportCallIssue、reportOptionalCall |
| conflicting-declarations | no-redef | reportRedeclaration |
| conflicting-metaclass | metaclass | reportGeneralTypeIssues |
| cyclic-class-definition | misc | reportGeneralTypeIssues |
| dataclass-field-order | misc | reportGeneralTypeIssues |
| deprecated | deprecated | reportDeprecated |
| disjoint-cast | reportInvalidCast(仅 basedpyright) | |
| division-by-zero | ||
| duplicate-base | misc | reportGeneralTypeIssues |
| duplicate-kw-only | misc | |
| dynamic-function-decorator-return | untyped-decorator | reportUntypedFunctionDecorator(仅 Unknown 返回) |
| empty-body | empty-body | reportReturnType(...函数体豁免) |
| final-on-non-method | misc | reportGeneralTypeIssues |
| final-without-value | misc | reportGeneralTypeIssues |
| inconsistent-mro | misc | reportGeneralTypeIssues |
| index-out-of-bounds | misc | reportGeneralTypeIssues |
| invalid-argument-type | arg-type、index、type-var、typeddict-item | reportArgumentType、reportAssignmentType |
| invalid-assignment | assignment、list-item、dict-item | reportAssignmentType |
| invalid-assignment(仅不兼容的方法替换) | method-assign(拒绝一切方法赋值) | reportAttributeAccessIssue(仅不兼容替换) |
| invalid-assignment(TypedDict 键值) | typeddict-item | reportGeneralTypeIssues |
| invalid-assignment(只读 TypedDict 键) | typeddict-readonly-mutated | reportTypedDictNotRequiredAccess(仅只读修改) |
| invalid-attribute-access | misc | reportAttributeAccessIssue |
| invalid-attribute-override | misc | reportIncompatibleVariableOverride(仅类/实例变量) |
| invalid-await | misc | reportGeneralTypeIssues |
| invalid-base | valid-type、misc | reportGeneralTypeIssues |
| invalid-context-manager | misc、attr-defined、union-attr | reportGeneralTypeIssues、reportOptionalContextManager |
| invalid-dataclass | misc | |
| invalid-exception-caught | misc | reportGeneralTypeIssues |
| invalid-explicit-override | misc | reportGeneralTypeIssues |
| invalid-frozen-dataclass-subclass | misc | reportGeneralTypeIssues |
| invalid-key | typeddict-item、typeddict-unknown-key | reportGeneralTypeIssues、reportAssignmentType、reportCallIssue |
| invalid-legacy-type-variable | misc、valid-type | reportGeneralTypeIssues、reportInvalidTypeForm |
| invalid-metaclass | metaclass | |
| invalid-method-override | override | reportIncompatibleMethodOverride |
| invalid-module-getattr-call | ||
| invalid-newtype | valid-newtype、misc | reportGeneralTypeIssues、reportArgumentType |
| invalid-overload | no-overload-impl、misc | reportNoOverloadImplementation、reportInconsistentOverload |
| invalid-parameter-default | assignment | reportArgumentType |
| invalid-protocol | misc | reportGeneralTypeIssues |
| invalid-raise | misc | reportGeneralTypeIssues |
| invalid-return-type | return、return-value | reportReturnType |
| invalid-type-arguments | misc、type-var | reportInvalidTypeArguments |
| invalid-type-form | valid-type | reportInvalidTypeForm、reportGeneralTypeIssues |
| invalid-type-guard-definition | narrowed-type-not-subtype、valid-type | reportGeneralTypeIssues |
| invalid-type-variable-bound | valid-type、misc | reportGeneralTypeIssues |
| invalid-type-variable-constraints | valid-type、misc | reportGeneralTypeIssues |
| invalid-type-variable-default | misc | reportGeneralTypeIssues |
| invalid-typed-dict-field | misc | reportIncompatibleVariableOverride |
| invalid-yield | misc | reportReturnType |
| isinstance-against-protocol | misc | reportArgumentType、reportGeneralTypeIssues |
| isinstance-against-typed-dict | misc | reportArgumentType、reportGeneralTypeIssues |
| mismatched-type-name | name-match、misc | reportGeneralTypeIssues |
| missing-argument | call-arg | reportCallIssue |
| missing-override-decorator | explicit-override | reportImplicitOverride |
| missing-type-argument | type-arg | reportMissingTypeArgument |
| missing-typed-dict-key | typeddict-item | reportAssignmentType |
| no-matching-overload | call-overload | reportCallIssue |
| not-iterable | misc、attr-defined、union-attr | reportGeneralTypeIssues、reportOptionalIterable |
| not-subscriptable | index | reportIndexIssue、reportOptionalSubscript |
| override-of-final-method | misc | reportIncompatibleMethodOverride |
| override-of-final-variable | misc | reportGeneralTypeIssues |
| parameter-already-assigned | misc、call-arg | reportCallIssue |
| positional-only-parameter-as-kwarg | call-arg | reportCallIssue |
| possibly-missing-attribute | ||
| possibly-unresolved-reference | possibly-undefined | reportPossiblyUnboundVariable |
| redundant-cast | redundant-cast | reportUnnecessaryCast |
| redundant-condition(仅确定真值性) | truthy-bool | |
| redundant-condition(仅函数对象) | truthy-function | reportUnnecessaryComparison |
| redundant-condition、redundant-condition-strict | redundant-expr(注意:与 mypy 不同,ty 只在if测试等布尔条件中检查and/or,不检查用于计算值的情形) | |
| redundant-condition、redundant-condition-strict | comparison-overlap(仅布尔条件中;其余情形尚未实现) | reportUnnecessaryComparison、reportUnnecessaryContains(均仅布尔条件中) |
| redundant-condition、redundant-condition-strict | unreachable(布尔条件中导致不可达代码的情形;其余尚未实现) | reportUnreachable(仅布尔条件中导致不可达代码的情形) |
| redundant-condition-strict | reportUnnecessaryIsInstance(布尔条件中) | |
| subclass-of-final-class | misc | reportGeneralTypeIssues |
| too-many-positional-arguments | call-arg | reportCallIssue |
| type-assertion-failure | assert-type | reportAssertTypeFailure |
| unbound-type-variable | valid-type | reportGeneralTypeIssues |
| undefined-reveal | unimported-reveal | |
| unknown-argument | call-arg | reportCallIssue |
| unresolved-attribute | attr-defined、union-attr | reportAttributeAccessIssue、reportFunctionMemberAccess、reportOptionalMemberAccess |
| unresolved-import | import-not-found | reportMissingImports |
unresolved-reference + RuffF823 | name-defined、used-before-def | reportUndefinedVariable、reportUnboundVariable |
| unsound-assignment(仅变量) | ||
| unsound-return-statement | no-any-return | |
| unsound-yield | ||
| unsupported-operator | operator | reportOperatorIssue、reportOptionalOperand |
| unused-awaitable(仅原生协程) | unused-coroutine、unused-awaitable | reportUnusedCoroutine |
| unused-ignore-comment | unused-ignore | reportUnnecessaryTypeIgnoreComment |
| unused-type-ignore-comment | unused-ignore | reportUnnecessaryTypeIgnoreComment |
blanket-ignore-comment + RuffPGH003 | ignore-without-code | reportIgnoreCommentWithoutRule(仅 basedpyright) |
由 Ruff 规则覆盖的映射
| ty / Ruff 规则 | mypy 错误码 | pyright / basedpyright 诊断 |
|---|---|---|
RuffF631 | reportAssertAlwaysTrue | |
RuffB006、B008(部分覆盖;排除不可变注解与调用) | reportCallInDefaultInitializer | |
RuffF811、I001(部分覆盖;可能漏掉独立 import 块) | reportDuplicateImport | |
RuffISC001、ISC002 | reportImplicitStringConcatenation | |
RuffW605 | reportInvalidStringEscapeSequence | |
RuffPYI010、PYI017、PYI048、PYI052 | reportInvalidStubStatement | |
RuffSLF001、PLC2701(部分覆盖;PLC2701需 preview) | reportPrivateUsage | |
RuffN804、N805 | reportSelfClsParameterName | |
RuffPYI033(.py文件需 preview) | reportTypeCommentUsage | |
RuffF822、PLE0604、PLE0605、PYI056 | reportUnsupportedDunderAll | |
RuffPYI024 | reportUntypedNamedTuple | |
RuffARG系列 | reportUnusedParameter(仅 basedpyright) | |
RuffB025(仅重复异常处理器;其余情形跟踪中) | reportUnusedExcept | |
RuffB015、B018 | reportUnusedExpression | |
RuffF401 | reportUnusedImport | |
RuffF841(仅函数局部变量) | reportUnusedVariable | |
RuffF403 | reportWildcardImportFromLibrary | |
RuffANN401(仅函数注解) | explicit-any | reportExplicitAny(仅 basedpyright) |
RuffANN系列 | no-untyped-def | reportMissingParameterType、reportUnknownParameterType |
尚未实现:迁移时请保持心理预期
原文档明确指出,mypy 和 pyright 的若干检查 ty 尚未实现。映射表中已标出“None yet”的行,这里汇总最常遇到的几类:
| ty 侧状态 | mypy 错误码 | pyright / basedpyright 诊断 |
|---|---|---|
| 实例化抽象类尚无规则 | abstract | reportAbstractUsage |
| 向需要具体类的位置传抽象类:暂无直接等价实现计划 | type-abstract | |
通过super()调用抽象方法尚无规则 | safe-super | reportAbstractUsage |
| 尚无规则(跟踪中) | reportConstantRedefinition、reportImportCycles、reportIncompleteStub、reportInconsistentConstructor、reportInvalidTypeVarUse、reportMatchNotExhaustive、reportMissingModuleSource、reportMissingSuperCall、reportMissingTypeStubs、reportOverlappingOverload、reportPrivateImportUsage、reportPropertyTypeMismatch、reportTypedDictNotRequiredAccess(非必需键访问)、reportUnhashable、reportUninitializedInstanceVariable、reportUnusedClass、reportUnusedFunction、reportUnusedCallResult 等 | |
| 尚无规则 | reportUnknownArgumentType、reportUnknownLambdaType、reportUnknownMemberType、reportUnknownVariableType、reportUntypedBaseClass、reportUntypedClassDecorator | |
| 尚无规则 | var-annotated、func-returns-value、no-any-unimported、truthy-iterable | |
| 尚无规则(跟踪中) | no-untyped-call、import-untyped、mutable-override、overload-cannot-match、overload-overlap、exhaustive-match、attr-defined(由--no-implicit-reexport扩展)、type-var |
其中与“Unknown 相关”的一族(reportUnknown*)尤其值得注意:它对应的是 pyright 对“类型未知”的告警,而 ty 的哲学是用Unknown渐进类型在无注解代码中避免误报(详见 docs/reference/typing-faq.md 对Unknown类型的解释)——这解释了为什么 ty 选择不实现这些检查。ty 的完整规则清单(包括上表中没有直接对应物的规则)见 docs/reference/rules.md。
迁移 FAQ 与常见坑
以下问题在原文档及配套 FAQ(docs/reference/typing-faq.md)中有更完整的讨论,这里给出与迁移直接相关的要点:
- ty 没有
--strict标志,但默认就相当严格:用本文第二节的两份配置可以近似甚至超越其他检查器的 strict 模式。 - “为什么 ty 不警告缺注解?”:这是设计决策而非缺陷——ty 把缺注解符号推断为
Unknown,继续提供其余有用的诊断;需要强制注解时用 RuffANN规则组。 - 未使用的抑制注释:开启
unused-ignore-comment规则后,ty 会报告未生效的ty: ignore与type: ignore注释。这类违规只能用# ty: ignore[unused-ignore-comment]抑制,不能用裸# ty: ignore或# type: ignore(见 docs/suppression.md)。迁移旧代码时,历史遗留的type: ignore很容易触发此规则,需要逐个清理。 - 迁移过渡期可以双跑:
ty check与旧检查器并行运行,利用type: ignore[arg-type, ty:invalid-argument-type]这种混合注释逐步替换,直到确认 ty 的诊断稳定后再完全切换。 --error=all慎用:文档明确建议不要用--error=all一把梭,因为默认关闭的规则多为“主观”或“误报多”的规则;按需开启是更稳妥的路径。
迁移检查清单
- 用
# ty: ignore[rule]替换# type: ignore[code]与# pyright: ignore[reportXyz];过渡期可用混合注释(type: ignore[ty:<rule>])与旧检查器共存。 - 把
disable_error_code/reportXyz = "none"迁移为[tool.ty.rules]下的"ignore";pyright 的"information"、basedpyright 的"hint"一律用"warn"。 - 需要强制注解时引入 Ruff
ANN(必要时再加PYI、PGH003),并开启 preview 以获得PYI033等新能力。 - 按第二节推荐配置逐条审视默认关闭的规则,优先启用
dynamic-function-decorator-return、missing-type-argument、unsound-return-statement等误报可控的规则;division-by-zero、possibly-missing-attribute、possibly-missing-import等高误报规则谨慎启用。 - 在 CI 中先以
ty check替换或并行旧检查器,结合unused-ignore-comment清理历史抑制注释;对照映射表确认尚未实现的检查是否影响你的代码库,必要时用 Ruff 规则补位。
【免费下载链接】tyAn extremely fast Python type checker and language server, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ty2/ty
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考