Watchman:监视Python库生态系统的依赖项冲突

2020-09-21 23:47:21

PyPI存储库中有超过140万个Python库。找出哪种组合能很好地结合在一起并不总是那么容易。事实上,我们有一个短语来描述现代项目似乎在积累的依赖关系的迷宫:“依赖地狱”。咬您的通常不是项目的直接依赖项,而是依赖项的依赖项,一直到可传递的闭包。在今天来自ICSE‘20的论文中,Wang等人。研究Python项目中依赖项冲突的流行情况及其原因。在证明(并不是说我们中的许多人需要很大的说服力)依赖关系冲突是一个真正的问题之后,他们构建并评估了Watchman,这是一种用于快速查找和报告依赖关系冲突并预测正在形成的依赖关系冲突的工具。它看起来也很有用。

如果您有一组一起工作的版本化依赖项,则可以创建锁定文件来固定这些版本。但是,如果您还没有达到那个满意的境地,或者您需要升级、添加或删除依赖项,那么您将只能任由pip或POLITE来找到一个有效的解决方案。本文是基于PIP.。

当项目声明对库的依赖项时,它可以指定特定版本或版本的范围约束。当然,这些依赖项也有它们自己的依赖项,以此类推。在Python生态系统中,最常见的是使用范围约束指定依赖项:

我们根据下游项目的数量调查了PyPI上最受欢迎的1000个Python项目。我们发现,这些项目中92.2%的直接依赖关系被限制在一定的版本范围内。相比之下,Maven按照同样的调查方法管理的Java项目,这一比例仅为0.03%。

当项目及其依赖项的所有约束不能同时满足时,理论上就会出现依赖项冲突问题。例如,想一想经典的菱形图案,其中A依赖于B和C,而B和C又都依赖于D,但是没有D的版本同时满足B和C的约束。

实际上,依赖冲突的出现比这更频繁,这是因为pip使用了急切的依赖固定:";当下载一个库时,pip安装程序总是选择PypI中满足库的版本约束的最新版本。";在这里做了一点背景阅读后,我发现pip的依赖解析器将在v20.3中得到升级,但是从简短的描述来看,这听起来可能会在短期内导致更多的依赖冲突问题!(ChangeLog-Dependency解析中的更多细节正在变得更加严格,一旦您意识到pip当前允许什么,这似乎是一件好事)。

从Python开发人员在项目中使用库的角度来看,作者确定了三个主要问题:

为Python项目安装的库的版本可能会随着时间的推移而变化(无需更改您自己的项目中的任何内容)。锁定文件可以帮助您做到这一点。

当库更新其对其他库的版本约束时,库的下游使用者可能会受到影响。

使用版本约束信息很难全面了解项目的(可传递的)依赖关系。

为了在实践中研究依赖关系冲突问题,作者在124个流行的Python项目中创建了一个包含235个依赖关系冲突问题的数据集。这些问题都有一个问题报告,其中包含根本原因的描述,以及问题的后续修复,或者在问题报告本身中记录了关于如何修复问题的明确共识。

这些问题中有89.8%(211)是由远程依赖项引起的(其余的是与本地安装的依赖项冲突,本地安装的依赖项可以通过创建隔离环境来修复)。其中,三分之二是由直接依赖和传递依赖之间的冲突引起的,其余三分之一是由传递依赖之间的冲突引起的。

1/4的远程依赖问题(59/235)是由过于具体的约束引起的(例如,需要精确的版本)。另有1/4(67/235)是在依赖项超出某个范围的上限时发生的。

对于直接依赖项和传递依赖项之间的冲突,最常用的策略(如果可用)是调整直接依赖项的版本约束,使其与传递依赖项的版本约束相兼容。传递依赖项之间的冲突有时也可以通过升级或降级直接依赖项来解决。

与上游依赖关系的开发人员协调,通常是为了放松他们的约束,以便找到解决方案。

消除直接依赖,仅仅依靠暂时性地获得它(听起来像是为将来积累麻烦!)

添加直接依赖项以固定版本,即使它不是项目的真正直接依赖项(另一种代码气味!)。

一个依赖项冲突问题可以有多个修复。问题的表现形式、项目依赖图的拓扑结构、PIP的安装规则以及上下游项目版本约束的冲突都会影响解决方案。

进入守望者!Watchman爬行PyPI以收集所有包的依赖元数据,然后在发布新包和更新现有包时使此信息保持最新。Watchman根据这些元数据,按照pip的安装规则,为每个库的每个版本构建一个完整的依赖关系图。

然后,可以使用这些图表来检测现有的依赖项冲突问题,并预测何时可能出现依赖项冲突问题。

要查找现有的冲突问题,Watchman会在具有多条传入边的库的完整依赖关系图中查找节点。首先遍历的边(根据PIP算法)固定版本号。如果这与为其他边指定的约束不兼容,则会发生冲突。

Watchman还警告开发人员一个库,因为它的预测可能很快会导致下游项目的依赖冲突问题。它针对两种类型的潜在问题执行此操作:

该库将其直接依赖项之一限制为特定版本,并且有多个下游项目同时依赖于该库及其固定的依赖项。

依赖项的已安装版本由未指定上限的边固定,并且所选版本接近同一结点的其他传入边的上限

作者通过回放PyPI上所有库从2017年1月1日到2019年6月30日的演变历史,对Watchman进行了反向测试。然后,可以测试在每个阶段检测到的冲突,以查看它们是否确实在稍后得到解决,并且可以测试预测的冲突,以查看它们稍后是否成为真正的冲突。Watchman检测到的所有冲突确实都是由开发人员解决的,从问题引入到解决平均需要26天。在Watchman预测可能发生的156起冲突中,有143起发生了!

从2019年7月11日起,Watchman用于在线监测PyPI,检测并预测了截至8月16日期间的189个进一步的依赖冲突问题。经过过滤,其中117个报告给了开发人员,其中63个被开发人员确认为真正的问题。其余54个问题仍然悬而未决,主要是因为相关项目的维护工作不积极。

评估结果表明,Watchman能够有效、高精度地检测出依赖冲突问题,并提供有用的诊断信息来帮助开发人员解决问题。在未来,我们计划进一步提高Watchman的检测能力,并将我们的技术推广到其他Python库生态系统,如蟒蛇,使其能够被更多的开发人员社区访问。

看起来Watchman是一项非常有用的服务,它可以警告图书馆开发人员潜在的未来冲突,并让他们更快地通知实际的冲突-而不需要等待社区中的某个人先被冲突绊倒!

您可以在项目站点上找到有关Watchman和它发现的问题的更多信息:https://www.watchman-pypi.com.。如果你有图书馆的话,你甚至可以把它指向你自己的图书馆!

我们以前看过“高风险的小世界”,它提供了一些关于NPM生态系统的有趣见解,尽管是通过安全镜头,以及Conflictjs,它明确地查看了相互冲突的JavaScript库。今天的白皮书中的相关工作部分也包含了一些Java和Android生态系统的有用看点:

Wang等人。查看了Java项目中的依赖项冲突,并构建了一个名为里德尔的工具来帮助公开和理解它们。