Linux 下 Python 开发环境架设小记

Python 语言从个人小副业到严肃的生意

相比含着金钥匙出生的 Java, Python 像 JavaScript 一样, 是个连编辑器都不需要就可以上手的语言. JS 还需要一个浏览器. 而对于大多数现代 Linux 发行版本来说, 由于自带 Python 解析器, 所以你连图形界面都不需要安装, 就可以开始学习 Python 的旅程.

当然, 在发展了这么多年以后, 在无数工程上失败(或者成功)的巨型项目之后(手动狗头), Python3 对于大型项目的友好程度已经不可同日而语, Python 开发环境相比十年前也已经得到了极大的改善.

不管愿不愿意, 事实是 Python 不再是个小小的个人副业(side project), 而是关系到上亿人, 相关年产值千亿刀的生意.

语言服务器协议(Language Server Protocol/LSP)及 Python 语言服务端实现

因为这个结果, 现在的 Python 开发环境确实现代化了不少.

在微软开始拥抱开源后, 这几年的开发环境最大的变化应该算 vscode 崛起; 但在幕后, 更重要的是 LSP 成为了行业标准. 今天的现代开发环境, 支持 LSP 的已经不在少数.

对于 Python 语言来说, LSP 的实现当前主要有三个, 微软有两个, 早一点的 Microsoft.Python.LanguageServer 和当前在推的 pyright (这里说一下 pylance 基于 pyright, 去年微软专门发过稿子, 而且给各大科技媒体也推过); 另一个更早的是 Palantir 的实现就叫 pyls.

Palantir 的实现因为比较早, 在 PyPI 中就叫 python-language-server, 非 vscode 用户估计可能第一个尝试的就是它. 它是一个 C++/Python 实现, 单独安装可以直接通过 PyPI 执行 pip 即可.

Microsoft.Python.LanguageServer 是 C# 的实现, 如果是单独安装, 需要自行下载, 或者编译源码;

对于以上两个 lsp 实现来说, 最大的麻烦是 PYTHONPATH 的配置. 特别是项目比较多的情况. Palantir 的 pyls 如果配置 jedi, 相对来说, 容易找到包, 但是性能和功能都很成问题.

mspyls 性能和功能都不错, 配置也较 Palantir 的版本简单. 但是引入的限制要严格的多, 基本上需要独立配置 python.autoComplete.extraPaths, 否则很容易引起 unresolved import 报错. 特别是对于一个项目有好多层的情况. 所以还是要针对不同的项目改配置. 同时性能只是相对来说有提高.

剩下要说的, 就是微软新出的 lsp 实现 pyright, 比较搞笑的是, 这个 lsp 是用 TypeScript 实现的. 这个实现总算是我默认打开一个项目就可以找到外部库的函数定义, 不再做需要额外的配置. 另外, 由于是静态检查器, 所以性能上相比前面两个要好不少.

正因为 pyright 是 TS 开发的, 所以安装需要使用 npm 或者 yarn. 当然, 如果 Linux 发行版是 ArchLinux, 那 AUR 源已经有一个 pkgbuild 了, 所以问题不大.

最后, 其实 Python 的语言分析工具, 更早的还有一个 Jedi, emacs 下面早两年的 Python 开发环境配置就以 Jedi 主核心, 但这个配置要复杂的多; 其实 Jedi 如果配置好了, LSP 并没有太多吸引力. 但是如果是 pyright 这个实现, 一般的新人估计很少会选择 emacs jedi 的了. 现在它也有一个 LSP 实现, 因为有上游项目, 所以这个实现要简单的多. 在 vscode 上也可以使用它.

编辑器相关

这里顺便提一下 Linux 下面的中文输入的问题. 从 emacs 26 开始, 我的主力机上一直有无法调出 fcitx 的问题, 当然可以使用 PyIM, 但这个的性能真不怎么样. 更何况我真的想用 fcitx5. Emacs 最近的 27 版本其实还行, 解决了一些小问题.

开发方面有人认为是 xim 的问题, 据说已经解决了. 但那个补丁我试过, 并没有解决我的问题, 事实上我还试了 emacs 28 的 git 编译, 也是一样. 我的时间不多, 所以我现在的建议如果真的需要 Linux 下用 fcitx 输入中文, 最好是保持 emacs 25.

回到架设 Python 开发环境上来, 对于 emacs 这样的编辑器来说, 需要实现一个 lsp client, 对于 pyright 来说, 就是 emacs elpa 仓库中的 lsp-pyright.

像上面说的 pyright 的配置要简单的多, 所以没有太多需要讲的. 当然, 它的主页未免也太简单了一点, 估计因为微软指着闭源的 “AI” 补全(pylance), 不过我们不用 vscode, 所以不用管 pylance. 和一切用 typescript 写的东西一样, 一定要注意 node 运行时的版本, Linux 因为有系统的包管理工具, 所以一般来就没有什么问题. 但我有几次就有不小心调用到 nvm 安装旧版 node 情况, 结果没有跑通的情况, 跟一切 node 的项目一样, 错误的 node 并不会失败, 而是可以跑起来, 但是对于绝大多数 lsp 客户端请求是不会响应的.

如果是 mspyls 的话, 基本上必然要配置 extraPaths, 甚至要加 .dir-locals.el. 因为现在我没有再用它了, 所以就略过了.

包依赖工具

最后提一下, 尽管最早的依赖工具是 pipenv, 但是根据最近两个项目的情况. 还是用 poetry 比较好, pipenv 确实太慢了. poetry 生成的项目, 大致是下面这样的:

.
├── dist
├── poetry.lock
├── pyproject.toml
├── pyrightconfig.json
├── src
│   ├── pkg1
│   └── pkg2
└── .venv
    ├── bin
    ├── lib
    └── pyvenv.cfg

对应的 pyrightconfig.json 配置如下,

{
    "executionEnvironments": [
        {
            "extraPaths": [
                "src"
            ]
        }
    ],
    "verboseOutput": true,
    "venv": ".venv",
    "venvPath": "."
}

pyrightconfig.json 多数情况下不需要, 但这里还有坑的, venvPath 指的是所有 virtualenv 所在的目录(即上一级目录), 而 venv 才是 virtualenv 的目录名. extraPaths 的位置也是足够奇葩.

如果是那种所有的 virtualenv 都在一起的情况, 这个 venvPath 其实还可以理解, 但是还是很坑, 毕竟只有一个参数就够了, 它非要两个. 多个的原因是有时一个项目需要多个 Python 执行时. 这种情况在 PaaS 厂商或者 2B 的厂商其实很常见. 对于 2C 厂商, 产品就是一个网站, 永远只跑一个运行时的可以说是无法想象.