API 領域與版本號碼對應表
摘要:仰賴特定底層函式庫的版本號碼可能不夠精確,而且會隨著上游函式庫演進和變更而造成問題。需要更詳細的方法。在這篇文章中,我概述了目前和潛在的工作,目標是更完整地檢查基於 API 的需求以及函式庫的動態釘選。
什麼構成良好的版本號碼
版本號碼的集合應具備以下屬性
- 此集合必須是無界的
- 此集合必須是可排序的(可能)
當然,符合這些要求的集合可能無法傳達太多關於它們所代表軟體的資訊,除了兩個事物是否等效及其相對年代。請注意,可排序的要求可能不是必要的,但在考慮「升級」的概念時通常很有用,因為它提供了較舊和較新套件之間的明確劃分。在許多情況下,版本號碼的結構提供了額外的資訊。對於某些專案,版本號碼包含發布日期,通常使用 cal ver。許多專案使用語意化版本控制,它試圖在版本號碼中編碼關於底層原始碼 API 的資訊。
版本號碼與 API 釘選
版本號碼最重要的指定位置之一是在 API 釘選期間。原始碼通常需要它使用的函式庫中的特定 API。這需要一個釘選來指定可以使用哪些版本的底層函式庫。然後,套件管理器使用這些釘選來確保建立相容的環境。
然而,這些釘選(甚至缺少釘選)都會產生問題。首先,釘選是對當前和未來、全球套件生態系統的一次性、本地聲明。例如,將 scipy
釘選到當前主要版本號碼可能無法隨著時間推移而保持有效,較新版本的 scipy
可能會破壞 API,但不會更改主要版本號碼。同樣地,缺少 scipy
的釘選也可能是錯誤的,因為 API 會中斷。即使建立穩固的上限和下限的釘選也可能是錯誤的,因為新版本的釘選函式庫會恢復遺失的 API。這些問題對於將釘選與特定版本的原始碼綁定的依賴系統來說尤其麻煩,需要建立新版本才能更新釘選。Conda-Forge 能夠透過repodata 修補來避免其中一些問題,動態更新套件的聲明需求。總體而言,此過程充滿風險,因為每個套件都依賴函式庫 API 的不同部分,一個破壞一個套件的版本變更可能會讓其他套件安然無恙。
潛在的發展方向
以上所有問題都是由混淆地圖與疆域所引起的。地圖(在此案例中為函式庫的版本號碼)無法準確地表示疆域,即 API 本身。為了修正此問題,我們需要更準確地描述疆域。實現此目標並不容易,但我認為有一種方法可以足夠接近以限制錯誤數量。
我們需要一種程式化的方法來檢查特定函式庫的特定版本是否提供所需的 API。我認為這可以透過迭代方式實現,每個步驟都提供額外的清晰度和實作難度。請注意,在以下步驟中,我以 python 套件為例,但我認為這些步驟足夠通用,可以應用於其他語言和生態系統。
- 確定程式碼的需求函式庫,這由 depfinder 等工具提供,並且開始整合到 Conda-Forge 機器人系統中(儘管它們仍然是高度實驗性的並且正在開發中)。
- 確定函式庫的版本是否提供所需的模組。這可以透過使用 depfinder 找到匯入,並使用 libcfgraph 提供的匯入名稱與運送這些匯入的套件版本之間的對應來完成。
- 確定匯入的模組是否提供正在匯入的符號。這將需要列出給定 python 模組中的所有符號,包括頂層作用域變數、函式名稱、類別名稱、方法等。
- 對於可呼叫物件,確定使用的呼叫簽章是否與方法或函式定義相符。
depfinder 專案在這條路上取得了重大進展,提供了一個易於使用的工具,可從原始碼中提取準確的匯入和套件需求資料。Depfinder 甚至有案例可以處理程式碼區塊內的匯入,這些匯入可能會使需求成為可選的或使用 python 標準函式庫。Depfinder 未來的研究方向,包括使用更準確的匯入與套件名稱之間的對應,以及提供關於套件需求的詳盡無遺的元資料(例如 try: except:
區塊中 pyqt4
與 pyqt5
的匯入),將提供更準確的需求資訊。
在上述每個階段,我們都可以透過幫助使用者、維護者和原始碼作者保持他們的需求一致,並在發生衝突時發出警告,從而為他們提供顯著的價值。當建立新版本的匯入函式庫時,Conda-Forge 可以更新其 repodata,以正確表示該版本是否與其下游消費者 API 相容。此外,可以向可能想要修補自己的元資料或檢查一段原始碼在其需求中是否自我一致的第三方消費者提供列出所有符號和呼叫簽章的表格。這也將有助於放寬釘選,為 Conda-Forge 和其他套件生態系統建立更多可解決的環境。此外,隨著此工具的成熟和準確性提高,它可以納入 Conda-Forge 機器人系統中,以在版本變更和 repodata 修補期間自動更新依賴項,從而幫助減少維護負擔。
從符號表建立的工具也可能產生遠遠超出 Conda-Forge 的影響。例如,符號表可以讓原始碼作者逐行檢查他們的程式碼,揭示哪些行強制使用較舊或較新版本的依賴項。這可以實現大規模的原始碼遷移,並具有外科手術般的精確度,使開發人員能夠提取和重寫阻止使用新版本函式庫的少數程式碼行。
注意事項
這種方法有一些重要的注意事項需要牢記。
- 所有這些工作都旨在理解給定函式庫的 API,這種方法無法深入了解 API 內部的程式碼,或者那裡的變更是否會影響下游消費者。例如,修復函式庫程式碼中錯誤和安全漏洞的版本更新可能根本不會更改 API。從此工具的角度來看,沒有理由升級,因為 API 沒有不同。當然,在這種情況下有強烈的升級理由,因為有錯誤或易受攻擊的函式庫可能會給下游程式碼帶來巨大的麻煩和責任,應該盡快移除。
- 某些功能可能取決於社群更廣泛的採用。例如,這種方法將極大地受益於 python 類型提示,因為 API 可以約束到預期的類型。此類類型約束將為 API 版本範圍提供更高的準確性,因為可以檢測到任何變更。但是,類型提示可能不會在 python 社群中以足夠高的採用率被採用,以使其真正對此應用程式有用。
- 原始碼從根本上來說是靈活的。可能會有即使這種方法也無法解決的程式碼難題,尤其是在多種語言和執行階段模組載入進入畫面時。我個人的希望是,程式碼能夠辨識何時發生這些情況,提供對正在發生的事情的最佳猜測,並向使用者提供足夠的元資料,以便他們了解結果準確性的降低。從根本上來說,工具只能向使用者提供非常有根據的猜測和上下文,然後使用者需要去弄清楚程式碼內部實際發生了什麼。
結論
基於版本號碼的釘選是對 API 相容性的不精確表示。基於原始碼檢查的更準確表示將使 Conda-Forge 生態系統更穩健和靈活,同時減少維護負擔。實現此目標的路徑部分已建成,並且可以使用目前的工具和資料庫實現近期步驟。