【问题标题】:Should I pin my Python dependencies versions?我应该固定我的 Python 依赖项版本吗?
【发布时间】:2015-04-15 01:52:41
【问题描述】:

我即将发布一个我过去几周一直在研究的 Python 库。我已经阅读了很多关于 Python 依赖项的内容,但还不太清楚:

有些人假装你不应该从不固定你的依赖版本,因为它会阻止你的库的用户升级这些依赖。

其他一些声称您应该始终固定您的依赖项版本,因为这是保证您的版本按照开发时的方式工作并防止在依赖项会对您的库造成严重破坏。

我不知何故选择了一个混合解决方案,我假设我的依赖项使用了semantic versioning ,并且只固定了主要版本号(比如somelib >= 2.3.0, < 3),除非主要版本号是0(语义版本控制规定这样的版本被认为是不稳定的,即使只是补丁号被碰撞也可能会破坏 API)。

到目前为止,我不确定哪种方式最好。是否有官方指南(甚至可能是 PEP?)规定了有关 Python 依赖项的最佳实践以及如何指定它们?

【问题讨论】:

    标签: python dependencies pip versioning semantic-versioning


    【解决方案1】:

    您应该始终固定您的依赖项,因为它增加了安全、可重复构建的可能性,即使随着时间的推移。固定版本是您作为包维护者的声明,您已验证您的代码在给定环境中工作。这有一个很好的副作用,可以保持您的理智,因为您不会被错误报告所淹没,在这些错误报告中,您必须对每个包的依赖关系和系统细节进行检查。

    用户始终可以选择忽略固定的依赖版本,并自行承担风险。但是,当您发布新版本的库时,您应该更新依赖项版本以进行改进和错误修复。

    PEP 426 about Semantic dependencies(Python 软件包的元数据)部分指出:

    “依赖性管理严重依赖于PEP 440(PEP 440 - 版本识别和依赖性规范)中定义的版本标识和规范方案。”

    据此,我推断权威的“最佳实践”是对您的依赖项进行版本控制,因为 PEP 与打包的关系被称为“高度依赖”相关 PEP 概述的版本控制细节。

    【讨论】:

    • 你在最后一段的推论是不正确的。引用的意思是,要使依赖项管理正常工作,包必须使用符合 PEP 指定格式的语义版本号。这与应指定的紧密或松散的依赖关系没有任何关系。
    • Python Dependency Resolution 的研究指出了一些固定问题,如我的回答中所述。
    • “固定版本是您作为包维护者的声明,您已验证您的代码在给定环境中工作。” -- 没错,但问题是您只能在将依赖项固定到非常特定的版本时进行 one 这样的声明,而实际上其他版本可能同样有效(您甚至可能已经验证了这一点)。
    • 如果您选择固定版本,您应该将它们固定在最终产品中,而不是在库中;根据nvie.com/posts/pin-your-packages
    【解决方案2】:

    固定可能会出现问题并导致安全风险。特别是对于一个库,就像你的情况一样,如果它通常与其他本身具有依赖关系的 PyPI 包结合使用,它可能会导致 更多 依赖关系冲突。

    为什么?对Python Dependency Resolution 的详细研究,在分析了数以万计的 PyPI 包及其当前的依赖冲突率后,讨论了这个问题。它解释说:

    如果发行版没有安装到它自己的、空的、单一用途的环境中,那么如果依赖版本都被固定而不是让范围保持灵活,那么依赖冲突的可能性就会大大增加。

    并指出固定会干扰升级,从而加剧安全问题

    建议:

    如果一个项目固定依赖项,那么它必须准备好在每次有项目直接或间接依赖的任何重要版本时发布一个新版本,一直到依赖链。

    【讨论】:

      【解决方案3】:

      其他两个答案相互矛盾的原因是它们都是正确的(并且值得一读),但它们适用于不同的情况。

      如果你在 PyPI 上发布一个库,你应该声明任何你知道的依赖,但是固定到一个特定的版本。例如,如果您知道您需要>= 1.2,但1.4 已损坏,那么您可以编写类似somepkg >= 1.2, != 1.4 的内容。如果您知道其中一件事是 somepkg 遵循 SemVer,那么您可以添加 < 2

      如果您要构建自己部署的网络应用程序,那么您应该固定所有确切的依赖项,并使用 pyup.io 或 requires.io 等服务在新版本发布时通知您。这样您就可以保持最新状态,同时确保您部署的版本与您测试的版本相同。

      请注意,这两条建议是相辅相成的:事实上,如果应用程序 A 使用库 B,那么 A 的作者或 B 的作者可以固定 B 的依赖项,但不能同时固定两者。所以我们必须选择一个。这里的基本原则是最好尽可能晚地完成,即由 A 的作者完成,他可以看到他们的整个系统;图书馆 B 的工作是传递一些有用的提示来帮助 A 做出这些决定。特别是,如果 A 所依赖的所有库都准确地记录了他们对底层依赖项的了解,那么 A 就有可能在它们重叠时做出明智的决定。就像如果依赖 B 依赖于 requests >= 1.0, != 1.2,而依赖 C 依赖于 requests >= 1.1,那么我们可以猜测 1.1 或 1.3 可能是好的版本。如果依赖 B 依赖于 requests == 1.1 而依赖 C 依赖于 requests == 1.2,那么我们就卡住了。

      【讨论】:

      • 这是一个非常清晰的解释,将其他两个答案放在正确的角度。该建议可能不仅适用于 Python 包,而且适用于任何类型的包管理,例如 semver。很容易成为公认的答案,IMO。
      • 固定你的直接依赖而不是那些依赖的依赖。如果您不信任它,请使用 pip freeze 并享受更新每个 dep 的乐趣。通过对代码的良好测试,我认为除了 requirements.txt 文件中的直接依赖项之外,您还需要维护任何内容。看看 mozillas 基岩项目,它是一个 django 项目。我喜欢他们的做法。
      猜你喜欢
      • 2010-09-18
      • 1970-01-01
      • 2019-01-17
      • 2016-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-24
      • 1970-01-01
      相关资源
      最近更新 更多