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)來完成,該參數的預設值會在多個版本中更改。結果仍然是一個不向後兼容的變更,但會經過較長的時間段。

儘管 scikit-image 處於 beta 版且為 0.x 系列版本,但它被廣泛使用,任何不向後兼容的變更都可能具有破壞性。此 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] float,而 [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 準備他們的程式碼。有關詳細資訊,請參閱 實作

此方法可確保所有使用者都獲得充分的警告,並有機會確保他們的腳本和函式庫在 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。

  • 為了讓使用者有時間轉換到新的 API,我們將會持續維護 0.19.x 分支一年,並提供錯誤修復。

向後相容性#

此提案會在函式庫的多個地方破壞向後相容性。

替代方案#

新的套件命名#

我們不直接在 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]