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

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

英文 | Python packaging - Growing Pains【1】python

原做 | BERNAT GABORgit

译者 | 豌豆花下猫github

声明 :本文得到原做者受权翻译,转载请保留原文出处,请勿用于商业或非法用途。bootstrap

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

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

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

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

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 便可。

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】,发表你的意见。

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

小结

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

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

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

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

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

公众号【Python猫】, 本号连载优质的系列文章,有喵星哲学猫系列、Python进阶系列、好书推荐系列、技术写做、优质英文推荐与翻译等等,欢迎关注哦。

相关文章
相关标签/搜索