Pipenv: 提案:python 库项目的`pipenv` 模式和反模式

创建于 2018-04-05  ·  74评论  ·  资料来源: pypa/pipenv

黑客maya我学到了很少的教训,这导致了我在 python 库中推荐使用pipenv以下建议。 我希望其他人审查该提案,如果我们达成一致,(更新后的)文本最终可能会出现在pipenv文档中。

pipenv python 库项目的模式和反模式

编辑
以下最适用于一般(主要是开源)python 库,这些库应该在不同的 Python 版本和操作系统上运行。 在严格的企业环境中开发的库可能会有所不同(无论如何请务必查看所有问题部分)。

编辑结束

TL;DR :将pipenv文件添加到 python 库项目中可能会引入额外的复杂性,并且可以隐藏一些错误,同时不会向库安全性添加任何内容。 出于这个原因,PipfilePipfile.lock.env在库源代码控制之外。

无论.gitignore的文件如何,您都可以

Python 库与 Python 应用程序

我所说的 python是指一个项目,通常有setup.py ,目标是在不同的 python 版本和/或操作系统的各种平台上分发和使用。

例如mayarequestsflask等。

另一方面(不是 python 库)有针对特定 python 解释器、操作系统的应用程序,并且通常部署在严格一致的环境中。

pipfilePipfile 与 setup.py 中很好地描述了这些差异。

什么是pipenv (部署工具)

我完全同意这样的说法,即pipenv是部署工具,因为它允许:

  • 为部署虚拟环境定义严格的要求( Pipfile.lock
  • 在不同的机器上以可重复的方式应用这些严格的要求

当必须在多个开发人员之间非常一致地部署应用程序或在 python 环境中开发时,它会有所帮助。

如果人们期望它创建 python 库或深入参与它们的创建,则调用pipenv打包工具是一种误导。 是的, pipenv可以提供很多帮助(在库的本地开发中),但可能会造成伤害(通常在 CI 测试中使用时没有深入思考)。

在错误的上下文中应用“安全原因”

TL;DR : pipenv通过应用Pipfile.lock文件中描述的批准的具体依赖项提供安全环境, python 库只允许定义抽象依赖项(因此不能提供Pipfile.lock )。

pipenv在以下步骤的部署场景中大放异彩:

  • 定义抽象依赖(通过Pipfile
  • 从中生成具体的依赖关系,导致Pipfile.lock
  • 创建(虚拟)python 环境反映那些具体的依赖
  • 运行测试以确保给定的环境按预期工作并且是安全的
  • 发布经过测试的“黄金” Pipfile.lock作为批准的python 环境的定义
  • 其他人可以使用pipenv sync在其他地方应用“黄金” Pipfile.lock获得相同的 python 环境。

随着python库的发展,人们无法实现这种安全性,因为库不能定义具体的依赖项。 打破这个规则(因此试图通过 python 库声明具体的依赖项)会导致如下问题:

  • 找到满意的共享库版本的问题(每个严格的包定义了共享库的确切版本,很可能版本会有所不同并阻止找到普遍可接受的版本)
  • 具体依赖项可能取决于 python 版本、操作系统或其他环境标记,并且尝试在不同的上下文中安装包很容易无法满足原始抽象依赖项中定义的一些规则。

问题:隐藏损坏的setup.py定义的依赖项

setup.py应通过install_requires定义所有抽象依赖项。

如果Pipfile定义了这些依赖项,它可能很容易隐藏以下问题:

  • install_requires缺少依赖项
  • Pipfile为依赖项定义了特定的规则(版本范围等),而install_requires没有。

为了防止它,请遵循以下规则:

  • 库定义的依赖项不能出现在Pipfile
  • [packages]Pipfile应为空或只定义在库本身单一依赖。

问题:存储库中的Pipfile.lock

在库存储库中保留Pipfile.lock (通常是出于“安全原因”)是错误的,因为:

  • 描述的依赖项可能对不同的 Python 版本或在其他操作系统中无效
  • 开发人员不仅在添加/删除某些依赖项时被迫更新文件,而且在其他库更新并且可能在库中可用时也被迫更新文件。

为了防止它,人们应该:

  • 从存储库中删除Pipfile.lock并将其添加到.gitignore

问题:与tox竞争(隐藏usedevelop

如果tox.ini包含在它的commands部分条目中,例如:

  • pipenv install
  • pipenv install --dev
  • pipenv lock

这通常是一个问题,因为:

  • pipenv install应仅安装库本身,并且tox (默认情况下)也这样做。 除了口是心非,也可防止usedevelop=Trueusedevelop=Falsetox.ini ,因为Pipenv能够仅在一个变体(和表达它tox.ini允许不同环境中的差异)。

为了防止它,人们应该:

问题:破坏构建,如果pipenv失败

pipenv正在大力开发中,有时会出现问题。 如果此类问题破坏了您的 CI 构建,则可以通过不使用pipenv和使用传统工具(通常更成熟一些)来防止失败。

为了防止它,人们应该:

  • 在将pipenv到 CI 构建脚本、 tox.ini或类似位置之前,请三思。 您知道添加它会获得什么价值吗? 可以使用现有工具完成这项工作吗?
  • 不要“仅仅出于安全原因”或“每个人都这样做”而添加它。

概括

关于pipenv在 python 库开发中的作用的关键问题是:

  • pipenv真正带来了什么价值? A: Virtualenv 管理工具。
  • pipenv相关用例是什么? A:管理 virtualenv。
  • 它会出现在库存储库中吗? 答:不。

更多细节和技巧如下。

pipenv不会为您的包裹增加任何安全性

不要仅仅因为每个人都这样做或因为您期望额外的安全性而将其推入项目。 它会让你失望。

使用具体(和批准的)依赖项进行保护应在应用程序使用您的库的后期阶段进行。

PipfilePipfile.lock.env文件保留在存储库之外

将文件放入.gitignore

Pipfile很容易重新创建,如下所示,因为大多数或所有要求已经在您的setup.py 。 并且.env文件可能包含私人信息,不得共享。

将这些文件保留在存储库之外将防止在不合适的情况下使用pipenv时 CI 构建可能发生的所有问题。

pipenv作为开发者的私人工具箱

pipenv可以简化开发人员作为 virtualenv 管理工具的工作。

诀窍是学习如何快速重新创建您的(私人) pipenv相关文件,例如:

$ cd <project_repository>
$ # your library will bring the dependencies (via install_requires in setup.py)
$ pipenv install -e .   
$ # add more dev tools you preffer 
$ pipenv install --dev ipython pdbpp
$ # start hacking
$ pipenv shell
...

如果您需要方便的方法来设置环境变量,请使用.env文件。

请记住:不要在 CI 构建中使用pipenv ,您的生活会更简单。

技巧:使用setup.py能力来声明额外的依赖项

在您的setup.py使用extras_requires部分:

from setuptools import setup

setup(
    name='mypackage',
    ....,
    install_requires=["jinja2", "simplejson"],
    extras_require={
        'tests': ['pytest', 'pyyaml'],
        'pg': ['psycopg2'],
    },
    ....
)

要安装为tests额外声明的所有依赖项:

$ pipenv install -e .[tests]

请注意,它将始终包含install_requires依赖项。

此方法不允许将依赖项拆分为 default 和 dev 部分,但这在预期场景中应该不是真正的问题。

Discussion Type

最有用的评论

@Moritz90 Python 的几个邮件列表将是举行此讨论的好场所。

pypa-dev是围绕 Python 打包及其周围生态系统进行讨论的最明确的工具。 如果我要发布类似的讨论,我可能会从这里开始。

python-ideas是一个讨论

所有74条评论

这非常令人印象深刻,非常感谢您的编译。 稍后肯定会更详细地审查

/cc @uranusjr @jtratner @ncoghlan

maya问题的一些参考:

  • kennethreitz/maya#138(从存储库中删除Pipfile.lock)
  • kennethreitz/maya#139(跳过在 tox.ini 中运行的 pipenv ...)
  • kennethreitz/maya#145(在 setup.py 中修复pendulum>=1.0 :版本在 Pipfile 中但在 setup.py 中缺失)
  • kennethreitz/maya#143(公关展示了pipenv问题如何破坏整个 Travis 运行)
  • kennethreitz/maya#144(根据半官方最佳实践,PR Refactor pipenv 使用)

我也喜欢这个。 也许我们应该将其添加到 Pipenv 的文档中,甚至是Python Packaging User Guide

上述建议的推论似乎是“放弃确定性/可重复的 CI 构建”,这在我看来是一个非常大的反模式。

您提出什么作为仍然允许确定性的替代方案?

@tsiq-oliverc 确定性构建目前占有一席之地,将构建一个应用程序。

想象以下尝试执行真正确定性的 Python 库构建:

  • 构建必须基于Pipfile.lock
  • 每个执行上下文(每个 python 和 OS 变体的组合)可能有不同的Pipfile.lockPipfile定义的库抽象依赖项产生
  • 存储库必须提供在存储库中定义的单独的Pipfile.lock实例。 请注意,在 CI 构建期间自动构建Pipfile.lock不会添加任何确定性

这是很多额外的努力。 你得到的是一个库,它将安装在不同的上下文中(例如,一周后标准安装将获取一两个升级的依赖项)并且不会从事实中得到任何东西,你使用了Pipfile.lock ,它目前已过时。

冲突在于库绝不能在内部定义严格的依赖关系。

如果您认为,还有另一种替代方法可以为 python 库获得确定性构建 - 描述它。

@vlcinsky - 如果您的库的使用者使用不同版本的依赖项等,那么这将超出您的控制。 所以我同意图书馆维护者没有可行的方法来管理它。

但这里的目标可能要小得多。 特别是,我认为库维护者的目标如下(大致等效):

  1. 如果您运行 CI 两次,您肯定会得到相同的结果(尽管存在网络问题!)。
  2. 您可以在本地重新创建(从而调试)您在 CI 上观察到的行为,即使这意味着运行 Docker/etc。 当地。
  3. 您可以自信地对您的消费者说“我的库在依赖版本 X、Y、Z 下的行为符合预期”。

如果这三件事中的任何一件不成立,我认为这与质量控制背道而驰。

所以是的,我会说,如果你保证为你的消费者支持 Python 变体 A、B 和 C,并且它们的行为足够不同以至于一个锁文件(等)不会切断它,那么你应该有三个锁文件(或任何)。

不过,我还没有足够地使用 Pipenv 来知道这在实践中有多容易。

我目前正在考虑将Pipfile添加到 CI 系统的一些库项目中。

我绝对需要依赖锁定(+散列)来遵守公司范围的安全准则,而且我目前不需要使用不同的 Python 版本进行测试,因为只有一个正式支持。 事实上,pipenv 简化了本地开发环境的设置,包括 virtualenv,这是一个很好的副作用。

你得到的是一个库,它将安装在不同的上下文中(例如,一周后标准安装将获取一两个升级的依赖项)并且不会从事实中得到任何东西,你使用了Pipfile.lock ,它目前已过时。

这并非普遍适用。 在企业软件领域,您仍然拥有官方支持的非常特定的环境,并且依赖项中的安全问题会导致您的产品被更新,而不是客户自己更新依赖项。

(是的,我说的是图书馆,而不是这里的应用程序......)

@Moritz90您的场景适用于企业环境中的 python 库, pipenv可能会有所帮助,因为它是更具确定性的环境。

我的描述是针对一般的 Python 库,例如flaskrequestmaya等,其中上下文更加可变。 试图解决maya几件事我感到沮丧,在许多情况下, pipenv引入真正的问题(通常隐藏通常会检测到的问题),而没有提供太多或任何添加价值。

获得确定性的构建是一件好事,但它会产生成本。 如果做错了,你可能会为较低质量的结果支付额外的费用——这就是我想要防止的。

我认为这是我们不希望构建具有绝对确定性的实例之一。 如果您不使用==固定您的依赖项,则您承诺在默认情况下维护对多个版本的支持,并且应该以这种方式设计库。 破坏 CI 构建的依赖项升级实际上是一件好事,因为它暴露了库中的错误。 完全确定性的依赖(由 Pipenv 管理)会掩盖这一点。 能够在需要时确定性仍然是有益的,这通常不是最好的。

@uranusjr - 当然。 我同意,如果愿望是“非确定性构建”,那么上面的建议可能很有意义。 事实上,这几乎是一个逻辑等价,可以更简洁地表述:“如果您不想要确定性构建,那么不要使用旨在确保确定性构建的工具 ( pipenv )” 😄。

但总的来说,这肯定不是一个理想的目标。

@tsiq-oliverc 很好的范围定义 - 它支持重点讨论。 我还要添加一个要求: CI 确定性不应隐藏测试库中可能存在的问题。

如果我们使用Pipenv.lock ,基于它创建 virtualenv 并运行库的 CI 测试,我们完成了库应该做的部分功能 - 安装适当的依赖项。 如果库在这方面以某种方式被破坏 - 预装环境会隐藏这个问题。

对我来说,检测库中的问题似乎比以确定性方式运行 CI 更重要。 如果有办法做到这两者(例如在私有 pypi 索引后面运行测试,这也可以支持确定性)我没有问题,但如果有冲突,我有我的优先事项。

不要误会我的意思:我不想运行非确定性构建,我的愿望是运行 CI 构建,这将检测尽可能多的问题。

@vlcinsky当然,我只是想分享我的经验,以确保更新的文档也反映了它。 当前文档在解释权衡方面做得很好:

对于库,通过 setup.py 中的 install_requires 定义抽象依赖项。 [...]
对于应用程序,定义依赖项以及在 Pipfile 中获取它们的位置,并使用此文件更新 Pipfile.lock 中的具体依赖项集。 [...]
当然,Pipfile 和 pipenv 对库开发人员仍然有用,因为它们可用于定义开发或测试环境。
而且,当然,有些项目的库和应用程序之间的区别不是那么清楚。

(突出显示适用于我的情况的部分。)

我只是想确保它保持这种状态。 我认为您的原始帖子包含太多笼统的声明,但没有免责声明您正在谈论将在 PyPI 上发布的开源项目。

@ Moritz90我完全同意。 我试图突出这个重点,但我可以让它更加明显。

@ Moritz90我添加了反映您评论的介绍性说明。

@vlcinsky - 这是有道理的。 据我所知,你不明确希望非确定性版本,但我认为这是不可避免地等同于你什么想(即渔获物问题,当你上游的依赖更新)。

大声思考,解决这两个相互冲突的目标的最佳方法是什么? 一种可能性是有一个两阶段的 CI 过程:

  1. 确定性阶段。 在您的回购中利用Pipfile.lock ,因此它是完全可重现的。
  2. 非确定性阶段。 运行pipenv update然后运行测试,以便它引入所有依赖项中的最新版本(我认为这与没有锁定文件的行为基本相同?)。

@tsiq-oliverc 要获得确定性构建,我会考虑以下设置:

  • 构建 pypi 缓存作业:从前运行,生成某种形式的 pypi 索引缓存(作为文件目录或任何类似内容)
  • 库测试工作:使用 pypi 缓存,但避免pipenv

使用pipenv进行安装类似于安装库本身应该做的事情,但它绝对不同,因为它是不同的代码完成工作。

构建pypi缓存作业

$ git clone <repo_url> <project_dir>
$ cd <project_dir>
$ pip install pipenv
$ $ # clean pypi cache and make it ready to cache somehow - not described here
$ pipenv install -e .[test]
$ # if we need extra testing packages in pipenv
$ pipenv install <extra_test_packages>
$ # record current requirements expressed in `Pipfile.lock`
$ pipenv lock
$ # if needed, record the `Pipfile.lock` somewhere

此类工作的输出是:

  • Pipfile.lock作为记录依赖(可以帮助开发者轻松复现环境)
  • 预先填充的本地 pypi 缓存

图书馆测试工作

有几个阶段:

  • 配置环境以仅使用我们本地的 pypi 缓存来强制toxpip
  • 运行 CI 测试(避免使用pipenv

我们得到了什么

  • 库在确定性环境中进行测试
  • 库经过测试,包括。 它能够自行安装
  • Pipfile.lock记录用于安装库的 pypi 包。 它可用于在开发人员站点重现环境。
  • 适应(可能是外部)pypi 上的升级包很简单(重新运行“构建 pypi 缓存作业)并以受控方式完成(pypi 的内容被记录,包括哈希值)

另一个优点是,这种设置不需要开发人员维护PipfilePipfile.lock 。 同样在不同的上下文中运行测试总是相同的( Pipfile.lock总是在给定的上下文中重建)。

还缺少什么(并且可以完成)

pypi 缓存是需要研究的部分。 我想,简单的目录就足够了,也许pipenv已经准备好帮助解决这个问题。 也许问题 #1731 是缺失的部分。

作为一个进行依赖解析的包,我们自己的许多测试依赖于确定性构建——也就是说,采用已知的东西并期待一个解析图。 我们为此使用pytest-pypi

喜欢关于这个话题的热烈讨论。 我认为细微差别很重要,您应该始终针对已知依赖项以及未固定的依赖项进行测试

您应该始终针对已知的依赖项以及未固定的依赖项进行测试

我赞同这个建议。 最好始终为可重现的构建提供明确的“已知良好状态”,并在更新破坏某些内容的情况下简化调试,同时确保较新的次要/错误修复版本也能正常工作。

(在我个人看来,理想的情况是包管理器默认安装最新的次要版本,以便库始终可以指定测试它们的具体依赖版本,但我意识到这是一个极具争议的观点,需要每个人跟随 semver。)

@Moritz90 @techalchemy @uranusjr @tsiq-oliverc

这是我之前讨论的总结。

特殊问题和建议的解决方案

许多执行上下文 - 谁应该维护Pipfile.lock文件?

每个支持的操作系统和 python 解释器都有助于可能的执行上下文矩阵。

例如 Flask 支持(至少在存储库中可见的 CI 内容):

  • 操作系统 Windows(python 2.7 和 Python 3.6)
  • Linux (python 2.7, 3.4, 3.5, 3.6, nightly, pypi)
  • OSX(py - 不确定是否有更多版本)

它产生了 9 个可能不同的不同执行上下文。

每个执行上下文可能有不同的Pipfile.lock

谁来维护它们?

选项是:

  • 让开发人员手动完成(NO WAY)
  • 主开发平台只维护一个Pipfile.lock (哪个平台喜欢被忽略?)
  • 通过 CI 自动创建(是)

建议:让 CI 通过pipenv install -e .生成文件。 不要将它包含在 repo 中,帮助开发人员选择正确的Pipfile.lock作为自动构建的结果。

开发人员需要可预测的环境

在修复可能由 pypi 依赖项更改引起的问题时,开发人员可能需要简单的方法来从失败的测试中重现环境。

提议:

  • 对于许多包,pypi 依赖项更改非常罕见,因此它们不是真正的问题
  • 为了自己修复环境,开发人员可以通过pipenv install -e .pipenv lock生成Pipfile.lock pipenv lock
  • 要从失败的测试中复制环境,开发人员从失败的测试中选择Pipfile.lock
  • 待办事项:显示示例,如何在tox.ini应用Pipfile.lock tox.ini

CI 必须显示损坏的setup.py

setup.py可能已损坏(缺少install_requires依赖项,缺少版本说明符等)并且 CI 测试不能隐藏此类问题(通过自行预安装省略的依赖项)。

提议:

  • 相信pipenv install -e .提供与普通安装相同的结果(目前存在一些问题)。
  • 运行普通安装测试(没有pipenv )并可能比较结果pip freeze输出是pipenv安装的内容的子集。

更新的 pypi 依赖可能会破坏事情,CI 应检测此类追逐

某些依赖项更新可能会破坏使用它的库。 CI 应检测此类问题的故障。

提议:

  • 至少一项测试必须针对未固定版本运行
  • 如果 CI 总是生成新的Pipfile.lock ,这不是问题(因为我们无论如何都在 unpinned 模式下运行)

不同库类型的 CI 模式

在所有建议的模式中,我试图避免将pipenv文件保留在存储库中,从而使开发人员免于维护这些非常复杂的东西(自动化!!!)。

与我的原文相反,第二和第三模式在 CI 脚本中确实使用了pipenv

模式:快跑,福雷斯特,快跑!

具有较少依赖项且不经常更改的简单包。

只需像pipenv时代之前一样运行,让我们保持简单。

很少有依赖项会导致麻烦的情况很容易修复,并且不能证明使 CI 更复杂是合理的。

方式:生成并密封

每次运行 CI 测试时,生成新的Pipfile.lock ,它完整地描述了当前使用的环境。

Pipfile.lock将成为 CI 人工制品。

如果出现问题,开发人员可以从损坏的构建中选择Pipfile.lock ,将其应用到本地并进行测试和修复。

如果有人想要部署,可以使用上次成功构建的Pipfile.lock

模式:冰河世纪

当更改依赖项是真正的问题时,CI 将创建Pipfile.lock一次并保持使用一段时间(一个月?)。

这使得 CI 设置更加困难,因为必须至少有两个不同的作业(一个生成Pipfile.lock ,另一个应用它并在测试中使用)。

警告: Pipfile.lock目前也必须更新, setup.py更改依赖项。

请注意,冰河时代需要 Scrat 松鼠类型的测试,它会忽略冻结状态并检查未固定版本。

闭幕致辞

正如所见,确定性和复杂性逐个增加。

我的建议是:

  • 开始简单(“跑,福雷斯特,跑”)。 您获得效率和速度。
  • 如果由于依赖关系的变化而变得过于复杂,请转到“生成并密封”。 您在本地环境中获得可重复性。
  • 如果情况真的很糟糕,请转到“冰河世纪”模式。 你获得了(暂时的)决定论。

所有的收益都要付出一些代价。

如果这里的目标是更新文档中的建议,那么老实说,说一些与“默认情况下遵循最佳实践(可重现的构建),直到您别无选择”截然不同的东西是不负责任的。

@vlcinsky在标题“模式:生成和密封”下,提到最后成功的Pipfile.lock应该始终保留,例如通过将其声明为 Jenkins 工件,这可能是有意义的。 有了这个变化,建议大多数项目使用这种设置就可以了。 像@tsiq-oliverc 一样,我永远不会推荐第一种模式。

我想得越多,我就越觉得这个文档将成为一个部分,说明为什么使用pipenv进行 CI 构建是一个好主意,即使您正在开发一个库。

@tsiq-oliverc 绝大多数通用 python 包都处于“运行、福雷斯特、运行”模式。 我通过引入toxpytest帮助了这些软件包中的一些,因为我觉得这将有助于给定的软件包质量,并且因为我有很清楚的想法,如何才能做得好。

现在有另一个很棒的工具,我想知道如何在一般 python 项目中正确使用pipenv来提高它的质量。 我想找到一两个行之有效的食谱,这些食谱是合理且易于遵循的。

我会对 Flask 项目说些什么?

  1. 默认情况下遵循最佳实践(可重现的构建),直到您别无选择?
  2. 添加 9 个Pipfile.lock文件并设置更新策略?
  3. 重构 Travis 和 Appveyor 的 CI 脚本以按照冰河时代模式以两阶段方式工作?
  4. 修改 Travis 和 Appveyor 的 CI 脚本以生成Pipfile.lock案例的人工制品,当有人需要在自己的计算机上重现失败的测试时?
  5. 没有评论,除​​了“非常感谢 Flask”。

目标是找到功能性工作风格。 如果它以 doc 结尾,很好,如果不是,没问题。

@vlcinsky我会说 (1) 和 (4) 应该是对此类项目的推荐。 虽然没有预先存在的Pipfile.lock您不会提前知道构建中使用的版本(这在公司环境之外很好),但如果您生成并存档锁定文件,您仍然会获得可重现的结果在构建过程中。

编辑:我建议的 tl;dr 版本是:

  • 无论您是在开发库还是应用程序,始终确保您的构建是可重现的。 pipenv可以帮助您实现这一目标。
  • 如果您正在开发应用程序,请将Pipfile.lock提交到您的存储库并将其用于部署。 (这已经包含在现有文档中。)
  • 如果您正在开发开源库,请在 CI 构建中即时生成Pipfile.lock并将其存档以备后用。
  • 如果您正在开发一个库并在限制性的公司环境中工作,请维护适当数量的锁定文件并在您的 CI 构建中使用这些文件。

(当然,实际的文档应该有更多的细节和例子。)

@ Moritz90我按照您的建议修改了“生成和密封”。

回复(1):说起来容易,没有更具体的就不可能执行。

回复(4):是的,我也认为,“生成并密封”是最可行的模式。 但在 Flask 的情况下,我不敢(至少目前不是)。

在企业环境中重新存在Pipfile.lock :它必须以某种方式(半)手动或自动创建。 我想,在企业环境中,您不会直接从公共 pypi 安装,而是使用一些私有的,并且只提供经过批准的软件包( devpi-server在这方面提供了很好的服务 - 多个索引,已发布软件包的受控波动性,外部批准包等)如果构建Pipfile.lock在这种环境中运行,它只能使用批准的内容,因此如果新版本出现在那里,则必须有人站出来使其批准。 遵循 CI 构建将测试它不会破坏事物。 并且用pipenv check测试安全问题也可能是自动化的。

我想这样的工作流程会比有人(半)手动创建它更安全。 但是我对企业环境的了解非常有限。

你好pipenv团队。 我确实分享了本文中的很多内容,它可以帮助任何开发人员在开发库时更好地理解 Pipfile/pipenv 的局限性。 我确实希望在官方 pipenv 文档中看到此文本或此文本的一部分。

我确实有以下修正案要讨论:

对于我们的内部 python 包,完全可重用,发布在我们的内部 pypi 等上,甚至对于我自己的 python 包(例如: cfgtreetxrwlockpipenv-to-requirements ),我使用一些可能已经知道甚至使用的包,它抽象了这些细节并使python开发人员的生活更轻松:PBR。
PBR 基本上读取在分发包的根文件夹中找到的requirements.txt并将其注入install_requiressetup.py 。 开发人员只需要维护一个带有松散依赖声明的requirements.txt 。 在Pipfile的支持正式集成到 PBR 中之前,我必须使用pipenv-to-requirementsPipfile自动生成requirements.txt Pipfile以便它们既同步又提交在源代码中,并且 PBR 在构建分发包后正确地进行注入。 我认为可以使用pipenv来生成这个requirements.txt

我致力于为 PBR提供 Pipfile 支持,以便它能够读取Pipfile (而不是锁定文件)并将其注入install_requires就像使用requirements.txt

我不知道是否存在其他类似的包,因为它还有其他人们可能不想要的东西(来自 git 历史的版本,AUTHORS 和 ChangLog 的自动生成)。

但最后,我真的觉得编写、维护和处理 Python 库的版本控制非常容易,我会很遗憾不分享这种经验。 我将其作为在我公司编写现代 Python 库的“推荐”方式进行宣传。

我确实认为这就像在关于库和 pipenv 的所有困难上“作弊”,但最终工作已经完成,开发人员很高兴到目前为止使用它。 我为公司的新 Python 开发人员提供的部分 Python 培训包括,首先编写一个手动维护install_requires的 Python 库,然后切换到PBR以查看它如何变得更容易(坦率地说,我是 pbr 的语义提交功能的粉丝,可以自动创建正确的 semver 版本标签)。

使用专用文件也为库​​声明库依赖项的部分原因是能够使用诸如 readthedocs 或 pyup 之类的工具(即使 pyup 在链接到应用程序时更有意义)。

我不一定要把这种方法宣传为做python包的“标准”方式,它实际上是“OpenStack”方式,但我会分享我的经验,如果其他人有相似或矛盾的经验,我会很高兴听到他们并更新我的观点。

团队,您如何看待文档中的“社区”部分? 让像我这样的用户可以分享他如何使用 pipenv 的经验,而不必得到 pipenv 团队的完全认可?

PS:如果你不想污染这个线程,我可以把它移到一个专门的问题上

@vlcinsky (1) 很容易执行——把你的锁文件放在你的仓库中。

我认为你的意思是:一旦这个基本策略不再足够,就不可能给出具体的建议。 这当然是正确的,但那是因为具体问题可能因具体情况而异。

或者换句话说,解决方案取决于您希望 CI 工作流提供哪些额外的保证。

@gsemet你知道吗? 我在过去两年中创建的所有 python 包都基于pbr - 这真的很棒。 我会尽可能地关注你在 pbr 中支持 Pipfile 的尝试(一些竖起大拇指,投票等)。

在这个问题的情况下(搜索pipenv模式和通用 python 库的反模式)我故意省略了pbr有两个原因:

  • 它会使概念讨论更加复杂
  • 有些人不喜欢pbr出于其他原因(你提到了他们),这可能会偏离讨论

另一方面,我真的很期待你为 pbr 爱好者准备的食谱。 我会读的。

@tsiq-oliverc 你一针见血:把你的锁文件放在你的仓库里

这正是促使我开始这个问题的问题。 如果你重读这个问题的开头,你会发现一些案例的描述,其中添加Pipfile.lock可以破坏你的 CI 测试(破坏构建运行或隐藏问题,否则会被检测到,或者安装错误的依赖项对于给定的上下文......)。

如果你给我看一个 repo,这是正确完成的(通用 python 库),我会很高兴。 或者我会证明,存在哪些风险或未完成的事情。

凉爽的 ! 我也维护这个 cookiecutter :)

@vlcinsky是的,所以让我们列举一下具体问题并为它们找到解决方案😄(我不知道有什么高质量的库使用了 Pipenv,但这主要是因为我没有看过。)

据我所知,这些是您原始帖子中的具体症状:

  • 隐藏损坏的 setup.py 依赖项。 这听起来不是问题 - pipenv install -e . ,对吧?
  • 对于不同的 Python 版本或在其他操作系统中,依赖项可能无效。 我可以看到这可能是一个问题,但是您能否提供一个具体的例子来说明这在实践中的重要性? (在锁定文件的上下文中)
  • 开发人员被迫更新......当其他库更新并且可能在库中可用时。 他们没有被迫这样做。 如果他们想保证他们的库适用于版本n+1而不是其依赖项的版本n ,他们就会这样做。 但请注意,我已经提出了一种可提供两全其美的替代方案。
  • 与 Tox 竞争。 我对 Tox 一无所知。 但是,是的,同时使用两个工具来管理您的依赖项听起来像是灾难的秘诀。 我会说使用哪个更适合该特定任务。
  • Pipenv 失败。 这听起来像是另一个非问题 - 你可以只固定 Pipenv 的版本(这是我当前的解决方案,就像我固定我的 Docker 镜像、我的 Pip 版本等)

@tsiq-oliverc 我不得不说,您的评论启发了我,我知道它们有助于提高建议解决方案的可重复性。

以下与您将锁定文件 ( Pipfile.lock ) 放入 repo 以确保可重复性的建议有关:

重新隐藏损坏的 setup.py 依赖项。 . pipenv install -e .遵循我的建议,但请注意,这不是Pipfile.lock用法,它是(重新)创建它的方法。 如果有人在安装包之前保留Pipenv.lock并使用它来创建 virtualenv,则问题存在。

re依赖项可能对不同的 python 版本或在另一个操作系统中无效。 示例很多:为 Python 2.7 安装的doit必须是旧版本,因为较新的版本已放弃对 Python 2.x 的支持。 watchdog依赖需要依赖平台的库:Linux 上的 inotify,Windows 上的其他东西,OSX 上的其他东西。 我以前的客户曾经说“这永远不会发生”,并且在 50% 的情况下,它会在 2 周内发生。 这不是 CI 脚本的最佳实践。

开发人员被迫更新..想象一下有 15 个贡献者的开源库。 新手或疲倦的核心开发人员很容易忘记重新生成Pipfile.lock 。 例如,在maya包中,当新的依赖项添加到setup.py我被要求重新生成Pipfile.lock setup.py 。 那有必要吗? 我是否正确更新了它? 我是否为所有支持的执行上下文更新了它? 答案是不,不确定,不。 无论如何,感谢您的建议(它启发了我在您的评论旁边描述的解决方案)。

与 tox 竞争:Tox 允许创建多个 virtualenv 并在其中运行测试的自动化。 典型的tox.ini为 python 2.7、3.4、3.5、3.6 和您需要的任何其他版本定义了不同的虚拟环境,并允许在那里安装包并运行测试套件。 它是认真的测试人员建立的动力工具。 pipenv不是用于此目的的工具,但可能会干扰安装所需的东西。 在某种程度上,我遵循了您的建议,并建议在可能的情况下使用超过pipenv高级工具 (tox)。

重新Pipenv 失败。 这真的很不幸。 我有 CI 测试(基于 tox)在本地主机上运行良好,但是当通过 Travis 运行时,由于pipenv问题而失败。 如果我现在想使用它,在发布修复程序之前固定没有帮助。 但事情就是这样——我会等。

请注意,我的原始帖子的某些部分似乎必须进行更新,在 CI 脚本中使用pipenv有其合理的位置(“密封” virtualenv 配置以供以后使用)。

@tsiq-oliverc 虽然我最初喜欢你对“已知好的”和最新版本进行测试的建议,但我越想越难证明这种努力的合理性。 我认为你应该决定做一个或另一个,而不是两者。

您唯一获得的是,您将立即知道失败是由依赖项更新还是代码更改引起的。 但是,您可以通过简单地进行单独提交(手动更新锁定的依赖项时)或尝试使用成功构建生成的最新锁定文件(始终使用最新版本时)来重现错误来实现相同的目的。 在受限环境中,无论如何你不能“只是更新”......

@vlcinsky虽然我同意您关于环境之间差异的一般观点,但“每个配置一个锁文件”的论点对我来说听起来像是一个稻草人。 实际上,您将能够在至少一些环境之间共享锁定文件。

剩下的一个没有人回答的悬而未决的问题是如何处理你们都需要在不同环境中进行测试并锁定依赖项的情况。 我不得不承认,除了它存在之外,我对tox一无所知,但似乎在toxpipenv之间需要某种胶水以某种方式解决了这个问题。

@莫里茨90

遇见稻草人

关于Pipfile.lock作为稻草人的太多变体(为了让其他人远离我的领域):

烧瓶

我拿了flask项目(认为它非常成熟)并运行毒物测试:

在这里,您会看到测试的变体列表(仅在 Linux 本地,将其乘以 3,因为 Windows 和 OSX 将执行相同的测试集,但可能会导致不同的环境)。

在一个操作系统上有 16 个不同的测试运行,其中 5 个失败了,因为我没有安装它们(这很好),一个正在处理构建文档(它需要不重要的库)和另一个覆盖(这也需要可导入的库) ):

  coverage-report: commands succeeded
  docs-html: commands succeeded
  py27-devel: commands succeeded
  py27-lowest: commands succeeded
  py27-simplejson: commands succeeded
  py27: commands succeeded
  py35: commands succeeded
  py36-devel: commands succeeded
  py36-lowest: commands succeeded
  py36-simplejson: commands succeeded
  py36: commands succeeded
ERROR:   py34: InterpreterNotFound: python3.4
ERROR:   pypy-devel: InterpreterNotFound: pypy
ERROR:   pypy-lowest: InterpreterNotFound: pypy
ERROR:   pypy-simplejson: InterpreterNotFound: pypy
ERROR:   pypy: InterpreterNotFound: pypy

对于每个创建的 virtualenvs,我都通过pip freeze > {venv_name}.txt创建了requirements.txt文件

然后计算文件的哈希值,根据哈希值排序,因此所有相同的都将被分组。 稻草人来了:

b231a4cc8f30e3fd1ca0bfb0397c4918f5ab5ec3e56575c15920809705eb815e  py35.txt
b231a4cc8f30e3fd1ca0bfb0397c4918f5ab5ec3e56575c15920809705eb815e  py36.txt
cdf69aa2a87ffd0291ea65265a7714cc8c417805d613701af7b22c8ff2b5c0e4  py27-devel.txt
dfe27df6451f10a825f4a82dfe5bd58bd91c7e515240e1b102ffe46b4c358cdf  py36-simplejson.txt
e48cd24ea944fc9d8472d989ef0094bf42eb55cc28d7b59ee00ddcbee66ea69f  py36-lowest.txt
f8c745d16a20390873d146ccb50cf5689deb01aad6d157b77be203b407e6195d  py36-devel.txt
053e107ac856bc8845a1c8095aff6737dfb5d7718b081432f7a67f2125dc87ef  docs-html.txt
45b90aa0885182b883b16cb61091f754b2d889036c94eae0f49953aa6435ece5  py27-simplejson.txt
48bd0f6e66a6374a56b9c306e1c14217d224f9d42490328076993ebf490d61b5  coverage-report.txt
564580dad87c793c207a7cc6692554133e21a65fd4dd6fc964e5f819f9ab249c  py27.txt
8b8ff4633af0897652630903ba7155feee543a823e09ced63a14959b653a7340  py27-lowest.txt

可怕的,不是吗? 在所有测试中,只有两个共享相同的冻结依赖项。

这是具有良好测试套件的通用 python 库的现实。 您现在可能会承认,这与在企业环境中测试的 python 库完全不同。

Jinja2

检查jinja2 ,这似乎是更简单的野兽:

  coverage-report: commands succeeded
  py26: commands succeeded
  py27: commands succeeded
  py33: commands succeeded
  py35: commands succeeded
  py36: commands succeeded
ERROR:   docs-html: commands failed
ERROR:   py34: InterpreterNotFound: python3.4
ERROR:   pypy: InterpreterNotFound: pypy

看到校验和我很惊讶,py27.txt 和 py26.txt 不同:

047a880804009107999888a3198f319e5bbba2fa461b74cfdfdc81384499864e  py26.txt
047a880804009107999888a3198f319e5bbba2fa461b74cfdfdc81384499864e  py33.txt
047a880804009107999888a3198f319e5bbba2fa461b74cfdfdc81384499864e  py35.txt
047a880804009107999888a3198f319e5bbba2fa461b74cfdfdc81384499864e  py36.txt
48bd0f6e66a6374a56b9c306e1c14217d224f9d42490328076993ebf490d61b5  coverage-report.txt
743ad9e4b59d19e97284e9a5be7839e39e5c46f0b9653c39ef8ca89c7b0bc417  py27.txt

@vlcinsky这确实很可怕。 我想知道 Flask 是否是一个特例,或者这是否实际上是常态,但你肯定已经证明我错了。

我现在希望我们的 Python 库有一天不会遇到同样的问题,并且差异会更易于管理。

@Moritz90您的内部库服务于完全不同的受众,因此您可以负担更窄的执行上下文。

通用的python 库通常是灵活和可配置的,例如Flask 允许安装替代的json 解析器并使用单独测试运行所涵盖的内容。

人们可以从 Flask 的tox.ini中学到很多关于测试和毒性的知识

最低的测试变体注意针对最旧的依赖版本进行测试。

devel 正在针对核心依赖项的开发版本进行测试。

我想说的是,Flask 的复杂性更高,并且展示了仔细的测试套件。

金字塔的 tox.ini显示了相似数量的环境(他们的目标也是 100% 的代码覆盖率)。

maya 的 tox.ini非常新鲜(2 天)和简单,即使这里有 4 种不同的环境,py27 与 py35 和 py36 的冻结要求也不同。

@莫里茨90
关于pipenv和tox之间的胶水

  • pipenv --man显示了一些说明,如何在tox.ini命令中使用pipenv
  • tox-pipenv试图提供一些额外的集成,但目前让我感到困惑。

tox.ini文件允许运行任意命令,因此这包括pipenv

pipenv有很棒的功能,当在已经激活的 virtualenv 中运行时(在基于 tox 的测试中是什么情况),它会安装到给定的虚拟环境中。 这真的很好。

由于我们可能需要生成Pipfile.lock ,因此必须做一些额外的努力来获取它并移动到适当的位置(ag 到.tox/py36/Pipfile.lock以防止以下测试覆盖。这应该是可能的,但要进行一些简化会很受欢迎。也许使用环境变量的一些技巧来定位Pipfile会使其更简单。

@vlcinsky

  • setup.py - 仍然不确定我是否理解问题。 您运行pipenv install -e .一次,以便 setup.py 现在通过您的锁定文件进行跟踪。 然后每当您向 setup.py 添加新包时运行pipenv install
  • 开发人员忘记更新锁文件- pipenv --deploy旨在捕捉这一点。 在您的 CI 中运行它!
  • Pipenv 失败- 同意,如果工具中有错误,那就太糟糕了。 但是错误通常会得到修复。 这不是扔掉整个哲学的理由😞
  • 毒物

    • 如果 Tox 适合管理测试,那就太好了。 如果它也非常适合管理包和确定性构建,那就更好了。

    • 但如果这是真的,Pipenv 就没有存在的理由了。 所以我只能假设 Tox 存在某种限制。

    • 与上述类似,当今世界的状况(缺乏良好的互操作性)听起来并不是拒绝哲学的理由。

  • 多个环境

    • 很明显,这里至少有一些特殊情况,比如 Flask。

    • 我没有更好的建议,即多个锁文件(尽管在这方面 Pipenv 可能有未来的功能?)

    • 但即使在这种情况下,我仍然不相信管理多个锁文件在实践中是一个问题。 最坏的情况是,您似乎可以在本地创建一个简单的脚本update-all-lockfiles.sh ,然后在 CI 上运行pipenv --deploy以捕获错误。


@ Moritz90 - 同意,在大多数情况下,“两阶段”方法可能是矫枉过正。 特别是,如果您有意/有意地对锁文件进行“手动”更新,则完全没有必要。


更一般地说,最好确保这个“建议”专注于真正困难的问题(在我看来,这是(A)服务多个环境,(B)想要捕捉上游依赖项的变化)。 它不应该基于暂时性的东西(Pipenv 中的错误)或对工具的使用方式的潜在误解。

但即使对于那些“困难”的问题,框架也应该是“在一些复杂的边缘情况下,你可能会发现一个基本的 Pipenv 工作流程是不够的,所以这里有一些事情需要考虑”。 IMO,不应将其视为默认方法(因为大多数人不会有这些担忧)。

如果 Pipenv/Pipfile 允许处理lib-dependenciesapp-dependenciesdev-dependencies ,那么@vlcinsky提供的文档示例将变得更简单、更容易混淆。 文档可能如下所示:

如果您打包的是共享库,请使用lib-dependencies选项。 示例Pipfile

[lib-dependencies]
some-lib=="*"
another-lib=="*"
yet-another-one==">=1.0"

[dev-dependencies]
some-dev-tool=="1.1"

对于共享库,重要的是将版本范围保持在[lib-dependencies]尽可能宽,以防止使用者系统上的版本冲突。

如果你的包是一个需要精确依赖版本的应用程序(打算由 pipenv 安装在目标系统上),你应该使用[app-dependencies]选项。 示例Pipfile

[app-dependencies]
some-lib=="1.0.12"
another-lib=="1.*"
yet-another-one=="2.0"

[dev-dependencies]
some-dev-tool=="1.1"

/结束文档示例

另一种方法可能是Pipfile.libPipfile.app

我认为这样的事情将省略对大量反模式部分和第三方工具来填补空白的需要。

如果人们期望它创建 python 库或深入参与它们的创建,那么调用 pipenv 打包工具是一种误导。

我认为这是一个真正的问题,会导致很多混乱。 尤其是那些习惯于用其他编程语言(例如 JS、Rust、Elm)打包管理器的人。 我花了几个月的时间,偶尔阅读 GIthub 的问题,直到我意识到我以错误的方式使用 Pipenv 和 setup.py。

@feluxe

您的[lib-dependencies]Pipfile.lib就是我们今天在Pipfile (作为抽象依赖项 - 尽可能宽)。

您的[app-dependencies]Pipfile.app是我们在Pipfile.lock (作为特定依赖项)。

pipenv及其文件可用于两种不同的情况 - 开发库或准备应用程序部署,但可能不能同时用于两者。 出于这个原因,我没有看到在Pipenv添加额外部分的充分理由。 开发人员有责任了解Pipfile将用于何种用途。

我认为这是一个真正的问题,会导致很多混乱。 尤其是那些习惯于用其他编程语言(例如 JS、Rust、Elm)打包管理器的人。 我花了几个月的时间,偶尔阅读 GIthub 的问题,直到我意识到我以错误的方式使用 Pipenv 和 setup.py。

同意。 三段式解决方案也是我从未考虑过的一个非常有趣的解决方案,它似乎是正确的并且(令人惊讶!)简单。

我自己来自 Python 背景,我一直觉得 Node 的 package.json 做错了(Rust 更好,因为它有编译器和链接器,并且可以在稍后阶段解决这个问题)。 以同样的方式处理 app 和 lib 依赖项对于像 Python 这样的脚本语言根本不起作用,至少在抽象意义上是这样——也就是说,它可能对你有用,但是像 Pipenv 这样的通用工具不能这样做,因为它需要通用的。

虽然我确实喜欢概念上的三部分解决方案,但它仍然是与现有生态系统相当不兼容的变化。 已经有 setup.py、setup.cfg 和(可能)pyproject.toml 填充了这个空间。 如果 Pipenv(准确的说是 Pipfile)想要进军这个领域,就需要整合相关的项目,比如 pip(理想情况下应该直接支持库支持)和flit

正如我在其他关于 lib/app 依赖处理的问题中提到的,这个讨论需要升级到 pypa-dev(邮件列表)和/或 PEP 流程,以便在 Pipenv 之前让其他各方和相关人员更好地听到(Pipfile) 可以向任何方向移动。

@vlcinsky

您的 [lib-dependencies] 或 Pipfile.lib 就是我们今天在 Pipfile 中所拥有的(作为抽象依赖项 - 尽可能广泛)。

对不起,如果这不清楚。 我的lib-dependencies是人们目前放入setup.py / install_requires 。 也许pypi-dependencies对我的意思来说是一个更好的名字。

@uranusjr

已经有 setup.py、setup.cfg 和(可能)pyproject.toml 填充了这个空间。

Pipenv(命令行工具)可以接口setup.py 。 只是setup.py的依赖部分必须移动到 Pipfile。 至少在我的想象中:)

正如我在其他关于 lib/app 依赖处理的问题中提到的,这个讨论需要升级到 pypa-dev(邮件列表)和/或 PEP 流程,以便在 Pipenv 之前让其他各方和相关人员更好地听到(Pipfile) 可以向任何方向移动。

好的,抱歉打扰了 ;) 如果我找到一些时间,我会为邮件列表写一些东西。

然而,在这个提案的范围内,我建议它专注于当前可能的最佳实践,而不是为整个 Python 打包社区制定新的工作流程。 在当前限制范围内提出最佳实践会更有成效,然后开始讨论改进。

@uranusjr - 我来自“编译”背景,所以我很好奇为什么会这样?

以相同的方式处理 app 和 lib 依赖项对于像 Python 这样的脚本语言根本不起作用

@tsiq-oliverc 由于应用程序的最佳实践要求您固定依赖项,如果库使用相同的需求文件源,它们也会开始固定它们的依赖项。 这将导致依赖项解析的问题。

假设我的应用程序有两个依赖项 A 和 B,它们都依赖于 C,但是 A 引脚 v1,而 B 引脚 v2。 编译语言允许工具链在编译时检测到这一点,并以多种方式解决它。 例如,Rust 在链接期间执行此操作——最终的可执行文件将包含 C 的两个副本(v1 和 v2),A 和 B 链接到每个副本。 在 C++ 领域,这将通过动态库解决; 符号查找甚至在稍后(在运行时)完成,但想法是一样的——编译器知道你需要什么(从你使用的接口),并可以相应地采取行动。

脚本语言无法做到这一点,因为它在真正到达调用之前不知道您真正想要做什么。 Node 通过始终假设依赖项不兼容来解决这个问题(A 和 B 总是得到自己的 C,即使两个副本相同),但这会导致一类新的问题,并导致像对等依赖项这样的尴尬黑客攻击,每个人(我希望?)同意是可怕的。 Python 可能不想去那里(无论如何它不能去那里,因为这可能会破坏所有现有的 Python 安装)。

解决此问题的另一种方法是在“取消固定”依赖项版本的打包工具中做一些聪明的事情。 Bundler(Ruby 的)就是这样做的,通过建议人们不要将锁定文件包含在 gem 中,因此 Bundler 可以使用 Gemfile 中未固定的版本,而不是 Gemfile.lock 中的固定版本。 但是人们往往会忽略建议并为所欲为,因此您仍然会随处可见固定版本。

我可能有点太强势了,不能说它

@tsiq-oliverc Pieter Hintjens 在某处写了一个概念“欢迎以拉取请求的形式发表评论”

我喜欢这样,因为它将重点从哲学建议转移到真正有形和实际的事情上。 而且它还限制了评论的数量,因为评论者经常在实际使用中发现该想法不完整或以某种方式被破坏的方式。

我问你一个 python 库的例子,其中pipenv被正确使用(或至少使用),而你没有提供任何。

你评论了tox品质,但承认你不熟悉它,仍在重复 Python 包开发领域的最佳实践。

你说Flask可能是特例。 所以我使用“library”这个词在 Github 上搜索了 Python 项目,根据 fork 的数量进行排序(因为它可能反映了有多少人在用它做一些开发),忽略所有“内容的精选列表”并计算一个环境的数量操作系统(通常是 Linux):

运行测试的实际环境数量大多是 2 (+Windows) 或 3 (+OSX) 倍。

tox用于 3 个项目中的 2 个项目(我不会将它与 Travis 或 Appveyor 进行比较,因为它们在旁边进行了另一级别的测试)。

要测试的环境数量相当多,Flask 绝对不是最狂野的。

要为其定义固定依赖项的环境数量确实无法手动管理。

简单地将Pipfile.lock放入存储库是相当容易的,但它并没有神奇的改进(如果是,请向我展示真实场景,何时会改善情况)。

也许您知道“编译”世界的黄金法则,并认为确定性(或可重复性)对于 Python 也是必须的。 如您所见,确实有许多 Python 项目没有它就可以很好地生活,因此黄金法则可能在这里并不严格适用。

如果我们发现将pipenv用于 python 库,这将改善情况,我会很高兴。 我想防止使用,这会损害整体质量。

为了达到这个目标,我的方法是迭代问题:

  • 我要不要使用这个工具?
  • 如何?
  • 为什么,我得到了什么价值?
  • 它是否引入了一些问题? (额外的工作,错误被隐藏......)

@feluxe

对不起,如果这不清楚。 我的 lib 依赖项是人们当前放入 setup.py / install_requires 的内容。 也许 pypi-dependencies 更适合我的意思。

请参阅此问题中的pbr讨论。 这是 Pipfile 支持库依赖的努力。

我认为,一个Pipfile不应用于两个目的(lib 和 app),这些事情应分开进行。 如果你觉得真的需要它,你能描述一下使用它的项目的目的吗? 我通常会尽量将库开发和部署项目分开,因为它们在时间上的用法完全不同。

@vlcinsky我不确定你想把它带到哪里(我不确定你要求什么样的公关!),所以我现在要退出这次谈话。

重申我的立场的 TL;DR:

  1. 确定性构建是非常可取的,在软件行业的其他地方很常见,并且使用 Pipenv 很容易实现。
  2. 肯定有一些边缘情况,有人应该在那里推动最先进的技术(通过变通方法或通过更好的工具)。
  3. Pipenv 文档仅仅因为 (2) 影响一小部分案例而全面反对 (1) 是不负责任的。

@uranusjr 明白了。 虽然我不认为这里有任何特定于语言的东西,只是不同的社区已经解决了不同的启发式方法来处理没有通用解决方案的问题——如果你有版本冲突,你就有问题。

Maven/Java(例如)迫使您在构建时考虑它。 NPM 方式意味着如果不匹配的版本跨接口,您将遇到运行时问题。 运行时解析(例如 Python、动态库)意味着依赖项可能会崩溃等。 如果依赖版本不是它所期望的。

@vlcinsky

请参阅本期的 pbr 讨论。 这是 Pipfile 支持库依赖的努力。

pbr 看起来不错,但它属于我试图解决的类别:

我认为这样的事情将省略对大量反模式部分和第三方工具来填补空白的需要。

我认为这些工具一开始就不应该是必要的。

如果你觉得真的需要它,你能描述一下使用它的项目的目的吗? 我通常会尽量将库开发和部署项目分开,因为它们在时间上的用法完全不同。

说到 pypi 包,我最终使用 Pipenv 来处理开发依赖, Pipfile来描述开发依赖, setup.py使用install_requiressetuptools来描述 lib 依赖setup.py中发布运行pipenv run python setup.py bdist_wheel upload 。 这就是我认为复杂的地方。

在其他现代语言中,我必须学习一种命令行工具(包管理器)和一种依赖文件格式。 文档集中在一个地方,更容易遵循,新人将在几个小时内解决所有这些问题。 这是npm initnpm install foo --devnpm publish 。 Pipenv/Pipfile 已经可以做大部分了,如果它可以做所有的事情,像这样的问题就不会存在了。

我再次呼吁为此讨论建立一种“社区”部分/维基。 有几个“模式”可以是合法的,我们中的一些人可能想分享它“做 python 库的方式”,有些人像我一样使用 pbr,而其他人可能有一个非常好的模式。 但是pipenv文件里面的一个页面,不确定是否是一个好主意。

PS:要准备迁移到新的 pypi,您应该使用 twine 而不是 python setup.py 上传。 使用“上传”应该被视为一种反模式。

也许 pipenv 可以增加“发布”命令?

@feluxe您可能想看看诗歌。 我只是偶然发现它,它似乎是你正在寻找的。

它做了pipenv所做的事情,而且似乎他们做得更好,尤其是在依赖管理方面(至少他们假装是这样)。 它通过一个工具poetry完成依赖管理、打包和发布。

我想知道pipenvpoetry是否可以共同努力最终为 Python 提供一个真正的包管理器。

在这个讨论走得太远之前,我想再次重申自己。 Pipenv 不能简单地增加publish命令,或者做任何试图接管打包任务的事情。 这只会使生态系统更加分裂,因为不是每个人都这样做,而且应用程序和库依赖项在理论上是不同的,一旦在他们的工作流程中做出区分,你就不能告诉某人将它们合并在一起。

似乎几乎每个人都参与了这次合并,但事实是有更多的人没有加入这个讨论,因为事情对他们有用,他们正在做其他事情。 我反复说过:关于改进工具链和文件格式设计的讨论应该在 Python 打包层次结构中更高的某个地方进行,因此它会更多地接触到设计 Pipenv 所依赖的更基本事物的人。 请在那里进行讨论。 在这里建议没有用,因为 Pipenv 没有位置改变它。

我已经反复说过:关于改进工具链和文件格式设计的讨论应该在 Python 打包层次结构中更高的某个地方进行,因此它可以让人们更多地接触到设计 Pipenv 所依赖的更基本事物的人。

我同意关于这个 bug 的讨论随着打包和发布的出现而失控(这个 bug 只与依赖管理有关!),但是你能指出我们在正确的地方进行这个讨论吗? 人们在这里拥有它是因为 pipenv 被视为朝着正确方向迈出的急需的一步,而不是因为他们想对 pipenv 维护者施加额外的责任。

编辑:对不起,我一定是错过了你第一次阅读新评论时所做的那篇文章。

然而,在这个提案的范围内,我建议它专注于当前可能的最佳实践,而不是为整个 Python 打包社区制定新的工作流程。 在当前限制范围内提出最佳实践会更有成效,然后开始讨论改进。

我非常同意这一点。 在我们提出大计划之前,我们应该首先弄清楚图书馆维护者现在最好的工作流程是什么。 因此,让我们再次关注这一点,就像我们在本主题开头所做的那样。 我认为我们还没有得出结论。

回到主题:引用@uranusjr的关于为什么应该在不同的库文件中定义依赖项的帖子:

解决此问题的另一种方法是在“取消固定”依赖项版本的打包工具中做一些聪明的事情。 Bundler(Ruby 的)就是这样做的,通过建议人们不要将锁定文件包含在 gem 中,因此 Bundler 可以使用 Gemfile 中未固定的版本,而不是 Gemfile.lock 中的固定版本。 但是人们往往会忽略建议并为所欲为,因此您仍然会随处可见固定版本。

我可能有点太强了,不能说它根本行不通。 但至少之前的所有尝试都失败了

我仍然不明白为什么现在官方推荐的库不能使用pipenv用于他们的 CI 构建,而是将Pipfile.lock放在源代码控制之外。 正如一些人指出的那样, pipenv目前与打包过程没有任何关系,因此我们不应该遇到您上面概述的问题。

而且我也不明白为什么这是反对在应用程序用来定义其抽象依赖项的同一文件中定义抽象依赖项的论据。 如果pipenv不想实现将Pipfilesetup.py集成的精心设计的解决方案,那也没关系,但我不明白为什么这通常是一个坏主意。

@vlcinsky

我认为,一个 Pipfile 不应用于两个目的(lib 和 app),这些事情应分开进行。

看我上面的帖子。 你能详细说明你为什么这么认为吗? 原则上我根本看不到任何缺点。 现在,包含Pipfile可能是一个坏主意,因为您必须在两个不同的文件中以相同的方式定义依赖项,但我还没有看到任何解释原因的论据通常将Pipfile用于依赖项声明是一个坏主意。

请注意,我已经同意Pipfile.lock不应该在库的源代码管理中,除非您处于与我相同的情况。

编辑:另外,如果事实证明pipenv本身实际上需要了解差异,您可以在开始引入app-dependencieslib-dependencies之前引入类似货物的crate-type字段的内容lib-dependencies - 这听起来过于复杂。

@Moritz90 Python 的几个邮件列表将是举行此讨论的好场所。

pypa-dev是围绕 Python 打包及其周围生态系统进行讨论的最明确的工具。 如果我要发布类似的讨论,我可能会从这里开始。

python-ideas是一个讨论

@tsiq-奥利弗

PR 我的意思是:展示一个例子来证明你的概念是可行的。

所以选择一些现有的库,分叉它,应用你的 (1) - 你说pipenv应该很容易并告诉我。 我非常努力地尝试并且遇到了困难。

如果您的 (2) 表示“其他人必须做这项工作”,那么您的 PR 将不存在。

在(3)中,您谈论“案例的小子集”而没有给出任何实数。 我描述的所有关于虚拟环境数量的顶级库都被视为“小子集”吗?

为了结束这次讨论,我对讨论中发现的内容进行了简短总结。

重点: pipenv (反)python 库和应用程序的模式

我稍微改变了重点:它不仅谈论(一般)python 库,还谈论应用程序,因为包含它相当便宜,并且很好地展示了差异。

我特意排除了任何提议更改现有工具的内容,例如pipenvtox等。

pipenv是什么,不是什么

  • 它是部署工具,允许通过Pipfile.lock定义和应用具体的依赖项。
  • 它是 virtualenv 管理工具。
  • 它不是生成 python 包意义上的打包工具。

库和应用程序

(python 软件)产品要么准备好在另一个产品(因此是库)中使用,要么是准备好运行的最终应用程序。

我个人认为,即使“企业库”也属于库类别(适用相同的规则,只是执行上下文的数量较少)。

软件产品类型

  • 库:用于其他产品(库或应用程序)
  • 应用程序:要部署和运行

安装方法:

  • library: pipenv install <package>因此“让包发挥作用(解析周围其他库的版本)”
  • 应用程序: pipenv sync因此“应用具体的依赖项”

抽象依赖和具体依赖

软件产品依赖

  • 抽象依赖:必须命名使用的库,可能会限制版本或使用,但必须保持足够的灵活性(不应固定版本)
  • 具体依赖项:必须固定版本,最好使用所用库的哈希值

    pipenv文物:

  • Pipfile : 抽象依赖

  • Pipfile.lock :具体(锁定)依赖项”

执行上下文

不同执行上下文的典型数量

  • 图书馆:

    • 一个操作系统上的 python virtualenvs:3 到 9(龙卷风使用 30)

    • 操作系统数量:1 到 3(Linux、OSX、Windows)

    • 总数:3 到 18

  • 应用:

    • 一个操作系统上的 python virtualenvs:1

    • 操作系统数量:1

    • 总数:1(或很少)

CI 目标、优先级和确定性

CI目标

  • 图书馆:

    • 代码包括它的抽象依赖项允许在执行上下文的所有预期变体中安装和预期功能。

    • 当 (private/public) pypi 获取依赖库更新时,如果它影响测试的函数库安装,则失败。

  • 应用:

    • 安装后(使用具体/固定依赖项),所有预期功能都在一个预定义的执行上下文中提供

关于功能的特定 CI 目标:

  • 图书馆:

    • 库声明的抽象依赖项是完整的并且包括所有必要的限制(仅在需要的地方):库正确安装自身

    • 所有预期的用例都能正常运行

  • 应用:

    • 具体的依赖是完整的,所有的都被固定,最好包括。 哈希:应用程序安装正确

    • 所有预期的用例都能正常运行

不同的 CI 测试模式

  • 模式:“奔跑,福雷斯特,奔跑”

    • 现在绝大多数 python 库都以这种方式进行了测试。

    • 使用tox或类似的测试软件

    • 不使用pipenv和具体的依赖项(对于库来说可以)

  • 模式:“生成并密封”

    • 存储库中没有Pipfile

    • Pipfile.lockpipenv install -e .

    • Pipfile.lock记录(密封)环境并允许以后复制 virtualenv 以分析问题。

  • 模式:“冰河世纪”

    • 两阶段测试

      • 当抽象依赖(在setup.py install_requires )更改或更新 pypi 上的依赖包时,通过pipenv install -e .重新生成Pipfile.lock pipenv install -e .

    • 功能测试运行:在库代码改变时运行。 在pipenv sync Pipfile.lock创建的 virtualenv 中运行

      Pipfile.lock如何以及由谁创建

  • 由开发人员手动(可能适用于应用程序)

  • 由 CI 构建自动(并通过所有测试,声明它已验证人工制品)

确定性与灵活性的优先级

  • 库:灵活性(尽可能运行)
  • 应用程序:确定性(在选定的执行上下文中以完全相同的方式运行)

    什么会影响已安装产品的确定性:

  • public pypi(低确定性,包随时更新)

  • private pypi(更高的确定性,可以控制包更新)
  • 库中的抽象需求(不得用于确定性)
  • 具体要求( Pipfile.lock ):完全确定性

各种各样的

Pipfile.lock一些用例:

  • (antipattern) 定义库抽象依赖(因为它必须是抽象的)
  • (反模式)为测试库设置 virtualenv(可能隐藏损坏的库抽象依赖项)
  • 记录运行测试的确切 virtualenv(“密封”)(因此允许开发人员稍后重新创建它以进行损坏的测试并在其中进行实验)
  • 为测试应用程序设置 virtualenv
  • 将应用程序部署到生产中

    其他提示

  • pbr库允许通过requirements.txt定义抽象库依赖项。 更新阅读Pipfile正在进行中。

  • poetry包尝试类似于pyenv

    常见问题

  • “将锁定文件放入 repo”,您将获得确定性的构建:

    • 应为应用程序工作。
    • 不适用于库,因为确实有很多执行上下文,并且每个上下文都有很大的潜力产生不同的Pipfile.lock 。 说真的: flask显示了它的 11 个不同的虚拟环境(在一个操作系统上)10 个不同的锁定依赖项。 谁来创建和提交它们?
    • 请注意,使用“生成并密封”CI 模式,您仍然可以获得Pipfile.lock (但由 CI 脚本生成)允许在其他地方重新生成 virtualenv。
  • Pipfile.lock在库中

    • 如果用于创建 virtualenv,它可以在setup.py隐藏库依赖项的损坏定义。

  • Pipfile在库存储库中

    • 如果它重复抽象依赖项(将在setup.py ),它可能会隐藏损坏的setup.py依赖项声明。

    • 推荐:通过pipenv install -e .pipenv install -e .[tests]生成Pipfile如果您还需要测试依赖项,并且它们在setup.py中声明为“测试”附加项

  • pipenv install <something>到 CI 脚本中

    • 它本身并没有太大改善决定论

    • 请参阅“生成并密封”CI 模式(然后所有安装到 virtualenv 都必须通过pipenv )。

结论

Python 库(尤其是通用库)表现出出乎意料的大量执行上下文。 原因是,图书馆的目标是在不同条件下证明灵活性。 灵活性似乎比确定性构建更重要。 对于来自“编译”世界的人来说,这可能感觉像是非常糟糕的反模式。 事实是,大多数(可能是所有)python 库不提供确定性构建(如果您知道一些,请告诉我)并且 Python 仍然做得很好。 Python 应用程序还活着的原因可能是:python 作为脚本语言与编译世界不同。 另一个原因可能是,一旦应用程序(从一组库构建)应解决确定性的(自然和合理的)要求,就可以(应该)稍后解决确定性问题。

对于应用程序,情况正好相反,在这里使用pipenv等工具很容易实现确定性。

接下来做什么?

  • 在接下来的一两周内,我没有时间处理这个问题。
  • 我可以想象在某处(不确定在哪里)创建一系列博客条目。 如果您知道这个地方(理想情况下允许进行一些讨论),那么让内容被引用、讨论并最终可能(如果它幸存下来)放在某个地方是很自然的:-)。
  • 我建议@uranusjr接管这个问题的控制权(关闭它,决​​定下一步做什么,将人们重定向到其他地方或任何看起来可行的东西)

感谢大家的非常鼓舞人心的讨论——我觉得“我完全迷失在这个话题中”的消息重构了三遍——这意味着我们自然变得更好了。

@vlcinsky poetrypyenv无关。 它很像pipenv (但在库和应用程序的管理方面有更好的实现,IMO)但是有打包和发布部分。

您有一个pyproject.toml文件,它定义了您的项目及其依赖项(抽象依赖项)和一个pyproject.lock ,它描述了固定的依赖项,并为任何 Python 版本和平台固定了pyproject.toml文件已指定,以便只有一个确定性锁定文件,以避免pipenv面临的问题。 只有在安装时, poetry才会通过检查环境来检查要安装的软件包。

当它打包您的库时,它将使用抽象依赖项(而不是固定依赖项),因此您在分发包时保持灵活性(例如通过 PyPI)。

这样做的好处是它将使用库的抽象依赖项和应用程序的锁定文件。 这是两全其美的。

不使用固定依赖项的@zface诗歌实际上

本质上,我们在弹性上花费了大量时间和精力,而那些提出崇高要求的小项目不必花费太多精力,因为人们没有遇到边缘情况。 如果您真的相信另一种工具为您提供了世界上最好的工具,那么我鼓励您使用它——pipenv 本身不会在短期内为您处理包装,如果有的话。

@techalchemy我不卖任何东西,真的,我只是针对可以在pipenv使用的想法。

poetry确实在pyproject.lock poetry中固定依赖项,就像pipenvPipfile.lock所做的那样。 因此,您可以像pipenv提供的那样进行复制。 如果你有一个锁定文件,它将被使用并安装固定依赖,如果我没记错的话,它也是pipenv所做的。

它使用抽象依赖项的唯一时间是它打包项目以进行分发(因此基本上用于库),因为在这种情况下您不需要固定依赖项。

@vlcinsky还有几点需要整理、更正或扩展,但我仍然非常热衷于将其转化为文档形式、Pipenv 或其他形式。 您是否有兴趣发送拉取请求? 我很乐意帮助充实这篇文章。

关于诗歌,我个人不是一个整体的粉丝,但它确实做了很多正确的事情。 它可能不应该在 Pipenv 文档中提及,因为它违反了 Pipenv 开发人员想要推动人们走向的一些最佳实践,但是如果讨论是在 pypa-dev 或类似的地方进行的,则应该提及它,以提供有关如何打包的完整图片生态系统目前是。

诗歌也可以用更多的关注和贡献。 这对社区来说是最好的,包括 Pipenv。 有了可行的选择,人们可以权衡他们的选择,而不是先进入 Pipenv 并抱怨它没有按照他们的期望去做。 库之间的良好竞争还可以促进依赖解析方面的技术改进,Pipenv 和诗歌都这样做(但都不是完美的)。 我们可以互相学习很多东西。

@uranusjr是的,我认为很少有事情得到澄清,值得与更广泛的受众分享。 非常欢迎您的帮助。

“成对文件起草”怎么样? 我认为目前只在两个人的小范围内进行工作是最有效的。

认为要做的是(可能进行一两次迭代):

  • 我们可以在哪里发布
  • 识别文档项目(文章、章节)
  • 明确每个项目的范围和目标
  • 同意大纲
  • 确定未解决的问题
  • 解决它们
  • 写文档
  • 发布(并希望它被接受)

如果你想自己写(基于讨论的内容)并让我作为审稿人,我不会抱怨。

我将通过电子邮件与您联系以商定下一步行动。

@vlcinsky如果您更喜欢实时交互,我也可以在PySlackers (一个 Slack 工作区)上以@uranusjr身份使用。 Pipenv 在那里有一个频道 ( #pipenv )。

@uranusjr这就是我所说的努力的意思。 Python 迫切需要一个像货物一样好的包管理器。 由于缺乏标准的做事方式,Python 生态系统与其他语言相比显得苍白无力。 我认为pipenv对此无济于事。

令我困扰的是, pipenv标榜自己为the officially recommended Python packaging tool而实际上它不是打包工具,远非如此,这对用户具有误导性。 它只是一个依赖管理器加上一个 virtualenv 管理器。

另外,你说它的灵感来自于货物、npm、纱线,它们是打包工具以及依赖管理器,而管道则不是。

这是pipenv的缺陷,它只是把水搅浑了,因为人们仍然会犯与以前相同的错误requirements.txt vs setup.py 。 因此,项目仍然会被错误地打包,并在其setup.py定义错误的依赖项。 这就是像货物这样的项目做得对的:他们处理开发项目/应用程序的所有方面以确保一致性,而像pipenv这样的项目则不然。

当你说:

Pipenv 和诗歌都可以(而且都不完美)

你的意思是? 从我所见,他们的依赖管理器比pipenv提供的依赖管理器更有弹性。 唯一的缺点是他们使用 PyPI JSON API,由于发布的包不好,有时没有依赖信息。

无论如何,我认为,就像你说的,这两个项目可以相互学习。

而且,还有一件事,如果最终 pip 处理Pipfile ,那么 pipenv 的未来是什么? 它只是一个 virtualenv 管理器吗?

如果诗歌依赖管理器依赖于 json api,那么它不仅有时会因为“发布的包不好”而出错,而且它实际上可以正确解析的内容将非常有限。 即使您正在处理旧版本,仓库 json api 也会发布 _most recent_ 依赖项,并且如果它完全具有该信息。 我们曾经也整合了 json api,它很棒,因为它很快,但基础设施团队告诉我们不要相信它。 如果它依赖于不可靠的来源,那么将其称为弹性似乎有点不诚实。

最终的挑战在于实际构建一个执行安装文件的依赖关系图,因为目前,这就是打包的工作方式。 没有办法解决它。 即使对于相同的包,在我的机器上解析的依赖关系图可能与在您的机器上解析的依赖关系图不同。

很容易挥手并说“如果 pip 可以读取 pipfile,那不就是让 pipenv 成为 virtualenv 管理器吗?” 不。 Pipenv 是一个依赖管理器。 它管理幂等环境并生成可重现的锁文件。 我意识到这对您来说似乎微不足道,因为您正在挥舞它并将此工具简化为 virtualenv 管理器,但事实并非如此。 我们解析锁文件并包含您没有、未使用的 Python 版本的标记,并保持可用,以便您可以跨平台和 Python 版本精确部署和重现。 我们使用多种解析方法,包括处理本地轮子和文件、vcs 存储库(我们也在那里解析图)远程工件、pypi 包、私有索引等。

在一天结束时 pip _will_ 处理 pipfiles,这就是计划,自创建格式以来一直是计划。 但这与询问“但是 pip 何时可以处理需求文件呢?”是一样的。 问题基本相同。 Pip 可以安装该格式。 除了我们还安装文件(顺便说一下,使用 pip)之外,它与我描述的任何功能都没有真正相关。

@技术化学

即使您正在处理旧版本,仓库 json api 也会发布最新的依赖项,并且如果它完全具有该信息

这完全是错误的,您可以通过调用https://pypi.org/pypi/{project}/{release}/json来获取特定版本的依赖项。 如果您只是调用https://pypi.org/pypi/{project}/json确保您只会获得最后一个依赖项,但您实际上可以获得正确的依赖项集。

并且python项目的打包/发布部分确实需要改进,因为最终它将使每个人受益,因为它将使可靠地使用JSON API成为可能。

它管理幂等环境并生成可重现的锁文件。
我们解析锁文件并包含您没有、未使用的 Python 版本的标记,并保持可用,以便您可以跨平台和 Python 版本精确部署和重现。

poetry 。 并且您可以使其不使用 JSON API 来提供与pipenv相同的解析方法(使用 pip-tools)。 参见https://github.com/sdispater/poetry/issues/37#issuecomment -379071989,它仍然比pipenv (https://github.com/sdispater/poetry#dependency-resolution) 更有弹性)

@zface我会说最后一次,请将它带到层次结构中更高的位置。 Pipenv 并未自称是官方推荐的 Python 打包工具; 它说,因为它是。 如果您觉得不合适,请告诉推荐 Pipenv 的官员。 请不要把这些东西放在 Pipenv dev 上。 这是投诉的错误地方,您不可能在这里获得投诉的解决方案。 您还可以获得关于您在那里遇到的技术问题的更好答案。 这是 Pipenv 的问题跟踪器,而不是 Python 打包工具以及 Python 打包是如何完成的讨论板。

Pipenv 不仅仅依赖于 pip 工具来解决问题,请停止将我们的软件简化为一个表现出缺乏理解的衬垫。 我非常了解 PyPI api 的工作原理,我直接与实现它的团队进行了交谈。

这完全是错误的,

这种态度在这里是不受欢迎的。 不要假设我们不明白我们在说什么。 请练习礼貌。

它仍然比 pipenv 更有弹性(https://github.com/sdispater/poetry#dependency-resolution)

Pipenv 目前不会展平依赖图。 指出一个特定的问题,一棵树被夷为平地,并声称整个工具因此更好,更有弹性是愚蠢的,你一遍又一遍地证明你只是在这里侮辱pipenv和促进诗歌。 请在路上,这种行为是不受欢迎的。

我同意讨论偏离主题,即试图利用围绕 pipenv 的“良好实践”。

然而,

[...] 使用requirements.txt 和setup.py 时仍然会犯和以前一样的错误。 因此,项目仍然会被严重打包,并且在它们的 setup.py 中定义了错误的依赖项。

我同意这个观点,让新开发人员成功打包他们自己的 Python 代码实际上很复杂,太复杂了,需要阅读大量在线文档。
但这并不完全取决于 pipenv 或任何其他包依赖项来处理。 我们无法改写历史。 作为一个社区,我们需要找到一种方法来逐步实现 Python 工具链的现代化。

pipenv(可能还有诗歌)是向前迈出的非常好的一步。

必须在一侧为应用程序维护Pipfile ,在另一侧为库维护setup.py ,这是不费吹灰之力的。 无论我们用大量的文字、长篇的文章和良好的实践指南来解释多么困难,它都太复杂了。 我完全同意目前是这样,但它不应该阻止我们想象更好、更安全的方式。
最后,作为一名开发人员,我想要一个单一的工具,也许有两种不同的模式,来帮助我并让我的生活尽可能轻松。

它们应该是一种从诸如 PBR 之类的库中提取requirements.txt/Pipfile的部分的方法,以提出一种“easy setup.py”,一个 Pipfile-aware 包装器围绕install_requires ,没有pbr 带来的所有不需要的行为,并将其打包在一个专用的 setuptools 包装器中,它只会这样做。

因此,我们将能够拥有更好的每个世界:

  • pipenv 维护Pipfile (在库和应用程序中都进行了版本控制)
  • pipenv 维护Pipfile.lock (仅适用于应用程序的版本化)
  • 一个人会使用这个神奇的包装包( pipfile_setuptoolsinstall_requires_pipfile ?),这将是第一级依赖,该工作只是将Pipfile注入install_requires

这是另一个与pipenv无关的项目,但仍然需要一个通用的Pipfile解析器库。 你怎么认为?

@gsemet根据我的理解,PyPA 一直试图用 pyproject.toml 来填充它,由flit领导。 在继续使用 Pipfile 作为源格式之前,您需要先(在 pypa-dev 或 distutils-sig)与他们讨论这个问题。 至于解析 Pipfile(和锁定文件),那是在pypa/pipfile (Pipenv 供应商提供核心解析逻辑)中处理的。


编辑:如果您决定在任一邮件列表中就此开始讨论,请给我留言。 我确实有一些想法,我们可以如何将 Python 打包分发的两个部分结合在一起。

我必须承认,看到pyproject.toml声明的依赖项(它采用 PBR 完成的setup.cfg角色),我有点难过,而 PyPa 也支持Pipfile ....

感谢您提供指向 flit 和 pipfile 的指针。 还有 Kennethreitz 的pipenvlib看起来更轻。

PBR 的 setup.cfg 似乎比官方文档更完整(例如: data_files )并且重用已经与多个工具(flake8,pytest,...)共享的文件可以使用相同的文件,减少数量python项目根目录下的文件)

此页面是否有帮助?
0 / 5 - 0 等级