如何通过编程方式调用Pylance查找代码引用及定义位置?
如何通过编程调用Pylance查找代码引用?
已知Pylance作为语言服务器,理论上可通过连接它提取函数调用的引用信息。比如以下代码中,check_files_in_directory是从GPT4Readability.utils模块导入的,但无法直接通过常规方式确认其定义位置,希望了解如何通过编程调用Pylance来获取这类信息:
import os from getpass import getpass from GPT4Readability.utils import * import importlib.resources as pkg_resources def generate_readme(root_dir, output_name, model): """Generates a README.md file based on the python files in the provided directory Args: root_dir (str): The root directory of the python package to parse and generate a readme for """ # prompt_folder_name = os.path.join(os.path.dirname(__file__), "prompts") # prompt_path = os.path.join(prompt_folder_name, "readme_prompt.txt") with pkg_resources.open_text('GPT4Readability.prompts','readme_prompt.txt') as f: inb_msg = f.read() # with open(prompt_path) as f: # lines = f.readlines() # inb_msg = "".join(lines) file_check_result = check_files_in_directory(root_dir)
实现方法
基于语言服务器协议(LSP)与Pylance通信
Pylance完全遵循LSP规范,你可以通过实现轻量LSP客户端,调用LSP的textDocument/definition请求来获取符号的定义位置,这正是你需要的函数来源信息。核心步骤拆解
- 启动Pylance进程:找到Pylance的JS服务器文件(通常在VS Code扩展目录,路径类似
~/.vscode/extensions/ms-python.vscode-pylance-*/dist/server.bundle.js),通过Node.js启动它,用标准输入输出做通信通道。 - 初始化LSP会话:发送初始化请求,传入项目根目录、进程ID等信息,让Pylance加载目标代码所在的项目环境。
- 告知Pylance目标文档内容:通过
textDocument/didOpen通知,把要分析的代码内容传递给Pylance。 - 发送定义查询请求:针对
check_files_in_directory调用的位置(行号、列号,注意LSP行号从0开始),发送textDocument/definition请求。 - 解析响应:Pylance会返回该函数的定义文件URI、行号等信息,直接就能确认它是否来自
GPT4Readability.utils模块。
- 启动Pylance进程:找到Pylance的JS服务器文件(通常在VS Code扩展目录,路径类似
简化代码示例
以下是用Python+asyncio实现的核心逻辑:import asyncio import json import os async def get_symbol_definition(): # 替换为你的Pylance服务器路径 pylance_path = "/path/to/ms-python.vscode-pylance-*/dist/server.bundle.js" proc = await asyncio.create_subprocess_exec( "node", pylance_path, stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) # 发送初始化请求 init_req = { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "processId": os.getpid(), "rootUri": "file:///path/to/your/project/root", "capabilities": {} } } await proc.stdin.write(json.dumps(init_req).encode() + b"\r\n") await proc.stdin.drain() # 读取初始化响应(可忽略内容,确认会话建立即可) await proc.stdout.readline() # 传递目标代码文档 target_code = """import os from getpass import getpass from GPT4Readability.utils import * import importlib.resources as pkg_resources def generate_readme(root_dir, output_name, model): with pkg_resources.open_text('GPT4Readability.prompts','readme_prompt.txt') as f: inb_msg = f.read() file_check_result = check_files_in_directory(root_dir)""" did_open = { "jsonrpc": "2.0", "method": "textDocument/didOpen", "params": { "textDocument": { "uri": "file:///path/to/your/target/file.py", "languageId": "python", "version": 1, "text": target_code } } } await proc.stdin.write(json.dumps(did_open).encode() + b"\r\n") await proc.stdin.drain() # 查询check_files_in_directory的定义(对应代码中第12行的调用,LSP行号为11) def_req = { "jsonrpc": "2.0", "id": 2, "method": "textDocument/definition", "params": { "textDocument": {"uri": "file:///path/to/your/target/file.py"}, "position": {"line": 11, "character": 27} } } await proc.stdin.write(json.dumps(def_req).encode() + b"\r\n") await proc.stdin.drain() # 解析响应结果 res_line = await proc.stdout.readline() res_data = json.loads(res_line.decode()) if "result" in res_data and len(res_data["result"]) > 0: def_uri = res_data["result"][0]["uri"] def_line = res_data["result"][0]["range"]["start"]["line"] print(f"函数定义位置:{def_uri},行号:{def_line + 1}") # 关闭会话 shutdown_req = {"jsonrpc": "2.0", "id": 3, "method": "shutdown"} await proc.stdin.write(json.dumps(shutdown_req).encode() + b"\r\n") await proc.stdin.drain() await proc.stdin.write(b"exit\r\n") await proc.wait() asyncio.run(get_symbol_definition())注意事项
- 确保Pylance路径正确,不同系统、VS Code版本的扩展路径有差异。
- 若项目有虚拟环境或特殊依赖,可在初始化请求的
workspaceConfiguration参数中指定Python解释器路径,保证Pylance能正确解析依赖。 - LSP交互严格遵循JSON-RPC格式,请求ID需唯一,参数格式不能出错,否则Pylance会返回错误响应。
内容的提问来源于stack exchange,提问作者Exploring
相关产品推荐
相关产品推荐

