SKIP 3 — 过渡到 scikit-image 1.0#

作者:

Juan Nunez-Iglesias <juan.nunez-iglesias@monash.edu>

状态:

最终

类型:

标准跟踪

创建时间:

2021-07-15

解决时间:

2021-09-13

解决方案:

已拒绝

生效版本:

摘要#

scikit-image 正准备发布 1.0 版本。这可能是一个清理 API 的机会,包括向后不兼容的更改。其中一些更改涉及更改返回值,但不更改函数签名,这通常只能通过添加一个无用的关键字参数(例如 new_return_style=True)来实现,该参数的默认值在几个版本中会发生变化。结果仍然是向后不兼容的更改,但会在更长的时间内进行。

尽管处于 beta 版本和 0.x 系列版本中,scikit-image 的使用非常广泛,任何向后不兼容的更改都可能会造成破坏。此 SKIP 提出了一个流程,以确保社区了解即将发生的更改,并可以相应地调整其库声明的 scikit-image 版本依赖项。

动机和范围#

在过去的 12 年中,scikit-image 有机地发展,功能由来自不同背景的广泛贡献者社区添加。这导致 API 的各个部分不一致:例如,skimage.transform.warp 反转了坐标的顺序,因此 (45, 32) 的平移实际上使 NumPy 数组中的值沿第 0 轴移动 32,沿第 1 轴移动 45,但仅在 2D 中

此外,随着用户群的增长,很明显某些早期的 API 选择结果证明比有帮助更令人困惑。例如,scikit-image 会自动将图像转换为各种数据类型,并在转换过程中重新缩放它们。范围为 [0, 255] 的 uint8 图像将自动转换为 [0, 1] 中的 float64 图像。这最初可能看起来是合理的,但是为了保持一致性,[0, 65535] 中的 uint16 图像会重新缩放到 [0, 1] 的浮点数,并且显微镜中常见的 [0, 4095] 中具有 12 位范围的 uint16 图像会重新缩放到 [0, 0.0625]。这些静默转换导致了许多用户的困惑。

更改此约定将需要在几乎所有 scikit-image 函数中添加一个 preserve_range= 关键字参数,该参数的默认值将在 4 个版本中从 False 更改为 True。最终,无论我们使弃用曲线多么平缓,此更改都将向后不兼容。

鉴于已积累的潜在 API 更改,这些更改已被证明过于繁琐和嘈杂,无法通过标准的弃用周期来修复,主要是因为它们涉及更改相同输入的函数输出,因此在过渡到 1.0 版本时进行所有这些更改是有意义的——我们使用的语义版本控制显式允许在主要版本更新时破坏 API 更改 [6]。但是,我们必须承认(1)大量的项目依赖于 scikit-image,因此会受到向后不兼容的更改的影响,并且(2)在科学 Python 社区中,对依赖项设置上限版本范围还不常见,因此,在他们的依赖项列表中,几乎没有人使用 scikit-image<1.*(尽管这种情况正在缓慢改变 [5])。

鉴于以上所述,我们需要找到一种方法来通知所有用户即将进行的更改,同时也允许他们在注意到这些更改后消除任何警告。

详细描述#

列出 scikit-image 1.0 的所有拟议 API 更改超出了本文档的范围,其中许多更改尚未决定。实际上,如果接受此 SKIP,1.0 过渡的范围和雄心可能会扩大。SKIP 提出了一种警告用户即将发生的重大更改的机制。可以在 GitHub 上找到跟踪拟议更改的元问题,scikit-image/scikit-image#5439 [7]。下面简要列出了一些示例以进行说明

  • 当 dtype 必须被强制转换为 float 时,停止重新缩放输入数组。

  • 停止在不同上下文(例如绘图或变形)中交换坐标轴顺序。

  • 允许自动返回非 NumPy 类型,只要它们可以通过 numpy.asarray 强制转换为 NumPy。

  • 统一不同函数中具有相同名称的相似参数;例如,我们目前在不同函数中具有 random_seedrandom_stateseedsample_seed,所有这些都表示相同的事情。

  • measure.regionprops 更改为返回字典而不是列表。

  • 将具有相同目的的函数(例如 watershedslicfelzenschwalb)合并到一个公共命名空间中。这将使新用户更容易找出他们应该尝试哪些特定任务的函数。

问题是,我们如何在尽可能减少干扰的情况下进行此过渡?

本文档建议将 0.19 作为最终的 0.x 系列版本发布,然后立即发布一个几乎相同的 0.20 版本,该版本会警告用户 1.0 中的重大更改,从而使他们有机会将其 scikit-image 依赖项固定为 0.19.x。该警告还将用户指向一个过渡指南,以准备他们的 1.0 代码。有关详细信息,请参阅 Implementation

此方法可确保所有用户都收到充分的警告,并有机会确保他们的脚本和库在 1.0 发布后继续工作。没有时间或意愿进行过渡的用户将能够正确地固定他们的依赖项。那些喜欢处于领先地位的用户也将能够围绕 1.0 版本进行计划,并与 scikit-image 同步正确地更新他们的代码。

实施#

该提案的详细信息如下

  • scikit-image 0.19 将是最终的真正的 0.x 版本。它包含一些新功能、错误修复和一些 API 更改,这些更改是在 0.17 中弃用的基础上进行的。

  • 在 0.19 之后不久,我们发布了 0.20,除了在导入时会发出警告之外,它与 0.19 完全相同。警告内容如下:“scikit-image 1.0 将于今年晚些时候发布,并且将包含重大更改。为确保您的代码继续运行,请安装 scikit-image<=0.19.*。要消除此警告,但在 1.0 发布后仍然依赖于 scikit-image,请安装 scikit-image!=0.20.*。” 该警告还包含指向进一步详细信息和有关在 conda 和 pip 环境中管理依赖项的说明的链接。

  • 在 0.20 之后,我们会进行所有需要的 API 更改,而无需弃用周期。重要的是,对于每个 API 更改,我们都会在文档中的“scikit-image 1.0 过渡指南”中添加一行,该指南会将库中每个更改的功能从旧形式映射到新形式。这些更改在 GitHub 问题 [7] 和 1.0 里程碑 [8] 中进行跟踪。

  • 一旦存储库中发生了过渡,我们就会发布 1.0.0a0,这是一个 alpha 版本,其中包含一个指向过渡指南的全局警告,以及所有新功能。我们还会发布 0.21,它包含相同的警告,但在功能上与 0.19 相同。这为选择固定到 scikit-image!=0.20.* 的作者提供了迁移到 1.0 的机会。

  • 至少一个月后,我们发布 1.0。

  • 我们将继续维护 0.19.x 分支一年,并进行错误修复,以便让用户有时间过渡到新的 API。

向后兼容性#

此提案会在库中的许多位置破坏向后兼容性。

替代方案#

新的包命名#

我们可以不破坏 scikit-image 包的兼容性,而是将该包保留在 0.19 版本,然后发布一个新的包,例如 scikit-image1,该包从 1.0 开始并导入为 skimage1。这将消除用户固定其 scikit-image 版本的需要——依赖于 skimage 0.x 的用户将能够“永久”使用该库。

最终,核心开发者们认为这种方法可能会不必要地分裂社区,一部分人继续使用 0.19 版本,而另一部分人则转向 1.0 版本。最终,下游代码迁移到 1.0 版本的过程会和所提出的方法一样痛苦,但由于所有安装 scikit-image 的人都仍然会得到旧版本,因此进行切换的压力会降低。

在多个版本中持续弃用#

这种过渡可以分多个版本逐步进行。例如,对于自动转换和重新缩放浮点输入的函数,我们可以添加一个 preserve_range 关键字参数,该参数最初的默认值为 False,但 False 的默认值将被弃用,用户会收到警告提示切换为 True。切换之后,我们可以(可选地)弃用该参数,在再过两个版本后,达到同样的结果:scikit-image 不再自动重新缩放数据,并且 API 中没有不必要的关键字参数残留。

当然,这种操作必须在上面提出的所有更改中同时进行。

最终,核心团队认为这种方法会给 scikit-image 的开发者和下游库的开发者带来更多的工作,而且好处不大:最终,更高版本的 scikit-image 仍然会与之前的版本不兼容,尽管时间跨度更长。

不进行提议的 API 更改#

另一种可能性是直接拒绝不向后兼容的 API 更改,除非在极端情况下。核心团队认为这本质上等同于将库锁定在 0.19 版本。

讨论#

2021 年 7 月初,核心团队举行了一系列会议来讨论这种方法。会议纪要位于 scikit-image 会议记录存储库中 [9]

正在进行的讨论将在用户论坛 [10]、开发者论坛 [11] 和 GitHub 讨论区 [7] 中进行。相关帖子的具体链接将在接受之前添加到本文档中。

决议#

这个 SKIP 在 2021 年 7 月的邮件列表中的一个主题中进行了最广泛的讨论 [12]。最终,许多核心开发者认为该计划存在太大的风险,要么会悄无声息地改变代码行为,要么会损害社区的善意,或者两者兼而有之。Matthew Brett 写道 [13]

恐怕我并不完全确定 1.0 选项是否会导致违反我称之为科学软件的 Konrad Hinsen 规则

“””在(几乎)任何情况下,新版本的科学软件包都不应在相同的函数/方法调用下,从软件包的先前版本中静默地给出明显不同的结果。”“”

Matthew 进一步写道 [14],如果我们 *不* 违反 Hinsen 规则,而是破坏了用户未固定的脚本,我们将会失去社区的大量善意

如果你让所有这些(如果他们幸运的话)崩溃,或者给出完全错误的结果,很难想象你不会对邮件列表中之外的其他大部分用户造成重大损害。

我们的核心开发者之一 Riadh Fezzani 强烈认为,SemVer [6] 足以保护用户 [15]

在 scikit-image 中,我们采用了在工程界广泛采用的语义版本控制。此约定管理 API 的破坏性更改,这正是我们通过发布 v1.0 所做的事情

但是,即使采用这种观点,也无法解决外部 scikit-image “文档” 的问题,例如十年来积累的 StackOverflow 答案,这些答案会因破坏性的 1.0 版本而过时,正如 Josh Warner 指出的那样 [16]

还值得考虑的是,那里有大量的 scikit-image 教学材料。我们无法控制其中的大多数,因此无法更新或编辑。YouTube 上教程的第一个搜索结果并不是最新的,而是浏览量较大的旧教程。

它也不能解决从旧 API *逐步* 迁移代码库到新 API 的问题,正如 Tom Caswell 指出的那样 [17]

换句话说,你不想让研究生陷入“我 *想* 使用新 API,但我有 10k LoC 的继承代码使用旧 API……”的境地。

最终,所有这些担忧加起来足以证明拒绝 SKIP 的理由充分。Juan Nunez-Iglesias 在邮件列表中写道 [18]

我未来的建议是拒绝 SKIP-3 并创建一个 SKIP-4,提议 skimage2 软件包。

因此,该 SKIP 被拒绝。

参考文献和脚注#

所有 SKIP 都应声明为使用 CC0 许可专用于公共领域 [1],如下面的 Copyright 中所示,并鼓励使用 CC0+BY 进行署名 [2]