使用> =或〜=用于整个系统的兼容?

发布于 2025-01-28 18:22:19 字数 1281 浏览 1 评论 0 原文

我的目标是导出我的 venv 的一种简单而正确的方法。在最佳情况下,由此产生的 unigess.txt 在所有兼容系统上都可以使用。

目前,我使用 pip冻结>需求.txt 。 这使用 == “版本匹配子句”。在另一个系统上,由于版本相互兼容,该文件可能无法正常工作。

pep 440 也有一个〜= “兼容子句”。但是,我找不到pip冻结中的选项 docs 。使用“查找和替换”或 awk 的工具替换==〜= with works可以。

我的天真结论是,〜= 将是在 unignts.txt 中使用的理想子句。但是,当我查看受欢迎的软件包时,他们经常使用> = 来指定版本。例如,在 urllib3

我看不到〜=的缺点吗?
如果不是这样: 为什么> =在这么多软件包中使用?

编辑:
pigar option 使用> =本地,并且有一个比较freeze 在这里。显然,它们也不使用〜=。
但是,我仍然不确定在发生主要版本时会使用哪一个,as> =可能会破裂。尽管应该兼容,但较低的次要版本的软件包也将被标记不兼容。

My goal is a simple and proper way to export my venv. In the optimal case, the resulting requirements.txt works on all compatible systems.

At the moment I use pip freeze > requirements.txt.
This uses the == "Version matching clause". On an other system the file might not work due to conflicting versions, although it was compatible.

In PEP 440 there is also a ~= "Compatible clause". However, I cannot find an option for that in pip freeze docs. Using "find and replace" or a tool like awk to replace == with ~= works okay.

My naive conclusion is that ~= would be the ideal clause to use in requirements.txt. However, when I look at popular packages they often use >= to specify a version. E.g. at urllib3.

Is there a drawback to ~=, which I do not see?
If that is not the case:
Why is >= used in so many packages?

Edit:
Pigar has an option to use >= natively and there is a comparison to freeze here. Apparently, they also do not use ~=.
Yet, I am still not sure which one to use, as >= could break when there is a major version change. Also packages which are a lower minor version would be marked incompatible, although they should be compatible.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦境 2025-02-04 18:22:19

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

首先简单的内容:有时版本使用终端后缀来表示诸如预发行的构建之类的东西,如果您依赖于预发行构建或其他某些其他情况,您期望终端后缀反复迭代(尤其是以一种非按序的方式),则〜= 通过让您接受构建上的所有迭代来帮助您。 pep 440 包含一个很好的例子:

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

第二, pip freeze 并不是要用于生成需求列表。它只是转储您当前拥有的所有内容的列表,这远远超出了需求文件中所需的时间。因此,它只能使用 == :例如,它的目的是让您将一组软件包复制到其他地方的“相同”环境中。


紧随其后的东西。在语义版本,唯一的后退 - 不兼容的修订应该是主要修订版。 (这取决于您信任维护者的程度 - 在其中添加PIN。)但是,如果指定一个补丁编号,则〜= 不会升级到新的未成年人即使有一个可用的修订,它原则上应该向后兼容。要清楚地谈论这一点很重要,因为“兼容发行版”具有两个不同的含义:在语义版本中,“兼容发行版”是(俗称)该版本和下一个主要的Rev之间的任何 Rev;在需求文件中,“兼容版本”是修订版,可以修补相同终端Rev

现在让我清楚:当我说“向后兼容”时,我的意思是在语义版本中。 (如果所讨论的软件包不使用语义版本,或者具有第四个版本编号,那么〜= 仍将与所有补丁匹配,但是请肯定。)

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

  1. 一般而言, 是软件包维护者的责任,以确保所有版本编号匹配其要求。TXT与该软件包兼容,偶尔偶尔的补丁修订版Revs除外。这包括确保supperies.txt尽可能小,并且仅包含该软件包的要求。 (更广泛地,“尽可能少并尽可能验证它。”)


  2. 一般而言,,无论该语言和包装如何,依赖关系都反映了一条信任链​​。我正在实施一个软件包;我相信您可以以继续运行的方式维护软件包(及其需求文件)。您信任您的依赖项以继续运行的方式维护 。反过来,您的下游消费者期望您以某种方式维护 软件包,这意味着它继续为他们起作用。这是基于人类的信任。该数字是“只是”一个方便的通信工具。


  3. 一般而言,无论变更集,包装维护者都非常努力地避免主要版本。没有人愿意成为释放重大转速并迫使消费者通过大量改写版本的人的人,或者将其项目委托给旧版本。我们在必要时接受大型转速(这就是为什么我们有系统可以跟踪它们的原因),但是人们通常不愿意使用它们,直到他们真的没有其他选择为止。

合成这三个。从包装维护者的角度来看,假设一个人信任维护者的依赖(正如一个人应该), 总体上说,更合理的期望重大修订是罕见的,而不是它要期望次要修订是偶然倒退的。这意味着您需要在> = 方案中进行的反应性更新数量应该很小(但是,当然, ,非零)。


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

例如,假设我开发了一个软件包, helloworld == 0.7.10 。您开发了一个软件包 helloworld == 0.7.10 ,然后我后来rev helloworld to 0.8。让我们从考虑最佳情况开始:我仍在为0.7.10版本提供支持,(例如)在以后的日期将其修补为0.7.11,即使在单独维护0.8的同时也将其修补为0.7.11。这允许您的下游消费者即使使用〜= ,也可以在不失去软件包的情况下接受补丁。而且,您可以“保证”将来的补丁不会破坏您当前的实施,也不会在错误的情况下进行维护 - 我正在为做这项工作。当然,这仅在我遇到维持0.7和0.8的麻烦时才有效,但这确实有利...

所以,为什么它会破裂呢?好吧,一个例子。如果您在软件包中指定 helloworld〜= 0.7.10 ,但是 上游依赖性(不是我!)升级,会发生什么,现在使用 helloworld> = 0.8.1 ?由于依赖次要版本的兼容性要求,因此现在存在冲突。更糟糕的是,如果您的的消费者想要使用 helloworld == 0.8.1 中的新功能,该怎么办?他们不能。

但是请记住,在Helloworld v0.7上构建的符合SEMVER的软件包应该可以在Helloworld v0.8上运行 - 不应有破坏更改。 您的 〜= 的规范是最有可能出于没有充分理由破坏依赖性或消费者需求的,而不是 helloworld

​如果0.8没有破坏您的实现,则应该是真实的,那么允许其使用将是正确的手动决定。您甚至不一定需要知道我在做什么或我写的0.8,因为次要版本应该是添加功能 - 您显然不使用的功能,但其他人可能会想要。

不过,信任链泄漏。作为Helloworld的维护者,我可能不知道某些我的修订0.8是否引入了可能干扰最初使用0.7撰写的包装使用的错误或潜在问题。当然,通过将其命名为0.8,而不是1.0,我声称我将(并且应该期望!)根据需要为 helloworld 提供补丁,以解决失败以维持向后兼容。但是实际上,这可能会变得站不住脚,或者根本不会发生,尤其是在包裹没有严格单位和回归测试的非常异常的情况下。

因此,您作为包装开发人员和维护人员的交易归结为:您信任我, helloworld 的维护者,以不经常发布大型转速,并确保Minor Revs不发行冒着向后兼容的风险,超过,您需要您的下游消费者可以保证


使用> = nese :(

  • 稀有):(稀有):如果我释放主要的rev,您' LL需要更新您的需求文件,以指定您参考哪个重大转速。
  • (罕见):如果我发布一个小修订版,但是一个错误,审核,回归失败等。导致该次要修订会破坏在旧版本上构建的包装,则您要么需要更新您的需求文件才能指定哪个您所指的次要修订版,或者等待我进一步修补它。 ,我的美好时光会怎么办?

我拒绝进一步修补它,或更糟糕的是

  • (如果 您可能会与您和上游提供商之间的依赖关系冲突。
  • 如果您的下游消费者想要或需要使用您依赖的软件包中 partim修订中引入的功能,则不会 - 并非没有超越您的需求文件并希望获得最佳。
  • 如果我停止支持您使用的软件包的次要修订,而仅对未来的未来修订版发布关键补丁,那么您和您的消费者将不会得到它们。 (如果这些很重要,例如安全更新怎么办? urllib3 可能是一个很好的例子。)

如果这些“稀有”或“不常见”事件对您的项目如此破坏,那么您可以您想承担这种风险的世界的受孕,即使以便利性/安全为代价,也要使用〜= 。但是,如果您想为下游消费者提供最大的灵活性,请不要介意处理偶尔发生的变化事件,并希望确保您自己的代码通常在最新版本上可以使用> = 是更安全的方法。无论如何,这通常是正确的决定。

因此,我希望大多数维护者故意使用> = 最多的时间。也许这是习惯的力量。也许我只是在阅读太多。

Your question is not simple to answer and touches on some nuances in the social dynamics around versioning.

Easy stuff first: sometimes versions use a terminal suffix to indicate something like prerelease builds, and if you're dependent on a prerelease build or some other situation where you expect the terminal suffix to iterate repeatedly (especially in a non-ordered way), ~= helps you by letting you accept all iterations on a build. PEP 440 contains a good example:

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

Second, pip freeze is not meant to be used to generate a requirements list. It just dumps a list of everything you've currently got, which is far more than actually needs to go in a requirements file. So it makes sense that it would only use ==: for example, it's meant to let you replicate a set of packages to an 'identical' environment elsewhere.


Hard stuff next. Under semantic versioning, the only backwards-incompatible revisions should be major revisions. (This depends on how much you trust the maintainer - put a pin in that.) However, if specifying a patch number, ~= won't upgrade to a new minor rev even if one is available and it should, in principle, be backwards-compatible. This is important to talk about clearly, because "compatible release" has two different meanings: in semantic versioning, a "compatible release" is (colloquially) any rev between this one and the next major rev; in requirements files, a "compatible release" is a revision that patches the same terminal rev only.

Let me be clear now: when I say "backwards-compatible," I mean it in the semantic versioning sense only. (If the package in question doesn’t use semantic versioning, or has a fourth version number, well - generally ~= will still match all patches, but check to be sure.)

So, there's a trade to be made between >= and ~=, and it has to do with chains of trust in dependency management. Here are three principles - then after, I'll offer some speculation on why so many package maintainers use >=.

  1. In general, it's the responsibility of a package maintainer to ensure that all version numbers matching their requirements.txt are compatible with that package, with the occasional exception of deprecated patch revs. This includes ensuring that the requirements.txt is as small as possible and contains only that package's requirements. (More broadly, “require as little as possible and validate it as much as possible.”)

  2. In general, no matter the language and no matter the package, dependencies reflect a chain of trust. I am implementing a package; I trust you to maintain your package (and its requirements file) in a way that continues to function. You are trusting your dependencies to maintain their packages in a way that continues to function. In turn, your downstream consumers are expecting you to maintain your package in a way that means it continues to function for them. This is based on human trust. The number is 'just' a convenient communication tool.

  3. In general, no matter the change set, package maintainers try extremely hard to avoid major versions. No one wants to be the guy who releases a major rev and forces consumers to version their package through a substantial rewrite - or consign their projects to an old and unsupported version. We accept major revs as necessary (that's why we have systems to track them), but folks are typically loath to use them until they really don't have another option.

Synthesize these three. From the perspective of a package maintainer, supposing one trusts the maintainers one is dependent upon (as one should), it is broadly speaking more reasonable to expect major revisions to be rare, than it is to expect minor revisions to be backwards-incompatible by accident. This means the number of reactive updates you'll need to make in the >= scheme should be small (but, of course, nonzero).


That's a lot of groundwork. I know this is long, but this is the good part: the trade.

For example, suppose I developed a package, helloworld == 0.7.10. You developed a package atop helloworld == 0.7.10, and then I later rev helloworld to 0.8. Let's start by considering the best case situation: that I am still offering support for the 0.7.10 version and (ex.) patch it to 0.7.11 at a later date, even while maintaining 0.8 separately. This allows your downstream consumers to accept patches without losing compatibility with your package, even when using ~=. And, you are "guaranteed" that future patches won't break your current implementation or require maintenance in event of mistakes - I’m doing that work for you. Of course, this only works if I go to the trouble of maintaining both 0.7 and 0.8, but this does seem advantageous...

So, why does it break? Well, one example. What happens if you specify helloworld ~= 0.7.10 in your package, but another upstream dependency of yours (that isn't me!) upgrades, and now uses helloworld >= 0.8.1? Since you relied on a minor version's compatibility requirements, there's now a conflict. Worse, what if a consumer of your package wants to use new features from helloworld == 0.8.1 that aren't available in 0.7? They can't.

But remember, a semver-compliant package built on helloworld v0.7 should be just fine running on helloworld v0.8 - there should be no breaking changes. It's your specification of ~= that is the most likely to have broken a dependency or consumer need for no good reason - not helloworld.

If instead you had used helloworld >= 0.7.10, then you would've allowed for the installation of 0.8, even when your package was not explicitly written using it. If 0.8 doesn't break your implementation, which is supposed to be true, then allowing its use would be the correct manual decision anyway. You don't even necessarily need to know what I'm doing or how I'm writing 0.8, because minor versions should only be adding functionality - functionality you're obviously not using, but someone else might want to.

The chain of trust is leaky, though. As the maintainer of helloworld, I might not know for certain whether my revision 0.8 introduces bugs or potential issues that could interfere with the usage of a package originally written for 0.7. Sure, by naming it 0.8 and not 1.0, I claim that I will (and should be expected to!) provide patches to helloworld as needed to address failures to maintain backwards-compatibility. But in practice, that might become untenable, or simply not happen, especially in the very unusual case (joke) where a package does not have rigorous unit and regression tests.

So your trade, as a package developer and maintainer, boils down to this: Do you trust me, the maintainer of helloworld, to infrequently release major revs, and to ensure that minor revs do not risk breaking backwards-compatibility, more than you need your downstream consumers to be guaranteed a stable release?


Using >= means:

  • (Rare): If I release a major rev, you'll need to update your requirements file to specify which major rev you are referring to.
  • (Uncommon): If I release a minor rev, but a bug, review, regression failure, etc. cause that minor rev to break packages built atop old versions, you'll either need to update your requirements file to specify which minor rev you are referring to, or wait for me to patch it further. (What if I decline to patch it further, or worse, take my sweet time doing so?)

Using ~= means:

  • If any of your upstream packages end up using a different minor revision than the one your package was originally built to use, you risk a dependency conflict between you and your upstream providers.
  • If any of your downstream consumers want or need to use features introduced in a later minor revision of a package you depend upon, they can't - not without overriding your requirements file and hoping for the best.
  • If I stop supporting a minor revision of a package you use, and release critical patches on a future minor rev only, you and your consumers won't get them. (What if these are important, ex. security updates? urllib3 could be a great example.)

If those 'rare' or 'uncommon' events are so disruptive to your project that you just can't conceive of a world in which you'd want to take that risk, use ~=, even at the cost of convenience/security to your downstream consumers. But if you want to give downstream consumers the most flexibility possible, don't mind dealing with the occasional breaking-change event, and want to make sure your own code typically runs on the most recent version it can, using >= is the safer way to go. It's usually the right decision, anyway.

For this reason, I expect most maintainers deliberately use >= most of the time. Or maybe it's force of habit. Or maybe I'm just reading too much into it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文