【问题标题】:Use >= or ~= for compatibilty across systems?使用 >= 或 ~= 以获得跨系统的兼容性?
【发布时间】:2022-06-29 00:41:55
【问题描述】:

我的目标是导出我的venv 的简单而正确的方法。在最佳情况下,生成的 requirements.txt 适用于所有兼容系统。

目前我使用pip freeze > requirements.txt。 这使用==“版本匹配子句”。在其他系统上,该文件可能由于版本冲突而无法工作,尽管它是兼容的。

PEP 440 中还有一个~=“兼容子句”。但是,我在 pip freeze docs 中找不到该选项。使用“查找和替换”或 awk 之类的工具将 == 替换为 ~= 可以正常工作。

我的幼稚结论是~= 将是在requirements.txt 中使用的理想子句。然而,当我查看流行的包时,他们经常使用>= 来指定版本。例如。在urllib3

~= 有没有我看不到的缺点?
如果不是这样: 为什么 >= 在这么多包中使用?

编辑:
Pigar 有一个 option 可以原生使用 >= 并且可以与 freeze here 进行比较。显然,他们也不使用 ~=.
但是,我仍然不确定要使用哪一个,因为 >= 可能会在主要版本更改时中断。此外,较低次要版本的软件包将被标记为不兼容,尽管它们应该是兼容的。

【问题讨论】:

    标签: python pip requirements.txt


    【解决方案1】:

    您的问题并不容易回答,并且涉及到围绕版本控制的社交动态中的一些细微差别。

    简单的东西首先:有时版本使用终端后缀来表示类似预发布版本的东西,如果您依赖预发布版本或其他一些您希望终端后缀重复迭代的情况(尤其是在无序的情况下)方式),~= 通过让您接受构建中的所有迭代来帮助您。 PEP 440 包含一个很好的例子:

    ~= 2.2.post3
    >= 2.2.post3, == 2.*
    

    其次,pip freeze 并不是用来生成需求列表的。它只是转储您当前拥有的所有内容的列表。所以它只使用== 是有道理的:例如,它的目的是让您导出一组包以在其他地方生成相同的环境。


    接下来是硬的东西。在semantic versioning 下,唯一向后不兼容的修订应该是主要修订。 (这取决于您对维护者的信任程度——稍后会详细介绍。)但是,如果指定补丁号,~= 不会升级到新的次要版本,即使是可用,原则上它应该是向后兼容的。清楚地谈论这一点很重要,因为“兼容版本”有两种不同的含义:在语义版本控制中,“兼容版本”是(通俗地)这个版本和下一个主要版本之间的任何版本;在需求文件中,“兼容版本”是修补相同终端版本的修订版。

    现在让我澄清一下:当我说“向后兼容”时,我的意思只是第一种(语义版本控制)意义上的。 (如果有问题的包没有使用语义版本控制,或者有第四个版本号,那么~= 通常仍会匹配所有补丁,但请检查以确保。)

    因此,>=~= 之间需要进行交易,这与依赖管理中的信任链有关。以下是三个原则 - 然后,我将提供一些关于为什么这么多包维护者使用 >= 的猜测。

    1. 一般而言,包维护者有责任确保与其 requirements.txt 匹配的所有版本号都与该包兼容,但不推荐使用的补丁版本偶尔例外。这包括确保 requirements.txt 尽可能小并且只包含该包的要求。 (更广泛地说,“尽可能少地要求并尽可能多地验证它。”)

    2. 一般来说,无论是什么语言,无论是什么包,依赖关系都反映了一个信任链。我正在实施一个包;我相信你会以一种可以继续运行的方式来维护你的包(及其需求文件)。您信任您的 依赖项以继续运行的方式维护他们的 包。反过来,您的下游消费者希望您维护您的包,以使其继续为他们工作。这是基于人类的信任。号码“只是”一种方便的交流工具。

    3. 一般而言,无论更改集如何,包维护人员都非常努力地避免使用主要版本。没有人愿意成为发布主要版本并强迫消费者通过大量重写来对其软件包进行版本控制的人 - 或者将他们的项目委托给旧的且不受支持的版本。我们接受必要的主要转速(这就是我们有系统跟踪它们的原因),但人们通常不愿意使用它们,直到他们真的别无选择。

    综合这三个。从包维护者的角度来看,假设一个人信任自己所依赖的维护者(应该如此),从广义上讲更合理期望重大修订很少,而不是预计小修订会意外地向后不兼容。这意味着您需要在 >= 方案中进行的响应式更新的数量应该很小(但当然,非零)。


    这是很多基础工作。我知道这很长,但这是好的部分:交易。

    例如,假设我开发了一个包,helloworld == 0.7.10。你在helloworld == 0.7.10 上开发了一个包,然后我将helloworld 修订为0.8。让我们首先考虑我可能仍会提供对 0.7.10 版本的支持,并且(例如)稍后将其修补到 0.7.11,即使同时单独维护 0.8。这一点很重要,因为它允许您的下游消费者 接受补丁而不会失去与您的包的兼容性,即使在使用~= 时也是如此。而且,您“保证”未来的补丁不会破坏您当前的实现或在出现错误时需要维护 - 我正在为您完成这项工作。当然,这只有在我费心维护 0.7 和 0.8 的情况下才有效,但这似乎确实有利...

    那么,它为什么会坏掉?嗯,举个例子。如果你在你的包中指定helloworld ~= 0.7.10,但是你的另一个上游依赖项(那不是我!)升级,现在使用helloworld >= 0.8.1,会发生什么?由于依赖于次要版本的兼容性要求,因此现在发生了冲突。更糟糕的是,如果 your 包的消费者想要使用来自 helloworld == 0.8.1 的新功能,而这些功能在 0.7 中不可用?他们不能。

    但请记住,基于 helloworld v0.7 构建的符合 semver 的包应该可以在 helloworld v0.8 上正常运行 - 不应该有重大更改。 你的规范 ~= 最有可能无缘无故地破坏依赖关系或消费者需求 - 而不是 helloworld

    如果您使用helloworld >= 0.7.10,那么您将允许安装 0.8,即使您的包没有明确使用它编写。如果 0.8 没有破坏您的实现,这应该是正确的,那么无论如何允许它的使用将是正确的手动决定。您甚至不必知道我在做什么或我是如何编写 0.8 的,因为次要版本应该只是添加功能 - 您显然没有使用的功能,但其他人可能想要。

    但是,信任链是有漏洞的。作为 helloworld 的维护者,我可能不确定确定我的 0.8 版本是否引入了错误或潜在问题,这些问题可能会干扰最初为 0.7 编写的包的使用。当然,通过将其命名为 0.8 而不是 1.0,我正在(并且应该期望)根据需要为 helloworld 提供补丁,以解决保持向后兼容性的故障。但在实践中,这可能会变得站不住脚,或者根本不会发生,尤其是在包没有严格的单元和回归测试的非常不寻常的情况下。

    因此,作为包维护者,您的交易归结为:您是否相信我,helloworld 的维护者,不经常发布主要版本,并确保次要版本不会有倒退的风险-兼容性,超过您需要保证下游消费者的稳定版本?


    使用 >= 表示:

    • (罕见):如果我发布了主要版本,您需要更新您的需求文件以指定您所指的主要版本。
    • (不常见):如果我发布了一个次要版本,但错误、审查、回归失败等导致该次要版本破坏了在旧版本之上构建的软件包,您需要更新您的需求文件以指定哪个minor rev 你指的是,或等我进一步修补它。 (如果我拒绝进一步修补它,或者更糟糕的是,花点时间修补它怎么办?)

    使用 ~= 表示:

    • 如果您的任何上游软件包最终使用的次要版本与您的软件包最初构建时使用的版本不同,则您和上游提供商之间存在依赖冲突的风险。
    • 如果您的任何下游消费者想要或需要使用您所依赖的软件包的以后次要修订版中引入的功能,他们不能 - 除非覆盖您的需求文件并希望最好的。
    • 如果我停止支持您使用的软件包的次要版本,并且仅在未来的次要版本中发布关键补丁,您和您的消费者将不会获得它们。 (如果这些很重要,例如安全更新怎么办?urllib3 就是一个很好的例子。)

    如果这些“罕见”或“不常见”事件对您的项目造成如此大的破坏,以至于您只是无法想象一个您愿意承担风险的世界,请使用 @987654345 @,即使以您的下游消费者的便利/安全为代价。但是,如果您想为下游消费者提供尽可能多的灵活性,不介意处理偶尔的重大更改事件,并希望确保您自己的代码通常可以在最新版本上运行,使用 >= 是更安全的出行方式。

    出于这个原因,我希望大多数 维护者在大多数 的时间里故意使用>=。或者也许我只是读得太多了。

    【讨论】:

      猜你喜欢
      • 2017-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-30
      • 1970-01-01
      • 2019-07-20
      相关资源
      最近更新 更多