• Python包管理发展历程
  • 发布于 1个月前
  • 48 热度
    0 评论
  • Rock
  • 1 粉丝 30 篇博客
  •   
2018年4月16日,Python包管理局(PyPA)部署了Python项目的官方在线存储库PyPI(发音pie-pea-eye)的新版本。以alpha和beta形式将新版本托管在https://pypi.org上;原始URL(https://pypi.python.org/pypi)现已重定向到这个新的、更简单的URL。
在最初被称为Monty Python skit接着又被非正式地称作cheese shop后,PyPI 2.0,这个名为 Warehouse的代码,使用了在第一个版本建立时不可用的工具来构造一个更现代的体系结构。

PyPI并不是打包生态系统中唯一需要发展的部分:用于构造Python项目、构建Python发行版和安装这些发行版的方法在过去两到四年中已经得到了改进。对于新版本的PyPI,这里以下是一个能促使你不断进步的高层次的修改概览。


如果您刚接触Python或Python中的包,我建议您首先阅读《The Hitchhiker"s Guide to Python》和《PyPA Python Packaging User"s Guide》来了解最新的技术动态。
要概观PyPI的变化和所采取的潜在行动,从Python官方博客帖子入门是个不错的选择。Sumana Harihareswara关于LWN.NET的文章是对PyPI历史和它所经历的变化的一个很好的概述。


依赖管理器:简化隔离和增加分辨率


在2008年创建、2011发布的pip工具,在相当长的一段时间里充当着Python事实上的安装程序。这是一个很好的工具,但是使用PIP本身有两个关键难点:
项目隔离:如果两个不同的项目需要同一库的两个不同版本,开发人员如何确保项目使用正确的库版本?
依赖同步:如果项目上的开发人员添加了新的依赖包或升级了现有的依赖包,开发人员又如何确保项目上的其他开发人员确定性地同步他们的依赖关系图?

为了解决第一个痛点,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列为正式推荐。


显然,PyPA推荐PiPeV用于应用程序,但不推荐用于库。库的依赖性需要灵活地定义,Pipenv严格地靠Pipfile.lock管理依赖关系,所以不适合这项任务。

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。


总之,尽管pip仍然是安装分发包的关键工具,并且虚拟环境对于隔离仍然是必需的,但是许多新工具使安装和隔离更加无缝。这些工具中的一些引入了依赖包解析,确保随着时间推移为不同的开发人员一致地安装依赖关系树。Pipenv是管理应用程序依赖包的官方新工具,但它不是唯一的选择,并且替代方案可能更契合需求。
若要了解使用Pipenv的更多信息,我推荐Lacey Williams Henschel和Jeff Triplett关于这个话题的文章。
David Jetelina在一篇关于Pipenv的评论中阐述了Pipenv的优点和缺点。
 
对稳健型项目结构的新建议
单个Python文件是一个模块。一组Python文件可以成为Python程序包。

以前,开发人员如何决定在源代码库中组织模块和包取决于她,并且很大程度上被视为首选。然而,对于Python库(旨在共享的代码)应该如何组织代码的共识越来越多,大家都注意到了代码组织中存在的一些陷阱。


2014年,Ionel Cristian Mărieș首先讨论了使用src/目录来保护Python代码免受特定陷阱的影响,但反响不一。Hynek Schlawack在2016年指出,他曾无视这种方法,然后就被Lonel文中提到的问题坑了。最终,像PyTest这样的工具现在也推荐使用这种结构,并且(正如Hynek在文章中指出的)它使得正确使用Tox进行测试更加容易。

我认为这是一个日益一致的共识,因为PyPA对于创建一个示例项目的指示明显不遵循SRC/结构。尽管如此,该项目还是在Read Me中声明它“不打算覆盖整个Python项目开发的最佳实践”。


无论如何,如果您正在启动一个新的Python项目,或者遇到上述文章中描述的问题,基于Ionel列出的所有原因,您可能对切换项目的目录结构感兴趣。

构建Python库的新工具


Python源代码的生成工具的演变可能是本文讨论的最重要一系列变化。这些变化需要比其他主题更仔细地观察(打起精神,我们继续!)
由其他包共享和安装的Python源代码包或捆绑包的正式称谓叫做分发包。它们的命名可以避免混淆Python包(Python模块的集合)的概念。
分发包可以是源码分发包或内置分发包。如果有人正在安装源码分发包(例如,直接从GitHub安装),他们的安装程序必须在这个过程中执行构建步骤。相比之下,安装程序可以简单地将生成的分发包放在正确的位置。“安装”这个术语随便用来指代构建分发包的放置或组合的构建过程和放置。

内置的分发包有多种格式,但在此我们只关注两种Python格式:eggs和wheels。Eggs最早可在Python 2.3中使用,但已被最早在2012年PEP 427中提出的wheels有效地替换。您可以通过阅读PyPA打包指南或wheels包的文档了解他们更多的差异。


如今,两种工具使用这些格式: distutils和setuptools,来构建和分发Python代码。

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文件不再是强制性的。


正如PEP 518所描述的,开发人员现在可以在他们的代码库中写一个名为pyproject.toml的TOML文件,以指定他们想要使用什么工具来构建分发包。TOML文件可以另外配置这些构建工具,并可以为其他工具提供设置。您现在可以使用这些文件:自从PR 4144在2017年5月合并以来,pip已经知道在存储库和源代码分发包中查找这些文件。

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的原作者之一)。


我觉得离尘埃落定、这些工具找到它们的沟槽还有一段时间。值得注意的是,Flit不支持本文前面讨论的src/项目结构。并且,Poetry在2018年2月才首次提交,现仍然处于pre-1.0。
总而言之,使分发包的构建更加模块化、并允许使用新工具已是天翻地覆的变化了。

结论

依赖管理器的生态系统正在迅速改善,但混乱也随着变化出现。Pipenv、Poetry、Hatch wrap pip和虚拟环境,但他们之中没有一个能取代pip或虚拟环境。他们之中的每一个都能提供不同的特性,解决不同的问题。正如PyPA成员Thea Flowers所指出的,没有一个工具能满足所有的要求。

构建工具也在快速发展,我希望我们在现有的工具成熟时的未来会看到更多。


如果你想知道使用哪些工具,首先问问自己你在努力实现什么。您需要依赖管理器还是分发构建器?你是在编写一个库还是一个应用程序?你需要支持旧的设置(可能仍然需要使用setup.py)吗,或者你的分发可以面向未来吗?对这些问题的回答会提示你做出选择。我发现自己唯一具有规定性的地方是使用src/项目结构,因为它避免了隐含的错误,并使得项目中新手开发人员的生活更容易——但即便如此,也不是每个人都认同这点。


打包Python长期以来一直是语言和社区的痛点(参见2013年Nick Coghlan关于用Python进行打包的说明)。上述变化是喜闻乐见的,我们在此诚挚地感谢辛勤工作对此做出贡献的个人。请花点时间在Twitter、Github上或亲自感谢他们吧!


Dustin Ingram是本文的审稿人之一。在PyCon US 2018上,他就此话题发表了演讲。你可以在YouTube上看到他精彩的演讲。
这里所涉及的主题只是过去和现在所有工作中的一小部分。PyPA"s history of Python packaging 提供了一个自1998以来的变化列表,这是一个引人入胜的回顾。

用户评论