[吐槽] pip 安装 pyright 是个坑

具体是这样的,今天下午我运行了一下 pip install --upgrade pyright,把 pyright 从 1.1.275 更新到了 1.1.278。

然后打开 .py 文件发现 lsp 迟迟不能就绪。

查看进程管理发现竟然有个进程的名字叫做 npm install [email protected]

查看 *lsp-bridge* 缓冲发现出错了:

--- Lsp server process 60652 already exited!
Exit server: /Users/gqj/Downloads/test_py/test.py#pyright

--- Send initialize for /Users/gqj/Downloads/test_py/test.py (pyright)

--- Send (29537): initialize
npm ERR! code ENOTEMPTY
npm ERR! syscall rename
npm ERR! path /private/var/folders/m1/.../T/pyright-python-langserver.gqj/node_modules/pyright
npm ERR! dest /private/var/folders/m1/.../T/pyright-python-langserver.gqj/node_modules/.pyright-XANTzgdw
npm ERR! errno -66
npm ERR! ENOTEMPTY: directory not empty, rename '/private/var/folders/m1/.../T/pyright-python-langserver.gqj/node_modules/pyright' -> '/private/var/folders/m1/.../T/pyright-python-langserver.gqj/node_modules/.pyright-XANTzgdw'

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/gqj/.npm/_logs/2022-11-09T06_38_34_625Z-debug-0.log
Traceback (most recent call last):
  File "/Users/gqj/.pyenv/versions/3.7.0/bin/pyright-langserver", line 8, in <module>
    sys.exit(entrypoint())
  File "/Users/gqj/.pyenv/versions/3.7.0/lib/python3.7/site-packages/pyright/langserver.py", line 57, in entrypoint
    sys.exit(main(*sys.argv[1:]))
  File "/Users/gqj/.pyenv/versions/3.7.0/lib/python3.7/site-packages/pyright/langserver.py", line 26, in main
    return run(*args, **kwargs).returncode
  File "/Users/gqj/.pyenv/versions/3.7.0/lib/python3.7/site-packages/pyright/langserver.py", line 47, in run
    node.run('npm', 'install', f'pyright@{version}', cwd=str(TEMP_DIR), check=True)
  File "/Users/gqj/.pyenv/versions/3.7.0/lib/python3.7/site-packages/pyright/node.py", line 112, in run
    return subprocess.run(node_args, env=env, **kwargs)
  File "/Users/gqj/.pyenv/versions/3.7.0/lib/python3.7/subprocess.py", line 468, in run
    output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['/Users/gqj/.nodenv/shims/npm', 'install', '[email protected]']' returned non-zero exit status 190.

--- Lsp server exited, exit code: 1
b''

为何 pyright/langserver.py 会启动安装命令?我好奇打开看了一下。顿时明白了,pip 安装的只不过是个“安装脚本”,而且里面有坑:

def run(
    *args: str,
    **kwargs: Any,
) -> Union['subprocess.CompletedProcess[bytes]', 'subprocess.CompletedProcess[str]']:
    TEMP_DIR.mkdir(exist_ok=True, parents=True)

    version = os.environ.get('PYRIGHT_PYTHON_FORCE_VERSION')
    if version is None:
        version = node.latest('pyright')

    pkg = TEMP_DIR / 'node_modules' / 'pyright' / 'package.json'
    if pkg.exists():
        current_version = json.loads(pkg.read_text()).get('version')
    else:
        current_version = None

    # TODO: use the same install location as the pyright CLI
    if current_version is None or current_version != version:
        node.run('npm', 'install', f'pyright@{version}', cwd=str(TEMP_DIR), check=True)

    binary = TEMP_DIR / 'node_modules' / 'pyright' / 'langserver.index.js'
    if not binary.exists():
        raise RuntimeError(f'Expected language server entrypoint: {binary} to exist')

    return node.run('node', str(binary), '--', *args, **kwargs)
  1. pip 和 npm 仓库更新不同步,前者停留在 1.1.278,而后者更新到了 1.1.279.
  2. 每次开启 pyright 时,就发现两个版本不匹配(哪怕比较一下版本大小也好啊),然后安装新版本。(但不知为何出错了)
  3. (手动更新 pyright 成功,) 重新加载走到第 2 步骤还是不匹配。

上边的源代码里还“贴心”地准备了一个环境变量,让用户可以手动解除窘境:PYRIGHT_PYTHON_FORCE_VERSION。当然也可以直接屏蔽更新语句。

从 pip 安装也是看了 pyright 官方说明。他们难道不知道自己在挖坑吗?明明 npm 就可以安装。

刚刚 pip 和 npm 版本终于同步上了:(注意截图中的时间)

1 个赞

难绷,说是个 wrapper 还真就只是个 wrapper。

我之前也发现这问题了,尝试用 pip 安装pyright, 发现它会自己给我装一个 npm,那我还不如直接 npm 安装好了。

2 个赞

可能他们觉得用 nodejs 实现 python lsp 不清真 :new_moon_with_face:

然后为了掩盖事实。。。

1 个赞

用 nodejs 实现和用 python 实现相比有什么优势吗?

Python 的 lsp-server 是最多的, pylsp, pyls pyrightjedi-language-server,4个里面有3个是用 python 实现的。

和 VSCode 更好交互和集成吧。

1 个赞

我之前都试用过,感觉pyright是最快最流畅的

npm install -g 是最方便的 也不可能出错的

这三个python的实现本质上都是jedi的包装。pyright是自己实现的语义分析。 pyright是pylance的残缺版、微软是故意把社区开源做的功能很少;吸引用户去使用只能在vscode用的pylance。而且pyright微软只承诺会维护他类型检查的功能,不排除哪一天就把自动补全的功能给drop掉了。

2 个赞

而且是个带 bug 加成的 wrapper

除了我在 1 楼所说的 pip 和 npm 仓库不同步之外,本地也会造成不同步:

  1. 打开编辑器,启动 pyright(pip),保持进程不关闭。
  2. pyright(pip) 检查$TMPDIR/pyright-python-langserver.$USER/node_modules/ 是否存在 npm 副本,没有则创建一份。后续的版本判断都从这个副本读取。
  3. 在终端更新:pip install --upgrade pyright
  4. 另开一个编辑器,启动 pyright(pip)。
  5. 检测到 pyright(npm) 副本的存在,从中读取读取版本,但此时的版本是旧的。因此判定为需要更新。
  6. pyright(pip) 自动运行 npm install pyright@{version} 更新。
  7. 自更新失败,可能因为副本目录被占用。这也就解释了,1 楼的疑问:为什么手动 npm installl 成功,但自更新失败。
  8. 死循环 4-7。
2 个赞

python 项目个人一般用 citre ,感觉也够用了,lsp 确实不好用