PEP 778 – 在 Wheels 中支援符號連結
- 作者:
- Emma Harper Smith <emma at python.org>
- 贊助人:
- Barry Warsaw <barry at python.org>
- PEP 委託人:
- Paul Moore <p.f.moore at gmail.com>
- 討論於:
- Discourse 討論串
- 狀態:
- 延後 (Deferred)
- 類型:
- 標準軌跡 (Standards Track)
- 主題:
- 套件封裝 (Packaging)
- 依賴項目:
- 777
- 建立日期:
- 2024年5月18日
- 公告歷史:
- 2024-10-10
摘要
Wheels 目前無法很好地處理符號連結,在安裝時會複製內容而不是建立符號連結。為妥善處理在 wheels 中發佈函式庫,我們提議引入一個新的 LINKS 中繼資料檔案,以平台可攜帶的方式處理符號連結。本規範需要一個新的 wheel 主要版本,詳見 PEP 777。
PEP 延後說明
此 PEP 已被推遲,直到為 wheel 格式的重大變更建立更好的相容性方案。一旦為 wheels 建立了一個允許以不引人注目的方式進行向後不相容行為的相容性方案,本 PEP 應解決以下幾點:
- 將此主題重新聚焦於 POSIX 平台上共用函式庫的符號連結,或許與平台標籤綁定?
- 符號連結應該在壓縮檔中以檔案屬性具體化,還是以
LINKS檔案呈現?它是否可以編碼在RECORD中? - 澄清本 PEP 對 PEP 660 可編輯安裝而言不夠實用,因為它將不再是跨平台的。
- 描述在 POSIX 平台上符號連結不可用時的備用行為。
動機
如今,wheels 中的符號連結會被建立為檔案的副本,因為 CPython 的 zipfile 模組 基於安全考量 不支援就地處理符號連結。
這 對希望在 wheels 中發佈大型編譯函式庫的專案造成問題,因為它們必須選擇大幅增加專案在磁碟上的安裝大小,或者省略符號連結並可能破壞某些下游使用案例。
要在 POSIX 上發佈一個能正確載入用於執行時使用或建置時連結的函式庫,該函式庫應遵循 POSIX 風格載入器和連結器搜尋的慣例。載入器使用的兩個主要檔案名稱是「soname」和「real name」。「soname」是一個類似 libfoo.so.3 的檔案,其中 3 是一個數字,當函式庫介面變更時會遞增。「real name」是一個命名為 libfoo.so.3.1.4 的檔案,其中額外的版本資訊允許載入器找到特定版本的函式庫。最後,在編譯程式碼以連結函式庫時,連結器會搜尋一個「連結器名稱」,命名為 libfoo.so。更詳細的描述可在 這份關於共用函式庫的 Linux 文件 中找到。為完全支援所有執行時和建置時的使用案例,專案需要發佈所有這三個檔案。通常,這在 POSIX 平台上是透過使用符號連結來處理的,這樣函式庫就不會在磁碟上重複三次。
回到 Python 封裝,有許多熱門專案會發佈二進位函式庫,例如 numpy、scipy 和 pyarrow。其他 site-packages 會在其他 wheels 中使用 dlopen 函式庫,例如 pytorch 和 jax。這些專案目前依賴 wheel 中的單一函式庫,但如果系統函式庫中存在一個「real name」函式庫版本,這可能會導致連結器找到錯誤的函式庫。
另外還有一個潛在的好處是,wheels 中的符號連結可以透過簡單地在使用者 site-packages 目錄中放置一個符號連結來實現更簡單的可編輯安裝,但本 PEP 將此作為一個開放性問題,留待未來的 PEP 探索。
原理
為支援 POSIX 上載入和函式庫連結所使用的函式庫的三種主要命名方式,我們建議在 Python wheels 中增加對符號連結的支援。為追蹤已建立的符號連結,並潛在地支援可能不直接支援 POSIX 符號連結的其他平台,我們提議使用一個新的 wheel 中繼資料檔案 LINKS,它將與 METADATA、RECORD 及其他中繼資料檔案一起存在於 .dist-info 目錄中。
使用 LINKS 檔案將允許更多跨平台的類似符號連結的使用方式。在 Windows 上,符號連結需要 允許使用者建立符號連結的群組原則 (例如透過啟用 開發人員模式) 或管理員權限。這表示在某些使用者系統上可能不支援符號連結。透過使用 LINKS 檔案,安裝程式將能夠潛在地使用其他方法來處理符號連結,例如 Windows 上的連接點 (junctions),否則安裝程式將會失敗。
本 PEP 也描述了安裝程式在安裝更新後的 wheel 時必須進行的檢查。這些檢查旨在處理允許 wheels 安裝符號連結所帶來的安全風險。有關這些檢查為何重要的更多資訊,請參閱 安全影響。
規範
Wheel 主要版本號升級
本 PEP 要求升級 wheel 的主要版本號,因此使用 LINKS 生成的 wheels 的 Wheel-Version **必須** 至少為版本 2.0,以防止舊版安裝程式默默地無法安裝符號連結並破壞使用者環境。詳情請參閱 PEP 777。
新的 LINKS 中繼資料檔案
為啟用跨平台符號連結,本 PEP 引入了一個新的 wheel 中繼資料檔案 LINKS。以下是一個 LINKS 檔案的範例:
my_package/libfoo.so.3.1.4,my_package/libfoo.so.3
my_package/libfoo.so.3,my_package/libfoo.so
如上所示,LINKS 的格式是 source_path,target_path,其中 source_path 是相對於 wheel 中任何命名空間或套件根目錄的路徑。target_path 是 wheel 中任何套件的套件或命名空間中的一個 *非懸空* 路徑 (即在解壓縮 wheel 內容後,檔案系統上存在的路徑)。這表示如果一個 wheel 包含多個套件,則 wheel 中所有套件的路徑都是可接受的。
安裝程式行為規範
安裝程式在決定任何 source_path 或 target_path 是否有效 *之前*,**必須** 解析 LINKS 檔案中包含的任何連結的路徑。安裝程式**必須**驗證 source_path 和 target_path 位於來自 wheel 的任何命名空間或套件內部。安裝程式**必須**拒絕 wheel 中的循環符號連結。如果符號連結的長鏈(符號連結多次指向符號連結)超過安裝程式設定的限制,安裝程式**可以**發出錯誤。
安裝程式在處理帶有符號連結的 wheel 時**必須**遵循以下步驟:
- 檢查
.dist-info中是否存在LINKS檔案。如果不存在,則無需進一步操作。 - 像 wheel 1.x 一樣,解壓縮 wheel 套件和資料目錄中的所有檔案。
- 驗證每個
source_path和target_path對組中,target_path存在於剛解壓縮的其中一個套件命名空間中。 - 接下來,檢查安裝程式是否可以在站台目錄中為每個對組建立某種類型的連結。如果安裝程式無法為目前平台上的檔案/資料夾
target_path建立連結,則**必須**引發錯誤。一個失敗模式的範例是 POSIX 符號連結指向檔案目標,而安裝程式在 Windows 上運行,且安裝程式無法建立符號連結但可以建立連接點 (junctions)。在這種情況下,安裝程式**必須**發出錯誤,因為它無法處理該連結。 - 最後,安裝程式**必須**在
source_path和target_path之間增加一個與平台相關的連結。
處理符號連結時,安裝程式預設**不得**複製檔案而不是生成符號連結。安裝程式**可以**在替代配置或命令列參數下提供此類行為。
建置後端規範
建立 wheel 時,建置後端在決定是否將符號連結包含在 wheel 中時,**必須**像對待其目標一樣對待符號連結。建置後端**必須**驗證 LINKS 檔案中沒有懸空符號連結。建置後端**應**識別將包含在建置中的平台相關符號連結。在 POSIX 系統上,這通常是符號連結;在 Windows 上,這包括符號連結和連接點 (junctions)。
回溯相容性
引入符號連結將需要增加 wheel 格式的主要版本號。這意味著使用新 wheel 格式的新 wheels 將在舊版安裝程式工具上引發錯誤,根據 wheel 規範。
請參閱關於「Wheel 2.0」的 PEP 777。
安全性影響
如果處理不慎,符號連結可能非常危險。一個簡單的例子是,如果使用者執行 sudo pip install malicious 而沒有任何保護,那麼惡意套件可能會覆寫 /etc/shadow 並替換系統上的密碼雜湊,從而允許惡意登入。
本 PEP 列出了安裝程式對 wheel 中符號連結進行檢查的幾項要求,以確保上述攻擊不會發生。這意味著安裝程式**至關重要地**實施這些安全防護措施,並防止在套件安裝時的惡意使用。
特別是,安裝程式**必須**進行以下檢查:
- 符號連結不得指向來自 wheel 的任何套件或命名空間之外。
- 符號連結不是懸空的(目標在安裝時存在)。
- 符號連結不是循環的,檢查到一定深度後停止,以避免阻斷服務請求。
移除時不要追蹤符號連結。
如何教學
一旦這些變更在生態系統中傳播開來,最終使用者應該能透明地體驗到 wheels 中符號連結帶來的好處。重要的是,如果平台上不支援符號連結,安裝程式應提供清晰的錯誤訊息,並解釋安裝失敗的原因。
對於建置函式庫的人來說,packaging.python.org 上的文件應該描述 wheels 中符號連結的使用案例和注意事項(特別是平台支援)。否則,建置後端應以處理任何普通檔案相同的方式透明地處理它。
參考實作
待辦事項
否決的想法
只在各處使用 POSIX 符號連結
本 PEP 希望允許將 LINKS 用於未來潛在的 PEP 660 可編輯安裝。這個未來的 PEP 應該支援 Windows,因此可能需要使用連接點 (junctions)。
不要在 LINKS 中使用連接點 (Junctions)
連接點 (Junctions) 是在 Windows 上支援資料夾之間符號連結的一種有限方式。它們不支援檔案。本 PEP 允許使用連接點,因為使用者可能希望只將資料夾連結到不同的位置,並且未來的 PEP 660 實作可能需要依賴此功能。
將符號連結放入 RECORD 中繼資料檔案
雖然這可以做到,但它會使 RECORD 檔案變得混亂。此外,最直接的實作會將目標放在記錄的末尾。這會使得掃描該行並直觀地查看 wheel 中存在哪些符號連結變得更加困難。
函式庫維護者應使用 Python 定位函式庫
使用 Python 來定位函式庫會容易得多。然而,有些函式庫,例如 libtorch,被擴充模組使用並本身需要載入依賴項。有些編譯後的函式庫無法使用 Python 來尋找其載入器依賴項。
包含硬連結的支援
本 PEP 並未指定任何與硬連結相關的行為。這是故意的。這將作為未來 PEP 的擴充功能。
待決問題
PEP 660 與延遲可編輯安裝支援
本 PEP 將 PEP 660 可編輯安裝機制的規範和實作留待未來的 PEP 解決;這是否應該在本 PEP 中指定?
安全性
本 PEP 需要審查,以確保它不會導致新的安全漏洞。我們是否應該對符號連結的來源或目標施加其他限制以保護使用者?
允許套件間的符號連結
這對於希望在多個 wheels 之間分片大型函式庫等依賴項,但又使其在主要父 wheel 中可用的專案來說可能很有用。
LINKS 的格式
目前格式是從 RECORD 衍生而來的,但或許存在更好的格式。
先前的討論
https://discuss.python.org/t/symbolic-links-in-wheels/1945/25
版權
本文件已進入公有領域或遵循 CC0-1.0-Universal 授權,以較寬鬆者為準。
來源: https://github.com/python/peps/blob/main/peps/pep-0778.rst
最後修改: 2025-07-09 03:07:54 GMT