SKIP 4 — 过渡到 scikit-image 2.0#
- 作者:
Juan Nunez-Iglesias <juan.nunez-iglesias@monash.edu>
- 作者:
Lars Grüter
- 状态:
草案
- 类型:
标准跟踪
- 创建时间:
2022-04-08
- 已解决:
<null>
- 解决方案:
<null>
- 生效版本:
无
摘要#
scikit-image 正准备发布 1.0 版本。这被视为清理 API 的一个机会,包括向后不兼容的更改。 其中一些更改涉及更改返回值而不更改函数签名,这通常只能通过添加一个无用的关键字参数(例如 new_return_style=True
)来实现,该参数的默认值会在多个版本中更改。 结果仍然是一个向后不兼容的更改,但发生在较长的时间段内。
尽管处于测试阶段并且处于 0.x 系列版本中,但 scikit-image 的使用非常广泛,任何向后不兼容的更改都可能具有破坏性。鉴于 SKIP-3 的拒绝,本文档提出了一种创建新 API 的替代途径。 新途径包括以下步骤:
任何计划在 v0.20 和 v0.21 中进行的待处理弃用都已完成(v0.19 中弃用消息建议的新 API 成为唯一 API)。
这作为 1.0 版本发布。
此时,分支
main
将包和导入名称更改为skimage2
,并且 API 可以自由发展。
下面解释了 API 更改的更多动机,并且主要从 SKIP-3 中复制。
动机和范围#
scikit-image 在过去 12 年多的时间里有机地发展,功能由来自不同背景的广泛贡献者社区添加。 这导致 API 的各个部分不一致:例如,skimage.transform.warp
反转坐标的顺序,因此 (45, 32) 的平移实际上将 NumPy 数组中的值沿第 0 轴移动 32,沿第 1 轴移动 45,但仅在 2D 中。 在 3D 中,(45, 32, 77) 的平移将每个轴中的值移动相应位置的数字。
此外,随着用户群的增长,人们越来越清楚地看到,某些早期的 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。 最终,无论我们使弃用曲线多么平缓,这种更改都将是向后不兼容的。
其他主要函数,例如 skimage.measure.regionprops
,可以使用 API 调整,例如,通过返回将标签映射到属性的字典,而不是列表。
鉴于潜在 API 更改的累积,这些更改已变得过于繁琐和嘈杂,无法通过标准的弃用周期来修复,主要是因为它们涉及更改相同输入的函数输出,因此在过渡到 2.0 版本时进行所有这些更改是有意义的。
尽管语义版本控制 [6] 在技术上允许主要版本跳跃时的 API 更改,但我们必须承认 (1) 大量项目依赖于 scikit-image,因此会受到向后不兼容更改的影响,并且 (2) 在科学 Python 社区中,在依赖项上设置上限版本范围还不是很常见的做法,因此不太可能有人在其依赖项列表中使用 scikit-image<1.*
或 scikit-image<2.*
。 这意味着发布具有中断 API 更改的 scikit-image 2.0 版本会给大量用户带来破坏。 此外,如此广泛的更改会使大量的 StackOverflow 和其他用户指南失效。 最后,发布具有大量更改的新版本会阻止用户逐步迁移到新 API:旧代码库必须进行整体迁移,因为不可能同时依赖两个版本的 API。 这将对许多用户构成巨大的进入障碍。
鉴于上述情况,此 SKIP 建议我们发布一个新软件包,在该软件包中,我们可以应用从十多年的开发中获得的所有经验,而不会破坏我们现有的用户群。
详细描述#
列出 skimage2 的所有建议 API 更改超出了本文档的范围,其中许多尚未决定。 实际上,如果接受此 SKIP,则 2.0 过渡的范围和雄心可能会扩大。 此 SKIP 而是提出一种管理过渡的机制,而不会破坏用户的代码。 有关建议更改的元问题可以在 GitHub 上找到,scikit-image/scikit-image#5439 [7]。 下面简要介绍一些示例以进行说明
当必须将 dtype 强制转换为 float 时,停止重新缩放输入数组。
停止在不同上下文(例如绘图或变形)中交换坐标轴顺序。
允许自动返回非 NumPy 类型,只要它们可以使用
numpy.asarray
强制转换为 NumPy。协调不同函数中的相似参数以具有相同的名称;例如,我们目前在不同函数中有
random_seed
、random_state
、seed
或sample_seed
,所有这些都表示相同的意思。将
measure.regionprops
更改为返回字典而不是列表。将具有相同用途的函数(例如
watershed
、slic
或felzenschwalb
)合并到公共命名空间中。 这将使新用户更容易找到他们应该尝试哪些函数来执行特定任务。 它还将帮助社区围绕通用 API 发展,现在 scikit-image API 对于每个函数基本上都是唯一的。
为了以最少的用户中断进行此过渡,此 SKIP 建议发布一个新的库 skimage2,该库将替换现有库,但前提是用户明确选择加入。 此外,通过发布新库,用户可以同时依赖 scikit-image (1.0) 和 skimage2,从而允许用户逐步迁移其代码。
实施#
该提案的细节如下:
scikit-image 0.19 之后将是 scikit-image 1.0。所有弃用消息都将从 1.0 中删除,并且该 API 将被视为 scikit-image 1.0 API。
在 1.0 之后,主分支将更改为 (a) 将导入名称更改为 skimage2,(b) 将包名称更改为 skimage2,以及 (c) 将版本号更改为 2.0-dev。
在 PyPI 上将没有版本为 2.0 的“scikit-image”包。使用
pip install scikit-image
的用户将始终获得该包的 1.x 版本。要安装 scikit-image 2.0,用户需要pip install skimage2
,conda install skimage2
或类似方式。在就新 API 达成共识后,将发布 skimage2。
在发布 skimage2 之后,将发布 scikit-image 1.1。此版本与 1.0(包括错误修复)相同,但会建议用户 (a) 升级到 skimage2 或 (b) 将包固定为
scikit-image<1.1
以避免警告。scikit-image 1.0.x 和 1.1.x 将在未指定的时间段内接收关键错误修复,具体取决于错误的严重性和所涉及的工作量。
向后兼容性#
此提案在库中的多个地方打破了向后兼容性。但是,它是在新的命名空间中进行的,因此该提案不会引起我们用户的向后兼容性问题。也就是说,作者将尝试将向后不兼容的更改数量限制在那些可能大幅提高整体用户体验的更改上。预计将 skimage
代码移植到 skimage2
将是一个简单的过程,并且我们将在 skimage2
发布时发布一份用户指南,以进行过渡。用户将通过 scikit-image 1.1 中的警告等方式收到有关这些资源的通知。
备选方案#
使用语义版本控制在同一包中发布新 API#
这是 SKIP-3,在与社区讨论后被拒绝。
在多个版本中持续弃用#
这种过渡可能会在多个版本中逐步发生。例如,对于自动转换和重新缩放浮点输入的函数,我们可以添加一个 preserve_range
关键字参数,该参数最初默认为 False,但 False 的默认值将被弃用,用户会收到警告以切换到 True。切换后,我们可以(可选地)弃用该参数,经过另外两个版本,到达相同的位置:scikit-image 不再自动重新缩放数据,API 中不再存在不必要的关键字参数。
当然,这种操作必须在上述所有建议的更改上同时进行。
最终,核心团队认为这种方法对 scikit-image 开发人员和下游库的开发人员都产生了更多的工作量,而且好处值得怀疑:最终,更高版本的 scikit-image 仍然会与之前的版本不兼容,尽管时间跨度更长。
包含两个版本的单个包#
由于导入名称正在更改,因此可以制作一个同时包含 skimage
和 skimage2
命名空间的单个包,至少在一段时间内如此。此选项很有吸引力,但这意味着 1.0 命名空间的长期维护,我们可能缺乏维护时间,或者 1.0 命名空间的长期弃用周期,这最终会导致许多不开心的用户从他们的 scikit-image 使用中收到弃用消息。
不进行建议的 API 更改#
另一种可能性是直接拒绝向后不兼容的 API 更改,除非在极端情况下。核心团队认为这本质上等同于将库固定在 0.19 版本。
“scikit-image2”作为新包名称#
作者承认,新名称应谨慎选择,以尽可能减少对 scikit-image 用户群和社区的干扰。但是,为了保护没有上限版本约束的用户免于意外升级到新 API,必须更改包名称 scikit-image
。类似地,更改导入名称 skimage
是有利的,因为它允许在同一环境中使用两个 API。
本文档建议仅将 skimage2
作为 scikit-image 的 API 版本 2.0 的唯一新名称,同时用于导入名称和在 PyPI、conda-forge 以及其他地方的名称。支持此做法的论点如下:
该项目仅引入一个新名称,从而使相关名称的数量尽可能少。
通过此更改,导入名称和包名称匹配。
用户可能会对他们应该安装
scikit-image2
还是scikit-image-2
感到困惑。人们认为skimage2
避免了这种困惑。知道
skimage
是什么的用户,如果在某个安装说明中看到skimage2
,很可能会推断出它是该包的较新版本。用户不太可能知道新的 API 2.0 而不知道新的包名称。拟议发布的 scikit-image 1.1 可能会在安装和更新过程中将用户指向
skimage2
,从而清楚地传达继承者的名称。
以下论点反对将包命名为 skimage2
:
根据“最少惊讶原则”,
scikit-image2
可能被认为是包名称最不令人惊讶的演变。它打破了其他 scikit(包括 scikit-image)所遵循的约定。(有人指出,这种约定已经有一段时间不成立了,并且在名称中引入版本号无论如何都是一个先例。)
前面的“相关工作”部分描述了其他项目如何处理类似的问题。
讨论#
此 SKIP 是对 SKIP-3 的讨论结果。有关此 SKIP 的动机的更多背景信息,请参阅该文档的“决议”部分。
决议#
参考和脚注#
所有 SKIP 都应声明为使用 CC0 许可专用于公共领域 [1],如以下 Copyright
中所示,并鼓励使用 CC0+BY 进行署名 [2]。
版权#
本文档使用知识共享 CC0 许可专用于公共领域 [1]。 在适当的情况下鼓励署名此来源,如 CC0+BY [2] 所述。