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

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


英文 | Python packaging - Growing Pains【1】

原作 | BERNAT GABOR

译者 | 豌豆花下猫

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


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

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

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

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

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进阶系列、好书推荐系列、技术写作、优质英文推荐与翻译等等,欢迎关注哦。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容