Published on

Python 打包的新动态

Reading Time

11 min read

今年以来,Python 打包社区依然很活跃,有对 PEP 582 的最新修改,也出现了一些新的提案, 本文将略作总结。

群众的呼声——统一的包管理器

在去年 9-10 月份的时候,PyPA 曾做了一次问卷调查,目的是了解用户对于 Python 打包现状的看法,以及对于未来的期望1。在收集上来的问卷中,有以下用户的回复:

回应 1: Unify the multiple tools. It’s good to have new ideas and new implementations, but it has to converge after a while. If my package has a compiled part, I’m stuck with setuptools, but all other tools are pushed forward while not covering this feature.

回应 2: There should be one– and preferably only one –obvious way to do it. Get rid of the fragmentation

回应 3: I definitely want to Python to introduce the One True packaging tool, ideally both easy as Rust’s cargo (where building, adding dependencies, running tests, code checking and deploying are all subcommands) and extensible (support for different build regimes, extensions in foreign languages etc). Package installing is easy, package building is a wild west at the moment, and no one tool is good enough for all use cases.

其中 Cargo 被高频地提及,很多来自其他语言的用户对 Python 多个打包工具并存的现状感到困惑,希望 Python 社区能够统一打包工具,以便于用户更好地使用2。 但我要为 Python 说句公道话,造成这种现状的原因并不是官方不给力,而是基于多种原因34

  • Python 社区本身就是分裂的,有很多不同的开发者群体,每个群体都有自己的需求和工作流,这些需求往往是相互矛盾的,因此很难找到一个统一的方案来满足所有人的需求。这也是 Python 在近 10 年大流行之下,带来的结果。比如现在流行的 包管理工具 Pipenv, Poetry 和 PDM,基本上都是给 Web 开发者设计的,却很少考虑到 C 扩展包、MLer 的需求(只能说尽量支持,但是不是很好)。所以这些工具,给 Web 开发者使用是最合适的。
  • PyPA(Python Packaging Authority)5 只是一个松散的组织,没有一个强大的驱动,因此很难像 Rust, Dart, .NET 那样,由官方去推动一个统一的打包工具。所以有提议将名称里的 Authority 改成 Association 以减少误解。

引用一下 Thea Flowers 的推特:

不要把事情想得太简单,你可能只看到了九头蛇的一头。

PEP 582 的最新修改

是的你没看错,PEP 582 并没有被遗忘,它的原作者 Kushal Das 又支楞起来了,在今年做了一些大的修改,基本上是重写了。相比 PDM 实现的 PEP 582 最初版本,一个最大的改变就是 目录结构变了,比如说一个包 bottle,它的导入路径从原来的:

__pypackages__/3.9/lib/bottle

改成了:

__pypackages__/lib/python3.9/site-packages/bottle  # Unix-like
__pypackages__/Lib/site-packages/bottle  # Windows

这个路径模式(Install scheme),和现在已有的 Python 包安装路径是匹配的。好处在于,现在立即就可以用 pip install --prefix __pypackages__ 来实现把包安装到 __pypackages__ 中。 但坏处也有,注意到 Windows 的安装路径,是没有按 Python 版本号来区分的,这就意味着,如果你使用多个不同版本的 Python 安装到同一个位置,这些包是会互相覆盖的。实际使用中, 你根本无从得知这个包是来自哪个版本,造成了不小出问题的可能性。关于这一点,我也在 Discussion 上提出来了,有 Core Developer 表示支持,作者也可能会采纳。但凡事有两面,如果在 Windows 路径中加入版本号,变成像 __pypackages__/Lib/3.9/site-packages/bottle 这样, 就和已有的路径模式不一致,需要增加新的,那么等到周边工具完全支持,周期一下就会漫长很多。但这是用短期的代价换取长期的好处,我觉得是值得的,等到后面出问题再想改就改不动了。

如果有读者用过 PDM 的 PEP 582 模式就会发现,无论如何改,都和 PDM 的实现不一样了6我也计划在下个大更新中把路径改成最新标准,提前预告一下。

除此之外,对于 PEP 582,还有以下几个争议点:

  • 如果脚本所在目录没找到 __pypackages__,是否去父级目录以及祖先目录去找?作者在提案中明确拒绝了,理由是安全的考量,因为用户可能执行 /home/me/path/to/my/project/script.py,却无意中加载了 /home/me/__pypackages__。 但说实话我没明白这个逻辑,如果恶意用户能在你 home 目录下拉屎,在安全这一块你就已经输了。而且严格控制只能加载当前目录的 __pypackages__ 无疑降低了这个提案的价值,现在这个提案仅仅是能降低一下初学者的 上手难度,不用管虚拟环境那些,愿景未免有点太小了,况且 Node.js 也是会去父目录寻找 node_modules 的,大家快去说服作者。
  • __pypackages__ 被加载时,是否要禁用系统的 site-packages?在我看来这是一个两难的选择,如果不禁用,确实会造成一些包版本冲突,但如果禁用了,那么就会造成一个结果,如果你在一个有 __pypackages__ 的目录下时,安装在全局的命令行工具就用不了了。 关于这些我也在 Discussion 上做了一个总结,包括 PDM 是如何处理这些问题的。

其他提案与动态

PEP 704: 安装 Python 包时明确要求虚拟环境

由于 PEP 582 存在的种种问题,PEP 704 应运而生,这是一个与 PEP 582 竞争的提案,它同样面向初学者,解决他们对包安装位置的困惑。它把安装包时,需要虚拟环境改成了默认行为,并且在没有激活虚拟环境时抛出错误。

PEP 691: 基于 JSON 的 Python 包索引的简单 API

之前的 Simple index(PEP 503) 完全是一个 HTML 页面,下载包时工具要自行解析 HTML。这个提案建议支持 JSON 格式的响应,这样带来的好处有:

  1. 解析 JSON 比解析 HTML 更容易
  2. JSON 更方便表示结构化的数据,以后可以添加更多不同类型的字段

这个提案已经在 PyPI 上实现,安装器方面,pip 和 PDM(unearth) 也已经支持获取和解析 JSON 的响应。

packaging 22.0 去掉了解析非标准版本字符串的支持

packaging 自从 22.0 以后,不再支持解析非标准的版本字符串,并使用了自己实现的 Parser 替代了 pyparsing 依赖。所有不符合 PEP 440 规范的版本字符串都会导致解析失败并报错。 但 PyPI 上仍有大量的包,使用了过时的不合规范的版本号定义,这就导致使用 PDM 安装包时,可能会遇到 InvalidRequirementError 错误。举例来说:

版本号或版本范围packaging<22packaging>=22
1.0
1.0a1
1.0-rc1
1.0-beta.1
1.a.b
>=1.0
!=1.*
>=1.0.*
>=1.0.0+g1213

由于 pip 采用 vendor 策略,暂时没有升级新版本的 packaging,所以 pip 不会遇到问题。

Footnotes

  1. 结果可以在这里查看。

  2. Python Discussion 上的讨论

  3. Pradyun Gedam: Thoughts on the Python packaging ecosystem

  4. Thea Flowers: So You Want to Solve Python Packaging: A Practical Guide

  5. Pradyun Gedam: How the Python Packaging community is organised

  6. Pradyun Gedam: PDM does not implement PEP 582, at the time of writing

Share: