ARTICLE DETAIL

资讯详情

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

Pyright Type Server Protocol(TSP)协议层源码解析:消息定义、类型体系与虚拟文件重定向

Pyright Type Server Protocol(TSP)协议层源码解析:消息定义、类型体系与虚拟文件重定向 Pyright Type Server ProtocolTSP协议层源码解析消息定义、类型体系与虚拟文件重定向【免费下载链接】pyrightStatic Type Checker for Python项目地址: https://gitcode.com/GitHub_Trending/py/pyright本篇技术指南聚焦 Pyright 静态类型检查器仓库中的protocol子系统深入剖析 Python Type Server ProtocolTSP的协议定义文件、JSON 化表示、类型模型以及 Pyright 专属的虚拟文件重定向扩展并结合仓库源码给出每个消息、枚举与接口的准确定义与调用证据。读完本文你将掌握 TSP 的请求/通知清单、Type判别联合discriminated union的设计思路、快照snapshot机制的语义以及 Pyright 如何通过扩展通知把虚拟文件注入类型服务器从而支撑 Django stub 生成等高级功能。1. protocol 子系统在仓库中的位置与职责在 Pyright 的工程架构地图中protocol是typeServer子系统下的一个独立功能分区。根据 architecture/generated/subsystems/protocol.md 的语义索引该分区共包含 2 个源码文件、2 个叶子符号职责可以概括为typeServerProtocol.ts约 1400 行——定义了一整套 JSON 可序列化的接口interface、请求request与通知notification构成Python Type Server Protocol的本体tspSupplemental.ts62 行——定义Pyright 专属的 TSP 扩展通知用于注册与移除虚拟文件重定向virtual file redirect。这两个文件位于 packages/pyright-internal/src/typeServer/protocol/与它们同目录的还有协议元数据与工具链文件文件作用typeServerProtocol.tsTSP 协议主定义TypeScript 类型 消息命名空间tspSupplemental.tsPyright 私有扩展通知定义tsp.json协议的结构化 JSON 清单含版本号tsp.schema.jsonTSP JSON 清单的 schema 定义generate_json.py从typeServerProtocol.ts自动生成/更新tsp.json的 Python 脚本从文件注释可知这份协议是跨实现共享的typeServerProtocol.ts定义的 TSP 基协议shared by all TSP implementers (e.g., ty-tsp)而tspSupplemental.ts中的通知NOT part of the base TSP只属于 Pyright。这一分层是理解整个协议层的第一把钥匙基础协议保持中立Pyright 特性通过扩展文件叠加。2. 协议的消息面8 个请求与 1 个通知TSP 采用与 LSP 相同的请求/响应 通知消息模型复用vscode-languageserver-protocol的ProtocolRequestType、ProtocolRequestType0与ProtocolNotificationType工具类来构造消息类型。以下清单完整取自 typeServerProtocol.ts 与 tsp.json。2.1 请求clientToServer方法名类型名参数结果typeServer/getComputedTypeGetComputedTypeRequest{ arg: Declaration \| Node; snapshot: number }Type \| undefinedtypeServer/getDeclaredTypeGetDeclaredTypeRequest{ arg: Declaration \| Node; snapshot: number }Type \| undefinedtypeServer/getExpectedTypeGetExpectedTypeRequest{ arg: Declaration \| Node; snapshot: number }Type \| undefinedtypeServer/getPythonSearchPathsGetPythonSearchPathsRequestGetPythonSearchPathsParamsstring[] \| undefinedtypeServer/getSnapshotGetSnapshotRequest无参数numbertypeServer/getSupportedProtocolVersionGetSupportedProtocolVersionRequest无参数stringsemver 格式typeServer/resolveImportResolveImportRequestResolveImportParamsstring \| undefinedtypeServer/connectionConnectionRequestConnectionRequestParamsConnectionRequestResult三个取类型请求是 TSP 的核心源码注释对三者的区分非常清晰见typeServerProtocol.ts的GetComputedTypeRequest、GetDeclaredTypeRequest、GetExpectedTypeRequest命名空间Computed type推断类型基于代码流code flow推断出的类型。例如def foo(a: int | str): if isinstance(a, int): b a 1 # Computed type of b 是 intDeclared type声明类型源码中显式声明的类型。例如参数a的声明类型是int | str。Expected type期望类型上下文期望的类型。例如foo(4)中实参4的期望类型是int | str。其余请求的语义分别为typeServer/getSnapshot—— 获取当前快照编号ProtocolRequestType0无参数返回number。快照是类型服务器某一时刻状态的标识客户端拿到的类型只在产生它的快照下有效。typeServer/getSupportedProtocolVersion—— 获取协议版本号按注释要求应为 semver 格式字符串。typeServer/getPythonSearchPaths—— 返回类型服务器用于解析 Python 模块的搜索路径标准库目录、site-packages、虚拟环境路径、PYTHONPATH与项目 src 目录等参数fromUri指明以哪个根目录为基准。typeServer/resolveImport—— 把导入名解析到文件系统中的具体位置服务于跳转定义、自动导入等场景。typeServer/connection——仅主连接可用的会话控制请求用于在 LSP 初始化完成后开启/关闭额外的只读 TSP 通道extra transport 必须保持只读不得承载 LSP 流量。这对应TypeServerVersion.current 0.4.1所引入的多连接协商与控制能力。2.2 通知serverToClientTSP 目前只定义了一条服务器发往客户端的通知方法名类型名参数typeServer/snapshotChangedSnapshotChangedNotification{ old: number; new: number }该通知由服务器在所有未完成快照失效时发送携带新旧快照编号告知客户端此前拿到的类型引用已不可再用于请求。2.3 Pyright 私有扩展通知tspSupplemental.ts 定义了两个Pyright-only的通知均为clientToServer方法名参数语义pyright/setVirtualFileRedirect{ realUri: string; virtualUri: string }注册虚拟文件重定向此后类型服务器对realUri的读取应重定向到virtualUripyright/removeVirtualFileRedirect{ realUri: string }移除先前注册的重定向恢复对realUri的真实文件读取文件头注释直接点明了这一扩展的动机与使用场景Django stub 生成功能中Pylance 的 Rust sidecar 会把合并后的虚拟.py文件写入磁盘再通过pyright/setVirtualFileRedirect通知类型服务器的虚拟文件覆盖层virtual-file overlay使 Pyright 分析的是虚拟内容而非磁盘上的真实文件。这两个通知要求所有类型均可 JSON 序列化All the types in this file should be JSON serializable, as they are sent over the wire。协议层只负责消息形状的定义真正的行为落在文件系统实现上重定向的读写由 virtualFileOverlayFileSystem.ts注册文件 URI 的读操作重定向到磁盘上的备用虚拟文件承接而消息的分发处理位于 server.ts。这种协议定义 / 服务实现分离的边界正是 protocol 子系统被单独抽取的原因。3. 类型体系TSP 如何表达 Python 类型协议在types一节定义了完整的类型模型。所有类型统一实现TypeBase通过kind判别字段构成Type联合见typeServerProtocol.ts末尾的export type Type ...BuiltInType | DeclaredType | FunctionType | ClassType | UnionType | ModuleType | TypeVarType | OverloadedType | SynthesizedType | TypeReferenceType3.1 TypeBase 与 TypeFlagsTypeBase是所有类型变体的公共基类携带三个字段id: number—— 类型实例的唯一标识用于环检测与类型查找缓存递归解析时通过 id 避免死循环kind—— 判别字段如if (type.kind TypeKind.BuiltIn) { ... }flags: TypeFlags—— 描述类型特性的位字段可用按位运算组合typeAliasInfo?: TypeAliasInfo—— 当类型来源于类型别名时的元信息。TypeFlags在tsp.json中被定义为 11 个字符串字面量None、Instantiable、Instance、Callable、Literal、Interface、Generic、FromAlias、Unpacked、Optional、Unbound。其语义注释包括Instantiable—— 该类型可以被实例化Instance—— 表示实例区别于类/类型本身Callable—— 实例可像函数一样被调用具有__call__Literal—— 字面量类型如42、helloInterface—— 接口类型在 Python 中对应ProtocolGeneric—— 可参数化的泛型类型Unpacked—— 解包类型用于TypeVarTupleUnbound—— 未绑定类型用于元组类型参数中的*args。判断某标志是否被设置由 typeServerProtocolUtils.ts 中的工具函数完成export function isTypeFlagSet(flags: TypeServerProtocol.TypeFlags, flag: TypeServerProtocol.TypeFlags): boolean { return (flags flag) flag; }TypeKind则枚举了联合的判别值BuiltIn、Declared、Function、Class、Union、Module、TypeVar、Overloaded、Synthesized、TypeReference。3.2 各类型变体要点BuiltInTypeunknown、any、unbound、ellipsis、never/noreturn等语义特殊的类型。其中unknown可携带possibleType用于类型推断信息不完整但可给出候选的场景如isinstance窄化前的未知类型。DeclaredType/FunctionType/ClassType有源码声明的类型。FunctionType额外包含returnType、specializedTypes泛型实参替换后的具体签名与boundToType方法绑定的类/实例。UnionTypeint | str | None这类联合类型。ModuleTypeimport os得到的模块类型。TypeVarType泛型中的T、P、Ts其 JSDoc 示例覆盖了普通TypeVar、约束TypeVar、ParamSpec与Concatenate场景。OverloadedType带多个overload签名的函数overloads数组存放全部签名implementation存放实际实现。TypeReferenceType按id引用另一类型的去重优化。当同一复杂类型反复出现如list[dict[str, int]]出现三次、或递归类型需要打破环如Node.next: Node | None时后续出现处用typeReferenceId指向首次出现的实例从而压缩线上载荷。SynthesizedType类型服务器自己合成、不对应源码声明的类型。其核心字段是stubContent——一段完整、合法的.pyistub 文本必须包含所需 import、TypeVar/ParamSpec 声明、类型别名或类/函数签名客户端通过解析并求值该 stub 来重建类型。典型用例包括dataclass合成的__init__、NewType声明以及泛型特化。SynthesizedTypeMetadata.primaryDefinitionOffset用从 stubContent 起始的零基字符偏移指向目标定义位置例如函数 stub 指向def关键字方便客户端定位。3.3 Declaration 与 Node把类型钉回源码Node表示源码中的位置AST 节点携带uri与零基range与 LSP 的字符偏移约定一致Declaration则区分两类来源RegularDeclarationDeclarationKind.Regular——存在于源码、有 AST 节点的声明字段为categoryDeclarationCategoryIntrinsic、Variable、Param、TypeParam、TypeAlias、Function、Class、Import、node与可选的nameSynthesizedDeclarationDeclarationKind.Synthesized——由类型检查器创建、无源码节点的声明例如内建函数len、dataclass合成的成员其uri指向概念上声明位置的文件如装饰器所在文件。协议还提供了表达 Python 特有字面量的辅助类型EnumLiteralclassNameitemNameitemType用于Literal[Color.RED]与SentinelLiteralclassNodemoduleNameclassName用于dataclasses.MISSING这类哨兵对象。TypeAliasInfo则记录了别名名称、全限定名、所在模块与文件、作用域 id隔离同名T、isTypeAliasType是否 PEP 695 的type关键字写法、typeParams/typeArgs与推断出的computedVariance。3.4 导入解析相关的参数对象ModuleName表达绝对/相对导入leadingDots表示相对导入层级import os.path为0 [os,path]from . import utils为1 [utils]from ...parent import module为3 [parent,module]。ResolveImportParamssourceUri导入发生所在文件、moduleDescriptor、snapshot若快照过期应抛出ServerCanceled异常。ResolveImportOptions三个可选开关——resolveLocalNames是否解析遮蔽导入的局部赋值、allowExternallyHiddenAccess是否允许访问_private或不在__all__中的成员、skipFileNeededCheck跳过文件存在性/有效性检查以优化。GetPythonSearchPathsParamsfromUri指定基准根目录snapshot用于失效校验。4. 快照机制类型与版本绑定的正确性约定TSP 的快照设计贯穿所有取类型请求。GetSnapshotRequest的 JSDoc 给出了精确定义快照是类型服务器状态的时间点表示包含所有已加载文件及其类型。只要类型服务器返回的任一类型可能失效就必须更换快照。类型只在返回它的快照下可用快照不能跨越会使类型服务器丢弃内部缓存的变更。它只是一个标识符告诉客户端类型服务器接受该快照下的类型请求。因此客户端的正确用法是先发typeServer/getSnapshot拿到编号再携带该编号调用getComputedType/getDeclaredType/getExpectedType/resolveImport/getPythonSearchPaths一旦收到typeServer/snapshotChanged通知旧的快照编号即告失效。在服务端ResolveImportParams与GetPythonSearchPathsParams的注释都明确要求Type server should throw a ServerCanceled exception if this snapshot is no longer current这与 typeServer/cancellation.ts 中定义的ServerCanceledExceptionResponseError子类对应 LSPServerCancelled相呼应——协议层把快照过期视为一种标准的取消语义。5. 协议版本化与 tsp.json 生成流水线协议版本在TypeServerVersion枚举中集中管理见 tsp.json版本说明0.1.0初始协议版本0.2.0新增请求类型与字段0.3.0切换到更复杂的类型0.4.0切换到 Type 联合并使用 stubs0.4.1current新增多连接协商与控制请求tsp.json的metaData.version当前为0.2.0这是生成清单自身的格式版本与协议能力版本0.4.1相互独立结构包含requests、notifications与types三大部分。值得注意的一个实现细节协议定义中v0.4.0引入的SynthesizedType用.pyistub 文本来表达合成类型这正是使用 stubs版本说明的来源。该 JSON 清单并非手写维护而是由 generate_json.py 自动生成。脚本的工作方式如下解析typeServerProtocol.ts先剥离注释获得结构再用带注释的原文提取文档通过正则与括号匹配识别export enum、export interface含泛型与extends继承、export type别名以及export namespace。转换类型_typescript_to_tsp_type把 TypeScript 类型映射为 TSP JSON 形态——基础类型映射为{kind: base}、联合映射为{kind: or}、数组映射为{kind: array}、其余引用映射为{kind: reference}bigint因不可移植而被过滤undefined被归一为可选的null。解析消息_parse_namespaces/_parse_request_namespace/_parse_notification_namespace从ProtocolRequestType、ProtocolRequestType0、ProtocolNotificationType的泛型实参中提取参数与结果类型并从 JSDoc 中收集文档字符串。补充 LSP 类型_add_lsp_types按 LSP 规范补全从vscode-languageserver-protocol导入但未在本文件定义的Range、Position等类型。写出 JSON按方法名排序后写入tsp.json。同时目录下的tsp.schema.json为清单本身提供 JSON Schema 约束。这意味着 TSP 拥有TypeScript 类型定义权威 JSON 元数据可被其他语言消费的双轨形态其他语言的 TSP 实现如注释中提到的 ty-tsp可以脱离 TypeScript 源码直接读取tsp.json。6. 消费方与调用链protocol 在 typeServer 中的落地protocol.md的依赖清单给出了该子系统的下游消费者全部集中在typeServer/内印证了协议只定义、服务来实现的分层server.ts语言/类型服务器本体接收 TSP 消息并应答查询是协议的最大消费方programWrapper.ts把 Pyright 的Program适配为类型服务器的IProgram接口并维护快照版本programTypes.ts导出程序、解析器、源码映射、符号查找与类型服务器求值器的 TypeScript 接口typeServerConversionTypes.ts与typeServerConversionUtils.ts把 Pyright 内部的分析器类型、解析节点与声明转换成协议结构或反向转换typeServerProtocolUtils.tsisTypeFlagSet位标志判断工具。更宏观地看protocol是 typeServer 子系统25 个文件、291 个叶子符号的消息契约层而typeServer整体又依赖于analyzerprogram.ts、sourceFile.ts、typeEvaluatorTypes.ts等完成实际类型求值通过src/backgroundAnalysisBase.ts接入后台分析并复用common/的 URI、文件系统与服务容器基础设施。当客户端发起一次typeServer/getComputedType时消息流大致为server.ts 接收 → 按快照校验过期则抛ServerCanceledException→ programWrapper 定位对应 Program/快照 → typeServerConversionUtils 把内部类型转为协议Type→ 返回给客户端。这条链路说明 TSP 并非第二套类型系统而是 Pyright 既有分析能力的协议化封装。7. 总结protocol子系统用两个源码文件完成了三层设计中立性——typeServerProtocol.ts定义了与具体实现无关的 TSP 本体8 个请求、1 条通知、完整的Type/Declaration/Node类型模型、快照与取消语义可被任意 TSP 实现复用扩展性——tspSupplemental.ts以pyright/前缀消息承载 Pyright 私有能力虚拟文件重定向并通过virtualFileOverlayFileSystem等模块落实行为可消费性——tsp.jsongenerate_json.pytsp.schema.json让协议元数据脱离 TypeScript 生态以结构化 JSON 面向多语言消费者。对于希望接入 TSP 或研究 Pyright 类型服务架构的开发者建议的阅读路径是先通读 typeServerProtocol.ts 掌握消息与类型全貌再对照 tsp.json 理解元数据形态随后进入 server.ts 观察消息如何驱动真实的类型分析最后以 tspSupplemental.ts 为范例学习如何为协议叠加 Pyright 专属能力。【免费下载链接】pyrightStatic Type Checker for Python项目地址: https://gitcode.com/GitHub_Trending/py/pyright创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表