PEP 610 – 記錄已安裝套件的原始直接 URL
- 作者:
- Stéphane Bidoul <stephane.bidoul at gmail.com>, Chris Jerdonek <chris.jerdonek at gmail.com>
- 贊助人:
- Alyssa Coghlan <ncoghlan at gmail.com>
- BDFL-Delegate:
- Pradyun Gedam <pradyunsg at gmail.com>
- 討論於:
- Discourse 討論串
- 狀態:
- 最終 (Final)
- 類型:
- 標準軌跡 (Standards Track)
- 主題:
- 套件封裝 (Packaging)
- 建立日期:
- 2019年4月21日
- 公告歷史:
- 決議:
- Discourse 訊息
摘要
根據 PEP 440,一個套件可以透過名稱與版本,或是直接 URL 參考(參見 PEP 440 直接參考)來識別。安裝後,名稱與版本會被記錄在專案元資料中,但目前沒有方法可以取得該套件最初以直接 URL 參考進行識別時的 URL 詳細資訊。
本提案定義了額外的元資料,由安裝前端(installation front end)新增至已安裝的套件中,記錄其「直接 URL 原始來源」,供檢視已安裝套件資料庫的消費者使用(參見 PEP 376)。
動機
本 PEP 最初的動機是為了讓具備「凍結(freeze)」功能的工具,能夠在更廣泛的情況下重新建立 Python 環境。
具體而言,本 PEP 源於解決 pip issue #609 的需求:即改進 pip freeze 在處理從直接 URL 參考安裝的套件時的行為。本提案遵循了 discuss.python.org 上的討論串,探討實作此功能的最佳途徑。
從直接 URL 參考進行安裝
像 pip 這樣的 Python 安裝程式,能夠從套件索引下載並安裝套件。它們也能夠根據需求下載並安裝原始碼,這些需求指定了原始碼壓縮檔或版本控制系統(VCS)儲存庫的任意 URL,正如 PEP 440 直接參考 所標準化規範。
換句話說,目前存在兩種相關的安裝模式。
- 要安裝的套件指定為名稱與版本說明符
在此情況下,安裝程式會查看套件索引(或在 pip 的情況下選擇性使用--find-links)以找到要安裝的套件。
- 要安裝的套件指定為直接 URL 參考
在此情況下,安裝程式會下載 URL 指定的內容(通常是 wheel、原始碼壓縮檔或 VCS 儲存庫)並進行安裝。在此模式下,安裝程式通常會將原始碼下載至臨時目錄中,必要時呼叫 PEP 517 建構後端來產生 wheel,安裝該 wheel,最後刪除臨時目錄。
安裝完成後,使用者系統上不會留下任何關於使用者請求下載該套件時所用 URL 的痕跡。
凍結(Freezing)環境
Pip 還提供了一個名為 pip freeze 的指令,用於檢查已安裝 Python 套件的資料庫,以產生需求清單。此指令的主要目標是協助使用者產生需求清單,以便日後能以最高的保真度重新安裝相同的環境。
截至 pip 19.3 版本,pip freeze 指令會為每個已安裝的套件(可編輯安裝除外)輸出一行 name==version。為了達到重新安裝相同環境的目標,這要求 (名稱, 版本) 元組必須參考套件的不可變版本。這種不可變性由 Warehouse 等套件索引所保證。所使用的套件索引通常可由安裝程式的環境變數或指令列參數得知。
因此,此凍結機制對於安裝模式 1(即套件安裝指定為名稱加版本說明符時)運作良好。
對於安裝模式 2,即套件安裝指定為直接 URL 參考時,name==version 元組顯然不足以重新安裝相同的套件,且使用 freeze 指令的使用者期望它能輸出最初請求的 URL。
上述推論同樣適用於除 pip freeze 以外的工具,這些工具試圖從已安裝 Python 套件資料庫產生 Pipfile.lock 或任何其他類似格式。除非另有說明,本文件中「凍結」一詞即作為此類操作的通用術語。
對於應用程式整合者而言,從版本控制系統(VCS)URL 進行安裝的重要性
對於應用程式整合者而言,能夠可靠地安裝並凍結未發布版本的 Python 套件非常重要。例如,當開發者需要部署某個相依套件的未發布修補版本時,通常會直接從包含修補程式的 VCS 分支安裝該相依套件,同時等待維護者發布更新版本。
在此類情況下,對於「凍結」操作來說,鎖定已安裝的確切 VCS 參考(若有的話為提交雜湊碼/commit-hash)非常重要,以便建立具有最高保真度的可重現建構(reproducible builds)。
VCS URL 可用的額外原始來源元資料
對於 VCS URL,在安裝時可取得額外的原始來源資訊,這對於內省(introspection)與特定的工作流程非常有用。例如,當從 VCS URL 安裝修訂版本(revision)時,工具可以判定該修訂版本對應的是分支、標籤(tag)還是(在 Git 的情況下)一個 ref。這些資訊可在檢視已安裝套件資料庫時,向使用者傳達更多關於安裝了哪個版本的資訊(例如安裝的是分支還是標籤,若有的話,該分支或標籤的名稱)。這也能讓人了解是否可以使用標籤格式來建構 PEP 440 直接參考 URL,因為只有標籤具有不可變的語意。
在修訂版本是可變的情況下(例如分支和 Git refs),了解此資訊可啟用一些工作流程,例如讓使用者更新到他們正在追蹤的分支的最新版本,或更新到他們正在本地檢視的 pull request 的最新版本。相反地,當修訂版本是標籤時,工具可以預先(例如無需網路呼叫)得知不需要更新。
如同 URL 本身,如果這些資訊不在安裝時(VCS 儲存庫可用時)記錄下來,它們就會丟失。
關於「可編輯(editable)」安裝的說明
Pip 的可編輯安裝模式大致上讓使用者為了開發目的將本地目錄插入 sys.path 中。此模式常被濫用以規避非可編輯安裝(從 VCS URL)在安裝後會丟失原始來源追蹤的事實。事實上,可編輯安裝會隱式地在檢出(checkout)目錄中記錄 VCS 原始來源,因此在執行「凍結」時可以恢復這些資訊。
這種變通方法雖然有用,但卻很脆弱,會導致對可編輯模式用途的混淆,且僅在套件能透過 setuptools 安裝時才有效(即無法與其他 PEP 517 建構後端一同使用)。
當本 PEP 實作後,將不再需要為了讓 pip freeze 能正確處理 VCS 參考而使用可編輯安裝。
原理
本 PEP 指定在已安裝套件的 .dist-info 目錄中建立一個新的 direct_url.json 元資料檔案。
所指定的欄位足以重現原始碼壓縮檔及 pip 支援的 VCS URL。這些欄位也足以重現 PEP 440 直接參考,以及 Pipfile 與 Pipfile.lock 條目。最後,它們足以記錄已安裝版本的原始分支、標籤及/或 Git ref,這些資訊對於可編輯安裝而言,由於 VCS 檢出目錄的存在,原本就已經可以取得。
由於目前已存在至少三種編碼此類資訊的方式,本 PEP 使用字典格式,以免對直接 URL 參考最終如何在需求或鎖定檔(lockfile)中編碼做出任何假設。關於此選擇的進一步討論,請參見下方的「替代方案(Alternatives)」章節。
參考 Ruby 的 bundler 手冊以確認其具備類似能力,並據此資訊選定並命名本規範中的欄位。
JSON 格式允許在未來新增額外的欄位。
規範
本 PEP 指定在已安裝套件的 .dist-info 目錄中建立一個 direct_url.json 檔案,以記錄該套件的直接 URL 原始來源。
關於此元資料檔案名稱與語意的官方來源,請參見 記錄已安裝套件的原始直接 URL 文件。
當安裝程式從指定直接 URL 參考(包含 VCS URL)的需求安裝套件時,必須建立此檔案。
當從其他類型的需求(即名稱加版本說明符)安裝套件時,不得建立此檔案。
此 JSON 檔案必須為字典格式,符合 RFC 8259 標準並以 UTF-8 編碼。
若存在此檔案,它必須包含至少兩個欄位。第一個是 url,型態為 string。根據 url 指向的內容,第二個欄位必須是 vcs_info(若 url 為 VCS 參考)、archive_info(若 url 為原始碼壓縮檔或 wheel)或 dir_info(若 url 為本地目錄)其中之一。這些資訊欄位的值為一個(可能為空的)子字典,包含下文定義的可能鍵值。
基於安全考量,url 必須移除任何敏感的驗證資訊。
URL 中的 user:password 部分可以由環境變數組成,需符合以下正規表示式:
\$\{[A-Za-z0-9-_]+\}(:\$\{[A-Za-z0-9-_]+\})?
此外,URL 中的 user:password 部分也可以是眾所皆知、非安全敏感的字串。一個典型的例子是像 ssh://git@gitlab.com 這樣的 URL 中的 git。
當 url 指向 VCS 儲存庫時,必須包含 vcs_info 鍵,並作為包含下列鍵的字典:
- 必須包含一個
vcs鍵(型態為string),包含 VCS 的名稱(例如git,hg,bzr,svn之一)。其他 VCS 應透過撰寫 PEP 來修正本規範以進行註冊。url的值必須與對應的 VCS 相容,以便安裝程式可以直接將其傳遞給 VCS 的檢出/下載指令,無需進行轉換。 - 可以包含一個
requested_revision鍵(型態為string),標註要安裝的分支/標籤/ref/提交雜湊碼/修訂版本等(格式需與該 VCS 相容)。 - 必須包含一個
commit_id鍵(型態為string),包含已安裝的確切提交/修訂版本編號。若該 VCS 支援基於提交雜湊碼的修訂版本識別符,則必須使用該雜湊碼作為commit_id,以引用已安裝原始碼的不可變版本。 - 若安裝程式能發現關於所請求修訂版本的額外資訊,則可以新增
resolved_revision及/或resolved_revision_type欄位。若所請求的 URL 中未提供修訂版本,resolved_revision可包含已安裝的預設分支,而resolved_revision_type將為branch。若安裝程式判定requested_revision為標籤,則可新增值為tag的resolved_revision_type。
當 url 指向原始碼壓縮檔或 wheel 時,必須包含 archive_info 鍵,作為包含下列鍵的字典:
- 應包含一個
hash鍵(型態為string),值為<雜湊演算法>=<預期雜湊值>。建議僅使用標準函式庫hashlib模組最新版本中所無條件提供的雜湊演算法作為原始碼壓縮檔的雜湊值。撰寫本文時,此清單包含 'md5'、'sha1'、'sha224'、'sha256'、'sha384' 及 'sha512'。
當 url 指向本地目錄時,必須包含 dir_info 鍵,作為包含下列鍵的字典:
editable(型態為boolean):若套件以可編輯模式安裝則為true,否則為false。若不存在,預設為false。
當 url 指向本地目錄時,必須使用 file 協定並符合 RFC 8089。特別是,路徑部分必須是絕對路徑。將相對路徑轉為絕對路徑時,應保留符號連結(symbolic links)。
附註
當請求的 URL 使用 file:// 協定且指向一個恰好包含 VCS 檢出資料的本地目錄時,安裝程式不得嘗試推斷任何 VCS 資訊,因此不得在 direct_url.json 中輸出任何與 VCS 相關的資訊(如 vcs_info)。
可以存在頂層的 subdirectory 欄位,包含相對於 VCS 儲存庫、原始碼壓縮檔或本地目錄根目錄的路徑,以指定 pyproject.toml 或 setup.py 的位置。
附註
作為一般原則,安裝程式在產生 direct_url.json 時,應盡可能保留請求 URL 中提供的資訊。例如,user:password 環境變數應被保留,且 requested_revision 應盡可能忠實地反映請求 URL 中提供的修訂版本。不過,這些資訊會透過 commit_id 等更精確的數據進行「豐富」。
已註冊的 VCS
本章節列出已註冊的 VCS;展開了關於如何使用 vcs, requested_revision 以及 vcs_info 中其他欄位的 VCS 特定詳細資訊;在某些情況下還包含額外的 VCS 特定欄位。工具可以支援其他 VCS,但建議透過撰寫 PEP 來修正本規範以進行註冊。vcs 欄位應為指令名稱(小寫)。支援此類 VCS 所需的額外欄位應以 VCS 指令名稱作為前綴。
Git
首頁
vcs 指令
git
vcs 欄位
git
requested_revision 欄位
標籤名稱、分支名稱、Git ref、提交雜湊碼、簡短提交雜湊碼或其他 commit-ish。
commit_id 欄位
提交雜湊碼(40 位十六進位字元 sha1)。
附註
安裝程式可以使用 git show-ref 和 git symbolic-ref 指令來判定 requested_revision 是否對應一個 Git ref。進而,以 refs/tags/ 開頭的 ref 對應一個標籤,而在 clone 後以 refs/remotes/origin/ 開頭的 ref 對應一個分支。
Mercurial
首頁
vcs 指令
hg
vcs 欄位
hg
requested_revision 欄位
標籤名稱、分支名稱、changeset ID、簡短 changeset ID。
commit_id 欄位
changeset ID(40 位十六進位字元)。
Bazaar
首頁
vcs 指令
bzr
vcs 欄位
bzr
requested_revision 欄位
標籤名稱、分支名稱、revision id。
commit_id 欄位
revision id。
Subversion
首頁
vcs 指令
svn
vcs 欄位
svn
requested_revision 欄位
requested_revision必須與svn checkout的--revision選項相容。在 Subversion 中,分支或標籤是url的一部分。
commit_id 欄位
由於 Subversion 不支援全域唯一識別符,此欄位為對應儲存庫中的 Subversion 修訂版編號。
範例
direct_url.json 範例
原始碼壓縮檔
{
"url": "https://github.com/pypa/pip/archive/1.3.1.zip",
"archive_info": {
"hash": "sha256=2dc6b5a470a1bde68946f263f1af1515a2574a150a30d6ce02c6ff742fcc0db8"
}
}
帶有標籤與提交雜湊碼的 Git URL
{
"url": "https://github.com/pypa/pip.git",
"vcs_info": {
"vcs": "git",
"requested_revision": "1.3.1",
"resolved_revision_type": "tag",
"commit_id": "7921be1537eac1e97bc40179a57f0349c2aee67d"
}
}
本地目錄
{
"url": "file:///home/user/project",
"dir_info": {}
}
以可編輯模式安裝的本地目錄
{
"url": "file:///home/user/project",
"dir_info": {
"editable": true
}
}
pip 指令範例及其對 direct_url.json 的影響
產生 direct_url.json 的指令
- pip install https://example.com/app-1.0.tgz
- pip install https://example.com/app-1.0.whl
- pip install “git+https://example.com/repo/app.git#egg=app&subdirectory=setup”
- pip install ./app
- pip install file:///home/user/app
- pip install –editable “git+https://example.com/repo/app.git#egg=app&subdirectory=setup” (在此情況下,
url將會是 git 儲存庫被 clone 到的本地目錄,且dir_info會包含"editable": true,且不會設定vcs_info) - pip install -e ./app
不會產生 direct_url.json 的指令
- pip install app
- pip install app –no-index –find-links https://example.com/
使用案例
「凍結」環境
如pip freeze等從已安裝 Python 套件資料庫產生需求的工具,若direct_url.json存在,則應利用該檔案,並賦予其高於版本元資料的優先權,以產生更高保真度的輸出。在存在vcs直接 URL 參考的情況下,應優先使用commit_id欄位,以對最初安裝的版本提供最高可能的保真度。若需求格式支援,也鼓勵工具輸出tag值(若存在),因其具有不可變的語意。工具可根據使用者的需求選擇其他方法。請注意,本 PEP 的初始版本並未試圖讓包含可編輯安裝或從本地目錄安裝的環境變得可重現,但確實試圖讓它們變得容易識別。透過此規範的
url和dir_info欄位定位本地專案目錄,工具可以實作任何符合其使用場景的策略。
回溯相容性
由於本 PEP 在 .dist-info 目錄中指定了一個新檔案,因此不會對向後相容性產生影響。
替代方案
PEP 426 source_url
現已撤回的 PEP 426 指定了 source_url 元資料條目。它也實作於 distlib 中。
它原本是用於稍微不同的目的,即用於 sdist。
該格式缺乏對 pip 需求 URL 中 subdirectory 選項的支援。同樣的限制也存在於 PEP 440 直接參考 中。
它也缺乏對 URL 中 user:password 部分環境變數 的明確支援。
在 PEP 440 中引入鍵/值擴充機制以及對 URL 中 user:password 環境變數的支援,對於本 PEP 的使用是必要的。
revision 與 ref 的區別
requested_revision 鍵之所以保留而未使用 requested_ref,是因為它在各種 VCS 中是一個更通用的術語,且 ref 對 git 而言具有特定含義。
致謝
許多人協助讓本 PEP 成為現實。Paul F. Moore 提供了摘要的精髓。Alyssa Coghlan 建議了 direct_url 這個名稱。
版權
本文件已進入公有領域或遵循 CC0-1.0-Universal 授權,以較寬鬆者為準。
來源:https://github.com/python/peps/blob/main/peps/pep-0610.rst
最後修改時間:2025-02-01 08:55:40 GMT