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)来实现,其默认值在几个版本中发生变化。结果仍然是向后不兼容的更改,但在更长的时间段内完成。

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

动机和范围#

scikit-image 在过去 12 年中逐步发展,来自不同背景的广泛社区的贡献者添加了功能。这导致 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] 内的 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 版本,该版本在导入时会向用户发出警告,从而使他们有机会将其 scikit-image 依赖项固定到 0.19.x。警告还会将用户指向迁移指南,以便为 1.0 准备其代码。有关详细信息,请参阅实施

这种方法确保所有用户都能获得充分的警告,以及一个机会来确保他们的脚本和库在发布 1.0 之后继续工作。没有时间或兴趣进行迁移的用户将能够正确地固定他们的依赖项。那些喜欢走在技术前沿的用户也将能够围绕 1.0 版本进行规划,并正确地更新他们的代码,与 scikit-image 同步。

实施#

该提议的详细信息如下:

  • scikit-image 0.19 将是最后一个真正的 0.x 版本。它包含一些新功能、错误修复以及从 0.17 中的弃用继续的几个 API 更改。

  • 在 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 行代码继承了使用旧 API 的代码……”。

最终,所有这些担忧都形成了一个有说服力的理由,拒绝了 SKIP。Juan Nunez-Iglesias 在邮件列表上写道 [18]

我建议将来拒绝 SKIP-3,并创建一个 SKIP-4 来提议 skimage2 包。

因此,拒绝了该 SKIP。

参考文献和脚注#

所有 SKIP 应根据 CC0 许可证 [1] 声明为公共领域,如以下 Copyright 中所示,鼓励根据 CC0+BY [2] 进行归属。