跳过 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 版本依赖。

动机与范围#

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] 的 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]。为了说明目的,下面简要包含了一些示例:

  • 当数据类型必须强制转换为浮点数时,停止重新调整输入数组。

  • 在不同的上下文中,例如绘图或扭曲,停止交换坐标轴的顺序。

  • 允许自动返回非 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 版本。它包含了一些新功能、错误修复,以及基于 0.17 版本中弃用的一些 API 变更。

  • 在 0.19 之后不久,我们发布了 0.20,除了在导入时发出警告外,其他都相同。警告内容如下:“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] 声明为专用于公共领域,如下面 版权 部分所示,并鼓励使用 CC0+BY [2] 进行署名。