十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

如何编写客户端接入 pyright-typeserver(TSP 协议)获取类型信息?

如何编写客户端接入 pyright-typeserver(TSP 协议)获取类型信息? 如何编写客户端接入 pyright-typeserverTSP 协议获取类型信息【免费下载链接】pyrightStatic Type Checker for Python项目地址: https://gitcode.com/GitHub_Trending/py/pyright如果你的工具代码生成器、自定义分析器、内部编辑器需要直接拿到 Pyright 对某段 Python 代码的类型推断结果——而不是走补全、悬停这类编辑器向的 LSP 请求——那么 Pyright 提供了一个独立的 type server以 npm 包pyright-typeserver分发通过 Type Server ProtocolTSP暴露typeServer/getComputedType、typeServer/getDeclaredType等类型查询接口。本文基于仓库中的协议定义与测试代码给出一条可运行的客户端接入路径用 stdio 拉起pyright-typeserver进程完成 LSP 初始化、打开文档、获取快照然后带着快照发起类型查询并验证结果。前置要求Node.js 14.0.0见 packages/pyright-typeserver/package.json 的engines字段。准备包结构与协议来源TSP 服务端的代码实际位于packages/pyright-internal/src/typeServer/服务器实现、协议定义、notebook 支持、文件系统层都在这里而packages/pyright-typeserver/只是可发布的薄封装其nodeMain.ts直接调用pyright-internal的main()见 nodeMain.ts。它复用 Pyright 的 analyzer / binder / type evaluator因此类型结果与 Pyright 命令行工具、语言服务器两个前端口径一致。写客户端时协议事实来源是下面几个文件typeServerProtocol.ts协议的唯一权威定义TS 源码。当前协议版本为TypeServerVersion.current 0.4.1。tsp.json 与 tsp.schema.json由generate_json.py从上面的 TS 文件生成不要手工编辑。客户端可以基于tsp.schema.json生成自己的类型定义。tspSupplemental.tsPyright 独有的扩展通知虚拟文件重定向。安装即npm install pyright-typeserver得到可执行文件pyright-typeserverpackage.json中bin字段。启动服务器pyright-typeserver --stdiotype server 与语言服务器一样走 stdiopyright-typeserver --stdio文档明确说明它不用于交互式运行而是由客户端进程拉起后通过协议驱动。所以在客户端代码里用 child_process 启动它并把 stdin/stdout 接到 JSON-RPC 的读写器上。仓库测试用的是vscode-jsonrpc的StreamMessageReader/StreamMessageWriter客户端可以用同一套依赖import { spawn } from node:child_process; import { StreamMessageReader, StreamMessageWriter, createConnection, } from vscode-jsonrpc/node; // 拉起 type server 进程npm 安装后可执行文件为 pyright-typeserver const server spawn(pyright-typeserver, [--stdio], { stdio: pipe }); const connection createConnection( new StreamMessageReader(server.stdout!), new StreamMessageWriter(server.stdin!), );完成 LSP 初始化TSP 架在 LSP 同一套传输之上文档按通常的 LSP 方式打开textDocument/didOpen、textDocument/didChange等类型查询走typeServer/*请求。因此客户端第一步是标准的 LSPinitialize握手。下面是仓库测试 inProcTypeServerTestUtils.ts 中的实际初始化载荷rootUri换成你的工作区根目录import { InitializeRequest, InitializedNotification, } from vscode-languageserver-protocol; // 服务端会反向请求客户端能力不实现这些 handler 会导致协议卡住。 // 以下四项对应测试桩里的 ConfigurationRequest / RegistrationRequest / // UnregistrationRequest / DiagnosticRefreshRequest。 connection.onRequest(workspace/configuration, (params: any) (params.items ?? []).map(() ({})) ); connection.onRequest(client/registerCapability, () undefined); connection.onRequest(client/unregisterCapability, () undefined); connection.onRequest(textDocument/diagnostic, () undefined); connection.listen(); const rootUri file:///your/workspace; // 替换为你要分析的工作区根目录 URI await connection.sendRequest(InitializeRequest.method, { processId: null, rootUri, capabilities: { workspace: { workspaceFolders: true, configuration: true }, textDocument: { synchronization: { dynamicRegistration: true }, diagnostic: { dynamicRegistration: true }, }, }, workspaceFolders: [{ uri: rootUri, name: workspace }], }); connection.sendNotification(InitializedNotification.method, {});打开文档并获取稳定快照所有类型查询都带一个snapshot参数快照是 type server 当前状态的时间点标识类型结果只对返回它的那个快照有效。服务器状态一变文件变更、分析推进快照就前进。先按 LSP 方式打开待分析的 Python 文档import { DidOpenTextDocumentNotification } from vscode-languageserver-protocol; const fileUri file:///your/workspace/main.py; // 替换为待分析文件的 file:// URI const code def takes_int(x: int) - None: pass __arg__ 1 takes_int(__arg__) ; connection.sendNotification(DidOpenTextDocumentNotification.method, { textDocument: { uri: fileUri, languageId: python, version: 1, text: code }, });然后请求稳定快照。仓库测试工具的做法是连续读typeServer/getSnapshot直到两次读数相同且 0期间轮询async function getStableSnapshot(timeoutMs 5000, pollMs 5): Promisenumber { const first await connection.sendRequestnumber(typeServer/getSnapshot); const second await connection.sendRequestnumber(typeServer/getSnapshot); if (second first second 0) { return second; } let previous second; const start Date.now(); while (Date.now() - start timeoutMs) { await new Promise((r) setTimeout(r, pollMs)); const current await connection.sendRequestnumber(typeServer/getSnapshot); if (current previous current 0) { return current; } previous current; } return previous; }另外要监听服务端通知typeServer/snapshotChanged参数{ old: number; new: number }它表示旧快照全部失效——收到后应丢弃基于旧快照的结果并重新取快照。发送类型查询请求协议定义的请求全集如下来自 typeServerProtocol.ts方法参数返回typeServer/getSupportedProtocolVersion无协议版本字符串semvertypeServer/getSnapshot无快照号numbertypeServer/getComputedType{ arg: Declaration \| Node; snapshot: number }Type \| undefined节点上推断出的类型typeServer/getDeclaredType同上Type \| undefined源码中显式声明的类型typeServer/getExpectedType同上Type \| undefined上下文期望类型typeServer/resolveImport{ sourceUri: string; moduleDescriptor: { leadingDots: number; nameParts: string[] }; snapshot: number }解析到的文件 URIstring \| undefinedtypeServer/getPythonSearchPaths{ fromUri: string; snapshot: number }导入解析搜索路径数组string[] \| undefined其中Node是{ uri: string; range: Range }range是零基位的 LSP 风格字符偏移区间用来定位源码中的声明、表达式或语句。关键点请求发出后服务器会核对program.getSnapshot() params.snapshot不一致就抛ServerCanceledserver.ts 中的处理。所以带快照的请求要“取快照 → 发请求 → 被取消则重试”。仓库测试工具封装的重试逻辑最多 10 次可以直接借鉴import { LSPErrorCodes } from vscode-languageserver-protocol; async function withSnapshotR(method: string, params: object): PromiseR { for (let attempt 0; attempt 10; attempt) { const snapshot await getStableSnapshot(); try { return (await connection.sendRequestR(method, { ...params, snapshot })) as R; } catch (e: any) { // 快照在读取之后、请求处理之前移动了按测试工具的做法重试 if (e?.code LSPErrorCodes.ServerCancelled) { continue; } throw e; } } throw new Error(未能获得匹配快照); }一个端到端示例沿用上一步打开的main.py对__arg__ 1中__arg__这个节点查询推断类型const version await connection.sendRequeststring(typeServer/getSupportedProtocolVersion); console.log(TSP version:, version); const type await withSnapshot(typeServer/getComputedType, { arg: { uri: fileUri, // __arg__ 位于第 3 行0 基字符 0–7替换为你自己文件中目标节点的范围 range: { start: { line: 3, character: 0 }, end: { line: 3, character: 7 } }, }, }); console.log(type?.kind, (type as any)?.declaration?.name);返回的Type是一个按kind区分的联合体取值包括BuiltInunknown/any/never 等、Function、Class、Union、Module、TypeVar、Overloaded、Synthesized、TypeReference。例如上例中__arg__推断为int返回对象的kind应为TypeKind.Class枚举值 3declaration.name为int。验证接入是否正确以下判断标准全部来自仓库测试 typeServer.inProc.test.ts 中的断言可直接用作你客户端的验收条件typeServer/getSupportedProtocolVersion返回非空字符串当前服务器实现返回0.4.1。typeServer/getSnapshot返回的快照号 0。对本地模块foo.py中value 1main.py中import foo调用typeServer/resolveImport返回 URI 以/src/foo.py结尾测试工作区布局你的工作区会对应你自己的模块路径。对标准库的resolveImport会落到 bundled 的 typeshed-fallback例如typing解析到包含/typeshed-fallback/stdlib/typing.pyi的 URI。typeServer/getPythonSearchPaths返回一个字符串数组。对__arg__ 1中__arg__节点调用typeServer/getComputedType返回类型的kind为TypeKind.Class且类名为int。若某项不符合版本为空说明协议方法没注册对确认方法名拼写快照一直不稳定说明文档变更仍在进行resolveImport返回undefined则检查sourceUri是否是已打开文件所在上下文、nameParts是否按模块路径拆分。限制与可选扩展版本策略协议目前处于 0.x 阶段minor 版本变化可能是破坏性变更协议注释明确要求客户端在发请求前先查typeServer/getSupportedProtocolVersion。快照语义类型只对随返回的那个快照有效。快照在读取后失效会收到ServerCanceled错误客户端必须按上节方式重试服务端状态变化时还会主动推送typeServer/snapshotChanged。Notebook 支持可选客户端发送notebookDocument/didOpen、notebookDocument/didChange、notebookDocument/didClose时服务器会把 notebook 建模为线性链式的 cell 源文件前面 cell 定义的名字在后续 cell 可见与 notebook 执行语义一致。虚拟文件重定向可选Pyright 扩展通过pyright/setVirtualFileRedirect参数{ realUri, virtualUri }与pyright/removeVirtualFileRedirect参数{ realUri }两条客户端通知把磁盘文件的读取内容替换为客户端提供的虚拟文档。典型用途是 stub 生成器合成了一个模块视图想让 type server 按合成内容分析。多连接协议还定义了typeServer/connection控制请求可在初始化后额外打开只读的 IPC 通道ConnectionTransportKind.Ipc附加通道不能承载 LSP 流量。参考文件docs/type-server.mdtype server 的官方说明运行方式、TSP 请求列表、notebook 与虚拟文件重定向。typeServerProtocol.ts / tsp.schema.json协议请求、参数与Type联合体的权威定义。server.ts服务器侧请求注册与快照校验实现。inProcTypeServerTestUtils.ts完整的客户端驱动流程初始化、打开文档、快照稳定化、带重试的快照请求。typeServer.inProc.test.ts各请求的验收断言。【免费下载链接】pyrightStatic Type Checker for Python项目地址: https://gitcode.com/GitHub_Trending/py/pyright创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表