← 返回文章流

Debian 系统上捉摸不定的 Python

在上周的周记中我记了一句: pdm 提了几个 issue,都和 debian 系统的 python 有关,i hate it 本文是对这句话的一个扩展。作为一个 Python 打包工具的开发者,非常痛恨 Debian 系统,所以我在回复 laixintao 时说道: Python 打包系统的混乱,Debian 系统是要居大功的。其实不止 Debian 是如此,下面开始吐槽,非常不严谨,吐槽完我就跑。 <!-- more --> Python 中的 Install Scheme Install Scheme 的概念,中文大概是翻译成「安装蓝图」?意思就是提供了一个路径的集合,告诉 Python 包的安装器(如 pip),什么文件应该放到哪个路径下。 本文只打算讨论 purelib 和 platlib,也就是 Python 库应该放到哪里。Install Scheme 可以通过以下函数获得: import sysconfig install_paths = sysconfig.get_paths() print(install_paths['purelib']) 正常情况下,比如我之前的文章提到的一样,Python 库是放到: Posix: $path_prefix/lib/pythonX.Y/site-packages Windows: $path_prefix/Lib/site-packages $path_prefix 是 Python 解释器所在的路径前缀。在这里我们先请优秀学生 Windows 回到座位上,来说说 Posix 的问题,比如 Python 路径是 /usr/bin/python3.9,那么 /usr 是路径前缀,上述路径则变为 /usr/lib/python3.9/site-packages。 Debian:事实并非如此 基本所有的 Linux 发行版都会自己打包 Python 库,他们不信任 PyPI,所有用到的 Python 库都拿源码下来,自己打包。那问题来了,那么多 Python 包,那么多 Python 版本,怎么打,怎么安装?他们发现了一点:纯 Python 库是比较兼容的,不需要每个 Python 版本都打一个包,只需要整个 Python 3 打一个包就可以了,所以有 python3-pip, python3-requests 这种命名方式的包。安装的时候,也把它们放在一个地方,所有 Python 3 版本都可以用。其他不是纯 Python 的包,再分版本存放。(这段是我臆想,不严谨) 所以,在 Debian 上,就有了下面三个路径存放 Python 库: /usr/lib/python3/dist-packages 放 apt 安装的纯 Python 库 /usr/lib/python3.9/dist-packages/ 放 apt 安装的带扩展的 Python 库 /usr/local/lib/python3.9/dist-packages/ 放 pip3 安装的 Python 库 这属于自己的设计了,那么就需要改安装库和导入库的逻辑。Debian 维护了一系列的补丁 来干这件事,改完之后,sys.path 会包含上面三个路径,site-packages 的路径从中去除了,而 pip3 也会安装包到第三个路径。所以要记住,发行版上自带的 Python 和 pip 都是特制的,你用从官网和 PyPI 上下载的去替换是会出问题的。 OK,这没问题,也能接受,反正你自己魔改自己发布到 apt,只要闭环了用户是感知不到变化的。但问题是,这些补丁是不完备的!。为了改默认的 sys.path,Debian 只修改了 site 模块,以及 distutils[^1] 模块,而没有修改上面的 sysconfig 模块,而 distutils 模块已经在 PEP 632 里被 Deprecated 了,官方推荐的替代就是 sysconfig。这个问题随着 Python 3.10 的发布而暴露了出来,Debian 的维护者也意识到这个问题,最新的补丁里已经修改了 sysconfig,但不是很小心,造成了一些回归问题。这样的后果就是,如果用户切换发行版的不同版本,以及 Python 3.10 或 3.10 以下,这个 Install Scheme 安装到哪里完全捉摸不定,不可预测,那么包管理器就很难做了(还能怎样,当然是原谅(兼容)啊,难道威胁用户,不许用 apt 的版本吗?) [^1]: distutils.sysconfig 中也包含一些和 sysconfig 模块中作用类似的函数。 到底有多捉摸不定? 我做了一个测试,分别测试 Python 3.9 和 3.10,以及 pip 使用 apt 的版本和 get-pip.py 安装的版本,在旧版本 Debian[^2] 和 debian:testing 中获取 Install Scheme 的值。 Image Python 3.9 Python 3.10 ubuntu:focal sysconfig: 未修改<br/>site: 已修改<br/>distutils: 已修改<br/>get-pip.py 安装的 pip 把库安装到 site-packages下 sysconfig: 已修改[^3]<br/>site: 已修改<br/>distutils: api 已移除<br/>get-pip.py 安装 pip 失败 debian:testing sysconfig: 未修改<br/>site: 已修改<br/>distutils: 已修改<br/>get-pip.py 安装的 pip 把库安装到 site-packages下 sysconfig: 已修改<br/>site: 已修改<br/>distutils: 已修改<br/>get-pip.py 安装的 pip 把库安装到 dist-packages下 这里已修改是指返回 dist-packages 的路径,而未修改是指返回 site-packages 的路径。测试脚本见仓库。 [^2]: 因为 deadsnakes ppa 只支持 ubuntu,我在这里用的其实是 ubuntu:focal 镜像。 [^3]: 在 focal 上没有 Python 3.10 的系统包,所以这里是从 deadsnakes ppa 安装的。 可以看到在 Python < 3.10 上虽然补丁不完备,行为倒还是统一的,但在 Python 3.10 上就出幺蛾子了,简直就是一团乱麻,Python 环境太难了。 Bonus: MacOS 上的 Python,取决于它是不是 framework,安装路径也有区别,但这已经在 CPython 的标准库中得到支持,不是用补丁方式解决的。 所以大家对下面这个主张没有异议了吧: Windows 上的 Python 环境是最清爽干净的。 如何避坑? 两条建议: 如果要在容器中使用 Python,用 python 系列的镜像。那里面的 Python 是标准化的,不是特制的。 如果用户系统是 Debian 系,不要在系统路径中安装包,对,--user 都不行,甚至 python3-pip 都不要装以绝后患。而应该使用虚拟环境(python3-venv 包)和 pipx。或者用 PDM 或 Poetry 这种包管理工具。因为只有在虚拟环境中,Python 库的安装路径永远是 site-packages,无论在哪个系统上。 一些 GitHub issues deadsnakes/issues#182 pypa/pip#10978

来自 Frost's Blog作者主页
在原博客阅读全文 (新标签页打开)

以上内容来自博客订阅源,版权归原作者。关注作者后可以在 关注列表 第一时间查看作者文章。

一起聊聊 0

尊重不同观点

还没有评论。欢迎分享第一个想法。

RSS SUBSCRIPTIONS

选择 RSS 订阅

把博客汇的文章添加到你常用的阅读器。

关注文章 RSS 订阅本订阅需要登录后使用