14

Python 官方团队在打包项目中踩过的坑

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzUyOTk2MTcwNg%3D%3D&%3Bmid=2247485045&%3Bidx=1&%3Bsn=d1774e88288332b9bab36075a055d9fa
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

:point_up_2:  Python猫 ” ,一个值得加星标的 公众号

花下猫语: 这是 packaging 系列的第三篇译文,该系列是全网关于此话题的最详尽(水平也很高)的一个系列。原作者是 Python 官方打包团队成员,是 virtualenv 和 tox 项目的维护者,及 setuptools 和 pip 项目的贡献者。

qeaU7rM.jpg!web

英文 | Python packaging - Growing Pains

原作 | BERNAT GABOR

译者 | 豌豆花下猫

声明 本文获得原作者授权翻译,转载请保留原文出处,请勿用于商业或非法用途。

在前两篇文章中,我介绍了Python 具有的包类型以及包的构建方式,尤其介绍了 PEP-517/518。尽管这些更改主要是为了使打包变得更健壮,但是在实施和发布时,我们却遇到了一些问题。这篇文章将介绍一部分,希望可以为大家提供经验教训,并提出一些有趣的问题以待将来解决。

查看 PEP-517 和 PEP-518 的改动,可以认为构建后端(亦即 setuptools、flit)几乎没有做什么,只是通过 Python 模块提供了功能接口。大部分繁重的工作都在构建前端上,它需要生成隔离的 Python,然后以新的方式调用构建后端。如今当我们谈论构建前端时,我们的选项主要是 pip 或 poetry(和开发者的 tox)。

这些项目由社区维护,由少数活跃的开发者在空闲时间维护。他们并没有因此获得报酬,而且需要谨慎考虑这些工具被使用的多种方式。考虑到这一点,在 PEP 被接受之后,还花了几乎两年时间才首次实施就不足为奇了。计划、测试和实施已经在背后进行了一年多。

但是,尽管做了所有准备工作,不可避免的是,第一版确实破坏了一些软件包,在大多数情况下,人们做的某些操作使维护人员感到惊讶。让我们试着了解其中一些例子,以及它们是如何被解决的。

BvYrQzZ.jpg!web

Mink Mingle摄/Unsplash--准备好出发!

PEP-518

此 PEP 引入了TOML文件格式。【2】一种专门为了易于读/写配置而创建的格式。尽管在 build-system 部分下介绍了打包配置,但其它工具可以自由地将其配置放在 tool:name 部分下,因为它们拥有 PyPi 命名空间中的名字。各种工具立即开始利用这一点(例如Towncrier【3】、 black【4】等)。

当pip 18.0(于2018年7月22日发布) 【5】添加对 PEP-518 包的支持时,使用 pyproject.toml 最早出问题,因为 PEP-518 要求所有带 pyproject.toml 的软件包必须指定 build-backend 部分。但是,软件包事先仅将其用于其它项目的配置文件,由于它们没有事先指定它,当 pip 碰到这些文件时,就会引发错误,提示 pyproject.toml 文件无效。

PEP-517

pip wheel 缓存问题

pip 在 PEP-517 世界中的安装方式是首先生成一个 wheel,然后将其提取。要进入 PEP-517 世界,必须指定 build-backend 键,否则每条声明都需要退回到使用 setup.py 命令。

当 pip 构建 wheel 时,默认情况下会通过缓存系统完成。这是一种提速机制,为了在多个虚拟环境需要同一个 wheel 时,我们不用对其进行重建,而是重复使用它。PEP-517 wheel 的构建操作也利用了这一机制。

但是,当你禁用缓存时,这就变得很麻烦。因为没有目标文件夹可用于构建 wheel。所以构建过程将失败,请参阅附录的问题。【6】这个问题虽然很早就显现出来了,但由于大多数 CI 系统都在启用该选项的情况下运行。仅在一天后,pip 19.0.1 修复了该问题。

pyproject.toml 没有加入 setuptools 中

事实证明,构建后端实际上要做的工作不仅仅是 PEP-517 中描述的公开其 API。后端还需要确保 pyproject.toml 被附加到已构建的源码包中,否则用户计算机上的构建后端将无法使用它。setuptools 1650【7】将为setuptools【8】修复此问题,在早期版本中,只需在 MANIFEST.in 中指定 pyproject.toml 即可。

jyqm2qv.jpg!web

Jorge Zapata摄/Unsplash--什么? 那永远不会发生

从 setup.py 中导入构建的包

另一个意外问题是从 setup.py 内导入软件包时。按照约定,软件包的版本既作为软件包的元数据公开(setup.py 中的 setuptools,setup 函数的 version 参数),也在软件包根目录的__version__ 变量公开。可以在两个地方都指定变量的内容,但是要使其保持同步就很麻烦。

一种解决方法:许多程序包将其放在根目录的 version.py 中,然后同时从 setup.py 和程序包根目录导入它,像这样 from mypy.version import __version__ as version 。这能起作用,因为当有人调用 Python 脚本时,当前的工作目录会自动被添加到 sys.path 中(因此你可以导入公开在其下的内容)。

但是,这种添加当前工作目录的行为从来不是强制的,更多的是通过 python setup.py sdist 调用构建时,产生的副作用。由于这种行为是副作用(并非保证),因此从 setup.py 导入的所有项目都应在构建开始时,将脚本文件夹显式地添加到 sys 路径。

是否该在打包期间(当尚未构建/分发时)导入已编译的软件包,这尚有争议(尽管 Python 打包组倾向于这样做)。然而,实际上当 setuptools 通过 setuptools.build_meta 暴露其接口时,它选择不把当前工作目录添加到系统路径。

PEP 从未要求后端做此添加,因为大多数构建后端(本质上是声明式的)根本不需要它。因此,此类功能被认为是前端的责任。setuptools 认为,如果用户需要此功能,则应在 setup.py 中明确指出,并提前手动在 sys.path 中添加相应的路径。

为了简化 pip 代码库,pip 决定加入 PEP-517,让所有人在 setuptools 后端加上 pyproject.toml。现在因为这个问题,即使没有选择加入 PEP-517 的程序包也出现崩溃。为了解决这个问题,setuptools 添加了一个新的构建后端(setuptools.build_meta:__ legacy__),当未指定构建后端时,前端可将其用作默认值;当项目添加 build-backend 键时,它们还必须更改其 setup.py,要么将源码根目录添加到 sys.path,要么避免从源码根目录导入。

自举的后端

还出现了另一个有趣的问题,该问题的用户群更加紧密,但是却暴露了一个有趣的问题。如果我们不想使用 wheel,我们只能通过源发行版进行设置;我们应该如何解决”如何提供构建后端的构建后端的问题“?例如,setuptools 通过setuptools 打包自身。也即当 setuptools 通过 PEP-517 指定了这一点时,构建前端将被放入无限循环内。

要安装 pugs 库,它首先会尝试创建一个隔离的环境。这个环境需要 setuptools ,因此构建前端就需要构建一个 wheel 来满足它。wheel 构建本身将触发隔离环境的创建,该环境又依赖于 setuptools。

如何打破这个循环?要求所有构建后端必须暴露为 wheel?允许后端构建自身?这些自建后端是否应该负担依赖项?漫长的各种观点间争论,利与弊,所以如果你有兴趣,请进入python Discourse board【9】,发表你的意见。

vmuuQf3.jpg!web

Sneaky Elbow摄/Unsplash--我们是一伙的

小结

打包是很难的。在业余时间完善打包系统,使用户可以在打包期间编写和运行任意代码,但还不引起任何破坏,这几乎是不可能的。

现在有了 PEP-518,构建时依赖项是明确的,并且构建环境易于创建。有了 PEP-517,我们可以使用更具声明性的打包命名空间,这减少了用户犯错的可能,当错误不可避免时,也能提供更好的消息。

诚然,在进行这些更改时,某些程序包可能会损坏,并且我们可能令曾经有效的方法失效。但是,我们(PyPa 的维护者)并不是出于恶意而这样做的,因此,当出现错误时,请务必填写详细的错误报告,例如什么错误、你的使用方法,以及你的用例。

我们努力在真诚地改善打包生态系统,为此我们创建了集成测试【10】存储库,以确保将来至少可以捕获到其中的一些边缘用例,免得它们落入到你的机器中。如果你对打包有任何建议或诉求,请随时在“ 讨论Python论坛【11】”的打包部分进行讨论,或者为相关工具提一个 issue。

VVviUbe.jpg!web

Milan Popovic摄/ Unsplash--结束了

先到此为止了,谢谢阅读完!我要感谢Paul Ganssle【12】审阅了打包系列文章,并要感谢Tech At Bloomberg【13】允许我在工作期间作开源贡献。

相关链接

[1] Python packaging - Growing Pains: https://www.bernat.tech/growing-pain/

[2] TOML文件格式。 : https://github.com/toml-lang/toml

[3] Towncrier: https://pypi.org/project/towncrier/

[4] black: https://pypi.org/project/black/

[5] pip 18.0(于2018年7月22日发布): https://pip.pypa.io/en/stable/news/%23id61#id61

[6] 请参阅附录的问题。 : https://github.com/pypa/pip/issues/6158

[7] setuptools 1650: https://github.com/pypa/setuptools/pull/1650

[8] setuptools: https://github.com/pypa/setuptools/pull/1650

[9] python Discourse board: https://discuss.python.org/t/pep-517-backend-bootstrapping

[10] 集成测试: https://github.com/pypa/integration-test

[11] 讨论Python论坛: https://discuss.python.org/c/packaging

[12] Paul Ganssle: https://twitter.com/pganssle

[13] Tech At Bloomberg: https://twitter.com/techatbloomberg

译者简介: 豌豆花下猫,生于广东毕业于武大,现为苏漂程序员,有一些极客思维,也有一些人文情怀,有一些温度,还有一些态度。

优质文章,推荐阅读:

2019 年 10 大顶级 Python 支持库

别让自己“墙”了自己

辟谣错误的爬虫说法,使用正确的爬虫姿势

Python 中 -m 的典型用法、原理解析与发展演变

ieYVBr2.jpg!web

感谢创作者的好文


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK