PyPI并不是打包生态系统中唯一需要发展的部分:用于构造Python项目、构建Python发行版和安装这些发行版的方法在过去两到四年中已经得到了改进。对于新版本的PyPI,这里以下是一个能促使你不断进步的高层次的修改概览。
依赖管理器:简化隔离和增加分辨率
为了解决第一个痛点,Python开发人员曾依赖虚拟环境。最初,这包括安装和配置virtualenv 或 virtualenvwrapper。从Python 3.3开始,Python也提供了内置的venv 模块,为开发者提供了另一种选择。
第二个痛点在Python世界中一直没有得到解决。开发人员依赖于setup.py(本文稍后将讨论)和指定的具有依赖项列表的 requirements.txt的约定。根据开发人员的意图,通常建议精确(固定)或有限制(例如,Django>=2.0)地指定依赖包的版本。指定的目的是确保无论谁安装、何时安装都能确保安装的一致性。然而,正确的固定或限制版本对开发人员是个复杂的手工活儿。一大主要困难来自于管理依赖包的依赖性(等等)。因此,仅使用pip确保重复安装中依赖包版本相同是非常困难的。
PIPNEV在2017年1月首次宣布减轻双方痛点。Pipenv充当pip和虚拟环境的包装器,并提供了使用两个工具解决第一个难点的无缝衔接式体验。Pipenv通过实现依赖性解决方案和自动化行为减轻了第二个痛点。例如,Pipenv保存了正在安装的依赖关系包的名称和版本,以便开发人员可以放弃手动更新requirements.txt。Pipenv不依赖于requirements.txt中的依赖包列表,而是定义并创建Pipfile和Pipfile.lock文件来管理依赖包。第一个文件定义项目的直接依赖包,而第二个文件保存所有安装的依赖包,确保安装的一致性不受时间影响。团队的开发人员在切换分支或从远程拉出时仍然需要记住同步它们的依赖包,但是Pipenv将工作简化为单个命令:pipenv sync。
Pipenv在应用程序开发中的好处导致PyPA推荐它用于应用程序的依赖管理。PyPA首先在2017年11月添加了Pipenv管理应用程序依赖包的教程,然后在2018年2月将Pipenv列为正式推荐。
Pipenv因PyPA的推荐而备受关注,但它并不是唯一的新的依赖管理器。例如,Poetry和Hatch都提供了与Pipenv重叠的功能。所有三个工具都包着PIP和VielalEnv来处理第一个痛点。然而,这是工具在它们的特征集中开始分散的地方。
Poetry和Pipenv都致力于解决依赖性进而解决第二个痛点。值得注意的是,Poetry试图使依赖解决方案比Pipenv的实现更可靠。更重要的是,Poetry意在同时管理应用程序和库中的依赖包。我们将在本文后面讨论这个原因。
另一个在Pipenv、Poetry和Hatch-pip工具之前的依赖管理器通过确保一致的安装来关注第二个难点。它基于另一个文件的内容生成requirements.txt,这种文件格式可以是:requirements.in——一个由pip-tools指定的文件格式,或者其他setup.py(将在稍后讨论)。它定义了用于同步环境的单个命令,使得团队中的开发人员很容易停留在同一个页面上,这与Pipenv非常相似。
并非所有工具在开发期间都关注代码库的依赖包管理;有些工具专注处理开发之外的依赖项。例如,pipsi允许将Python命令行应用程序安装在单独的虚拟环境中,使它们看起来是全局的,同时又将它们彼此隔离。例如,如果两个命令行脚本需要两个不同版本的Click,则pipsi启用两个工具的安装。Jacob Kaplan Moss是Django最初的核心贡献者之一,他在他的setup中使用Pipsi安装Pipenv。
以前,开发人员如何决定在源代码库中组织模块和包取决于她,并且很大程度上被视为首选。然而,对于Python库(旨在共享的代码)应该如何组织代码的共识越来越多,大家都注意到了代码组织中存在的一些陷阱。
我认为这是一个日益一致的共识,因为PyPA对于创建一个示例项目的指示明显不遵循SRC/结构。尽管如此,该项目还是在Read Me中声明它“不打算覆盖整个Python项目开发的最佳实践”。
构建Python库的新工具
内置的分发包有多种格式,但在此我们只关注两种Python格式:eggs和wheels。Eggs最早可在Python 2.3中使用,但已被最早在2012年PEP 427中提出的wheels有效地替换。您可以通过阅读PyPA打包指南或wheels包的文档了解他们更多的差异。
Python的distutils自2000年末与Python 2.0并行发布的Python 1.6以来,一直被用于捆绑Python代码。2000年11月编写的PEP 229第一次概述了使用distutils实现Python自己分发Python代码的意图。
setuptools项目始于2004年,构建于distutils之上,其目的是克服distutils中的限制,并包括名为easy_install的工具;setuptools是引入eggs格式的工具。Python分发遵循distutils强加的基本规则。特别是,所有Python打包和分发工具(包括pip和Pipenv)都希望源代码分发包的根目录中存在名为setup.py的文件。此文件描述distutils和setuptools如何从源代码创建内置分发包。
PEP 517和PEP 518——分别在2017年9月和2016年5月被接受——通过使包的作者能够选择不同的构建系统,改变了这种现状。换句话说,开发人员可选择使用除distutils或setuptools之外的分发包构建工具,这在Python中史无前例。Python库中无处不在的setup.py文件不再是强制性的。
Github中关于pip的资料档案库提供了一个如何编写pyproject.toml文件的例子。在这种情况下,pip定义使用setuptools和wheels构建分发包,并进一步配置Amber Brown的towncrier项目来生成新闻。然而,Python包的作者最终可以选择用Flit或上述Poetry等工具来构建分发。没错:Poetry不仅仅是一个依赖性管理者,而且是一个使用pyproject.toml的分发构建者和发行者。尽管Python核心贡献者Brett Cannon建议使用Flit,核心贡献者Mariatta Wijaya似乎也同意这一点,但Poetry也因为它的范围,开始引起像Jannis Leidel这样的人的注意(Jannis Leidel是pip的原作者之一)。
构建工具也在快速发展,我希望我们在现有的工具成熟时的未来会看到更多。
如果你想知道使用哪些工具,首先问问自己你在努力实现什么。您需要依赖管理器还是分发构建器?你是在编写一个库还是一个应用程序?你需要支持旧的设置(可能仍然需要使用setup.py)吗,或者你的分发可以面向未来吗?对这些问题的回答会提示你做出选择。我发现自己唯一具有规定性的地方是使用src/项目结构,因为它避免了隐含的错误,并使得项目中新手开发人员的生活更容易——但即便如此,也不是每个人都认同这点。
打包Python长期以来一直是语言和社区的痛点(参见2013年Nick Coghlan关于用Python进行打包的说明)。上述变化是喜闻乐见的,我们在此诚挚地感谢辛勤工作对此做出贡献的个人。请花点时间在Twitter、Github上或亲自感谢他们吧!
或是邮件反馈可也:
askdama[AT]googlegroups.com
订阅 substack 体验古早写作:
关注公众号, 持续获得相关各种嗯哼: