PEP 694 – Python 套件索引的 Upload 2.0 API
- 作者:
- Barry Warsaw <barry at python.org>, Donald Stufft <donald at stufft.io>, Ee Durbin <ee at python.org>
- PEP 委託人:
- Dustin Ingram <di at python.org>
- 討論於:
- Discourse 討論串
- 狀態:
- 草案
- 類型:
- 標準軌跡 (Standards Track)
- 主題:
- 套件封裝 (Packaging)
- 建立日期:
- 2022 年 6 月 11 日
- 公告歷史:
- 2022 年 6 月 27 日, 2025 年 1 月 6 日 2025 年 4 月 14 日 2025 年 8 月 6 日 2025 年 9 月 27 日
摘要
本 PEP 提議一個用於將檔案上傳到 Python 套件索引(例如 PyPI)的可擴充 API。除了標準化之外,該上傳 API 還提供了其他實用功能,例如支援:
- 發佈工作階段,可用於同時發佈套件發行版本中的所有 wheel 檔;
- 「暫存」發行版本,可用於在正式發佈前測試上傳,而無需使用 test.pypi.org;
- 在發佈工作階段完成前,可以被覆蓋與更換的產物 (artifacts);
- 產物上傳狀態的詳細資訊;
- 無需上傳產物即可建立新專案;
- 未來無需完整 PEP 即可擴充支援的上傳機制協定;這些可以是標準化且推薦給所有索引的,也可以是索引特定的;
一旦採用此新上傳 API,現有的舊版 API 即可被廢棄,然而本 PEP 並未提出舊版 API 的廢棄時程。
原理
目前尚無用於將檔案上傳到 Python 套件索引(如 PyPI)的標準化 API。相反地,每個人都被迫對現有的「舊版 (legacy)」 API 進行逆向工程。
舊版 API 雖然可用,但洩露了原始 PyPI 程式碼庫的實作細節,這些細節在新的程式碼庫和其他替代實作中都被忠實地複製了。
此外,舊版 API 存在許多重大問題:
- 它是完全同步的,這迫使請求必須在整個上傳過程中保持開啟,並且在索引處理上傳檔案以判斷成功或失敗時也必須保持開啟。
- 它不支援任何並行或續傳機制。由於 PyPI 上最大的預設檔案大小約為 1GB,要求整個上傳成功完成意味著當此類上傳在請求進行中遭遇網路中斷時,頻寬會被浪費。
- 操作的原子單位是單一檔案。當一個發行版本邏輯上包含一個原始碼發行版 (sdist) 和多個二進位 wheel 檔時,這會產生問題,導致使用者如果在其平台的 wheel 檔尚未完全上傳時不幸需要該套件,會獲得不同版本的套件。如果發行版本先上傳 sdist,也可能導致某些使用者僅看到 sdist,進而觸發本地端的原始碼建置。
- 狀態報告非常有限。不支援報告多個錯誤、警告、廢棄訊息等。狀態僅限於 HTTP 狀態碼和原因短語(Reason Phrase),而原因短語自 HTTP/2 (RFC 7540) 起就已被廢棄。
- 發行版本的中繼資料是隨檔案一起提交的。然而,由於這些中繼資料向來不可靠,大多數安裝程式反而選擇下載整個檔案並從中讀取中繼資料。
- 沒有機制允許索引在消耗頻寬進行上傳之前進行任何形式的健全性檢查。許多中繼資料無效或權限不正確的情況都可以在上傳檔案前檢查。
- 不支援在正式發佈到索引之前「暫存」發行版本。
- 建立新專案需要至少上傳一個檔案,這導致出現「佔位」上傳以聲明專案命名空間的情況。
本 PEP 提出的新上傳 API 提供了直接或透過擴充方法解決所有這些問題的方法,允許伺服器實作續傳和並行上傳等功能。此 API 提供了更好的錯誤報告、更穩健的發行測試體驗,以及所有發行產物的原子化且同步的發佈。
舊版 API
以下是舊版 API 的概述。詳細說明請參閱 PyPI 使用者指南文件。
端點 (Endpoint)
現有的上傳 API 位於一個基礎 URL。對於 PyPI,該 URL 目前為 https://upload.pypi.org/legacy/。執行上傳的用戶端透過添加一個值為 file_upload 的 :action URL 參數來指定要呼叫的 API。[1]
舊版 API 還有一個 protocol_version 參數,理論上允許定義新版本的 API。實務上這從未發生過,其值始終為 1。
因此,PyPI 上有效的上傳 API 為:https://upload.pypi.org/legacy/?:action=file_upload&protocol_version=1。
編碼
要提交的資料是以內容類型為 multipart/form-data 的 POST 請求提交的。這反映了舊版 API 的歷史性質,它最初並非設計為 API,而是初始 PyPI 實作上的網頁表單,並編寫用戶端程式碼來以程式化方式提交該表單。
內容
大致上,套件內含的中繼資料會以 content-disposition 為 form-data 的部分提交,中繼資料鍵名即為欄位名稱。這些各種中繼資料的名稱並未記錄在案,它們有時(但並非總是)與套件產物之 METADATA 檔案中使用的名稱相符。大小寫鮮少匹配,且 form-data 到 METADATA 的轉換並不一致。
上傳產物檔案本身作為名為 content 的 application/octet-stream 部分發送,如果有附加 PGP 簽署,則會作為名為 gpg_signature 的 application/octet-stream 部分包含在內。
驗證
上傳驗證也尚未標準化。
PyPI 使用 HTTP 基本驗證(HTTP Basic Authentication),以 API 權杖作為密碼,用戶名為 __token__。可信發佈者 (Trusted Publishers) 透過 OpenID Connect 進行驗證,並接收以相同方式使用的短期 API 權杖。
Upload 2.0 API 規範
本 PEP 將現有 API 的大部分問題根源歸結為兩點:
- 中繼資料是隨檔案一起提交的,而不是從檔案本身解析出來的。[2]
- 它僅支援單一請求,僅使用表單資料,結果不是成功就是失敗,所有動作在該單一請求中都是原子性的。
為解決這些問題,本 PEP 提出了一個多請求工作流程,高層級步驟如下:
- 起始一個發佈工作階段,建立發行暫存區。
- 作為發佈工作階段的一部分,起始指向該暫存區的檔案上傳工作階段。
- 在用戶端與伺服器之間協商要使用的特定檔案上傳機制。
- 使用協商好的機制執行檔案上傳工作階段的檔案上傳。
- 完成檔案上傳工作階段,將其標記為已完成或已取消。
- 完成發佈工作階段,發佈或捨棄暫存內容。
- (選用)檢查發佈工作階段的狀態。
版本控制
本 PEP 使用與 PEP 691 相同的 MAJOR.MINOR 版本控制系統,但除此之外是獨立版本控制的。舊版 API 被本 PEP 視為版本 1.0,但本 PEP 不會以任何方式修改舊版 API。
因此,本 PEP 提出的 API 版本號為 2.0。
Upload API 的主版本號與次版本號 必須 (MUST) 僅能透過 PEP 程序更改。索引營運者與實作者 不得 (MUST NOT) 在未經批准的 PEP 情況下宣傳或實作新的 API 版本。這確保了所有實作之間的一致性,並防止生態系統碎片化。
內容類型 (Content Types)
與 PEP 691 類似,本 PEP 提議來自此上傳 API 的所有請求與回應都應具有標準內容類型,用以描述內容為何、代表 API 的哪個版本以及使用了哪種序列化格式。
此標準請求內容類型適用於所有請求,除了執行檔案上傳機制的請求,後者將由該機制的說明文件指定。
所有其他請求的 Content-Type 標頭結構為:
application/vnd.pypi.upload.$version+$format
由於次要 API 版本差異不應具有破壞性,因此內容類型中僅包含主要版本;版本號前綴為 v。
用戶端請求之 .meta.api-version JSON 鍵中指定的主要 API 版本 必須 (MUST) 與 Content-Type 標頭中的主要版本匹配。
與 PEP 691 不同,本 PEP 不會以任何方式更改現有的 舊版 1.0 上傳 API,因此伺服器必須將本 PEP 描述的新 API 託管在與現有上傳 API 不同的端點。
由於 JSON 是本 PEP 定義的唯一請求格式,本 PEP 定義的所有非檔案上傳請求 必須 (MUST) 包含以下值的 Content-Type 標頭:
application/vnd.pypi.upload.v2+json.
與 PEP 691 類似,本 PEP 也規範使用伺服器驅動的內容協商,允許用戶端請求不同的版本或序列化格式(包括內容類型的 format 部分)。然而,由於本 PEP 預期現有的舊版 1.0 上傳 API 存在於不同的端點,且本 PEP 目前僅提供 JSON 序列化,因此此機制並非特別有用。用戶端只有單一版本和序列化可以請求。儘管如此,用戶端 應該 (SHOULD) 準備好優雅地處理內容協商,以應對未來可能加入的其他格式或版本。
伺服器 不得 (MUST NOT) 宣傳支援超出已批准 PEP 定義的 API 版本。任何新版本或格式都需要透過新的 PEP 進行標準化。
除非另有說明,否則本文檔中的所有 HTTP 請求與回應均假設包含 HTTP 標頭
Content-Type: application/vnd.pypi.upload.v2+json
根端點
此處描述的所有 URL 均相對於「根端點」,該端點可以位於網域 URL 結構中的任何位置。例如,根端點可以是 https://upload.example.com/ 或 https://example.com/upload/。
根端點的選擇由索引營運者決定。
Upload 2.0 API 的驗證
本規範中的所有端點 必須 (MUST) 使用 RFC 7235 定義的標準 HTTP 驗證機制。
驗證遵循標準 HTTP 模式:
- 需要驗證時,伺服器使用
WWW-Authenticate回應標頭 - 用戶端透過
Authorization請求標頭提供憑據 401 Unauthorized表示缺失或無效的驗證403 Forbidden表示權限不足
具體的驗證方案(例如 Bearer、Basic、Digest)由索引營運者決定。
錯誤
用戶端通常應準備好處理 HTTP 回應錯誤狀態碼,其 可能 (MAY) 包含以下格式的負載:
{
"meta": {
"api-version": "2.0"
},
"message": "...",
"errors": [
{
"source": "...",
"message": "..."
}
]
}
除了標準的 meta 鍵外,這還有以下頂層鍵:
message- 封裝了此請求中可能發生的所有錯誤的單一訊息。
errors- 特定錯誤的陣列,每個錯誤包含一個
source鍵(指出錯誤來源的字串)和一個該特定錯誤的message鍵。
message 和 source 字串沒有特定的程式意義,旨在供人類解釋以協助診斷底層問題。
某些回應可能會傳回如下文所述的更具體的 HTTP 狀態碼。
發佈工作階段 (Publishing Session)
建立發佈工作階段
發行流程始於建立新的發佈工作階段。要建立工作階段,用戶端向根 URL 提交一個 POST 請求,如下所示:
{
"meta": {
"api-version": "2.0"
},
"name": "foo",
"version": "1.0",
}
請求包含以下頂層鍵:
meta(必要)- 描述負載本身的資訊。目前唯一定義的子鍵是
api-version,其值必須為字串"2.0"。選用的子鍵可以定義索引特定行為。 name(必要)- 此工作階段嘗試發佈新版本的專案名稱。名稱 必須 (MUST) 符合標準套件名稱格式,且伺服器 必須 (MUST) 對名稱進行正規化。
version(必要)- 此工作階段嘗試添加檔案的專案版本。版本字串 必須 (MUST) 符合套件版本規範。
成功建立工作階段後,伺服器會傳回 201 Created 回應。回應 必須 (MUST) 包含一個 Location 標頭,其中包含與回應主體中 links.session 鍵相同的 URL。
如果是為先前無發行記錄的專案建立工作階段,索引 可以 (MAY) 在發佈工作階段之前預留專案名稱,但 直到 暫存內容發佈前,不得 (MUST NOT) 透過「常規」(即非暫存)存取協定導航到該專案。如果此首發暫存區被取消,索引 應該 (SHOULD) 刪除該專案記錄,視同從未上傳過。
工作階段由建立它的使用者擁有,所有後續請求 必須 (MUST) 使用相同的憑據執行,否則後續請求將傳回 403 Forbidden。
選用的索引特定中繼資料
索引可以選擇為索引特定行為定義自己的中繼資料。中繼資料鍵 必須 (MUST) 以底線開頭,其後的值應能輕鬆且唯一地識別該索引。例如,PyPI 可以透過使用以下索引特定中繼資料區段,允許在發佈者所屬的組織帳號中建立專案:
{
"meta": {
"api-version": "2.0",
"_pypi.org": {
"organization": "my-main-org"
}
},
"name": "foo",
"version": "1.0",
}
這僅是一個範例。本 PEP 並未定義或保留任何索引特定的鍵或中繼資料;這留給索引自行指定與記錄。索引特定中繼資料的語義(例如:錯誤的鍵或值是否會導致錯誤或被忽略)在此亦不作定義。
回應主體
成功的請求回應包含以下內容:
{
"meta": {
"api-version": "2.0"
},
"links": {
"stage": "...",
"upload": "...",
"session": "...",
},
"mechanisms": ["http-post-bytes"],
"session-token": "<token-string>",
"expires-at": "2025-08-01T12:00:00Z",
"status": "pending",
"files": {},
"notices": [
"a notice to display to the user"
]
}
除了與請求 JSON 格式相同的 meta 鍵外,成功回應還有以下鍵:
links- 將鍵映射到與此工作階段相關之 URL 的字典,詳情如下。
mechanisms- 伺服器支援的檔案上傳機制列表,按伺服器偏好順序排序。至少需要一個值。
session-token- 如果索引支援預覽暫存發行版本,此鍵將包含唯一的「工作階段權杖」,可提供給安裝程式以便在發佈前預覽暫存發行版本。此權杖 必須 (MUST) 在加密上是不可推測的。如果索引 不 支援暫存預覽,則 必須 (MUST) 省略此鍵。
expires-at- 符合 RFC 3339 格式的時間戳記字串;此字串 必須 (MUST) 使用「Zulu」(即
Z)標記表示 UTC 時間戳記,且僅使用整數秒(即無秒小數部分)。此時間戳記代表伺服器何時將使此工作階段過期,進而使其所有內容(包括任何已上傳的檔案以及與工作階段相關的 URL 連結)失效。除非用戶端自行取消或發佈工作階段,否則工作階段 應該 (SHOULD) 至少在該時間前保持活動。伺服器 可以 (MAY) 選擇延長此過期時間,但絕不應提前。用戶端可以查詢工作階段狀態以獲取工作階段當前的過期時間。 status- 一個包含
pending(待處理)、published(已發佈)、error(錯誤) 或canceled(已取消) 之一的字串,代表工作階段的整體狀態。 files- 將已上傳至此工作階段的檔名映射到包含此工作階段中引用檔案詳細資訊之字典的對應表。
notices- 一個選用鍵,指向伺服器希望傳達給終端使用者的、人類可讀的資訊通知陣列。這些通知針對整體工作階段,而非工作階段中的任何特定檔案。
多個工作階段建立請求
如果在同一個名稱-版本對已有工作階段處於 pending、processing 或 complete 狀態時,收到第二次建立工作階段的嘗試,則 不會 建立新工作階段。相反地,伺服器 必須 (MUST) 回應 409 Conflict,且 必須 (MUST) 包含指向工作階段狀態 URL 的 Location 標頭。
對於處於 error 或 canceled 狀態的工作階段,會以相同的 201 Created 回應和負載建立新工作階段,但發佈工作階段狀態 URL、session-token 和 links.stage 的值 必須 (MUST) 不同。
發佈工作階段連結
對於成功 JSON 中的 links 鍵,以下子鍵是有效的:
session- 執行此工作階段操作的端點,包括發佈此工作階段、取消並捨棄工作階段、查詢目前工作階段狀態,以及(如果伺服器支援的話)請求延長工作階段存留期。
upload- 工作階段用戶端將用於為要包含在此工作階段中的每個檔案啟動檔案上傳工作階段的端點。
stage- 在發佈工作階段之前可以預覽此暫存發行版本的端點。這可用於下載並驗證尚未公開的檔案。此 URL 必須 (MUST) 在加密上不可推測,且 必須 (MUST) 使用上述
session-token來達成此目的。此stageURL 應能使用session-token輕鬆計算,但該 URL 的確切格式由索引特定。如果索引不支援預覽暫存發行版本,則 必須 (MUST) 省略此鍵。
發佈工作階段檔案
files 鍵包含從此工作階段中上傳的檔案名稱到具有以下鍵之子映射的映射:
status- 有效值為
pending、processing、complete、error和canceled的字串。如果上傳過程中發生錯誤,則用戶端不應假設檔案處於任何可用狀態,將會傳回error,最好取消或刪除該檔案並重新開始。此操作將從工作階段狀態回應主體的files鍵中移除該檔案名稱。 link- 用戶端應用於引用此特定檔案的 絕對 URL。此 URL 用於檢索、更換或刪除引用的檔案。如果支援暫存預覽,此 URL 必須 (MUST) 在加密上不可推測,且 必須 (MUST) 使用相同的發佈工作階段權杖來確保此約束。URL 的確切格式由索引決定,但 應該 (SHOULD) 記錄在案。
notices- 一個選用鍵,其格式與語義與
notices工作階段鍵類似,但這些通知針對引用的檔案。
完成發佈工作階段
要完成工作階段並發佈包含在其中的檔案,用戶端向工作階段建立回應主體中提供的 session 連結發送一個 POST 請求。
請求如下所示:
{
"meta": {
"api-version": "2.0"
},
"action": "publish",
}
如果伺服器能夠立即完成發佈工作階段,則可以這樣做並傳回 201 Created 回應。如果無法立即完成(例如:如果需要進行可能超過單一 HTTP 請求合理時間的驗證),則可以傳回 202 Accepted 回應。
伺服器 必須 (MUST) 在回應中包含指向發佈工作階段狀態 URL 的 Location 標頭,該 URL 可用於查詢當前工作階段狀態。如果伺服器傳回 202 Accepted,則可以透過輪詢該 URL 來監控工作階段狀態的變化。
取消發佈工作階段
要取消發佈工作階段,用戶端向工作階段建立回應主體中提供的 session 連結發送一個 DELETE 請求。伺服器隨後將工作階段標記為已取消,且 應該 (SHOULD) 清除作為該工作階段一部分上傳的所有資料。未來任何存取該工作階段 URL 或任何發佈工作階段相關 URL 的嘗試 必須 (MUST) 傳回 404 Not Found。
為了防止出現懸置工作階段,伺服器也可以選擇自行取消超時的工作階段。建議伺服器在不短於一週的時間後清除工作階段,但每個伺服器可以選擇自己的排程。伺服器 可以 (MAY) 支援用戶端引導的工作階段延長。
發佈工作階段狀態
用戶端隨時可以透過向 links.session 中提供的 URL(亦提供於工作階段建立回應的 Location 標頭中)發送 GET 請求來查詢工作階段狀態。
伺服器將以與初始建立發佈工作階段時相同的發佈工作階段建立回應來回應此 GET 請求,但反映了 status、expires-at 或 files 的任何變化。
延長發佈工作階段
伺服器 可以 (MAY) 允許用戶端延長工作階段,但總存留期和允許的延長次數由伺服器決定。要延長工作階段,用戶端向 links.session URL(同上,亦為 Location 標頭)發送 POST 請求。
請求如下所示:
{
"meta": {
"api-version": "2.0"
},
"action": "extend",
"extend-for": 3600
}
指定的秒數僅是用戶端對伺服器關於延長目前工作階段額外秒數的建議。例如,如果用戶端希望將目前工作階段再延長一小時,extend-for 將為 3600。成功延長後,伺服器將以與初始建立發佈工作階段時相同的發佈工作階段建立回應主體進行回應,但反映了 status、expires-at 或 files 的任何變化。
如果伺服器拒絕延長所請求的秒數,它 必須 (MUST) 仍然傳回成功回應,且 expires-at 鍵將僅反映工作階段當前的過期時間。
發佈工作階段權杖 (Token)
索引 應該 (SHOULD) 支援暫存預覽,以便在發佈前對上傳的檔案進行實機測試。例如:CI 用戶端可以使用預發佈的 wheel 檔執行安裝測試,以確保其新版本在公開發佈前能如預期運作。
索引透過在其發佈工作階段建立的回應中傳回兩項關鍵資訊來宣傳其對暫存預覽的支援。不支援暫存預覽的索引 不得 (MUST NOT) 在其回應中包含這些內容。
session-token 是一個短權杖,如果安裝工具想要透過命令列參數支援暫存預覽(例如:$TOOL install --staging $SESSION_TOKEN),則此權杖可作為一種便捷的使用者體驗。links.stage 鍵給出暫存區的完整 URL,可用於 CLI 中(例如:pip install --extra-index-url $STAGE_URL)。工作階段權杖和 URL 必須 (MUST) 在加密上不可推測,但產生權杖的演算法由索引自行決定。暫存區 URL 必須 (MUST) 能從工作階段權杖中計算出來(使用索引記錄的格式),但 URL 的確切格式也留給索引決定。
檔案上傳工作階段
建立檔案上傳工作階段
建立發佈工作階段後,回應之工作階段連結映射中的 upload 端點將用於開始將新檔案上傳到該工作階段。用戶端 必須 (MUST) 使用提供的 upload URL,且 不得 (MUST NOT) 假設各個工作階段之間的這些 URL 具有任何規律或共通性。
要起始檔案上傳,用戶端首先向 upload URL 發送一個 POST 請求。請求如下所示:
{
"meta": {
"api-version": "2.0"
},
"filename": "foo-1.0.tar.gz",
"size": 1000,
"hashes": {"sha256": "...", "blake2b": "..."},
"metadata": "...",
"mechanism": "http-post-bytes"
}
除了標準的 meta 鍵外,請求 JSON 還有以下額外鍵:
filename(必要)- 正在上傳的檔案名稱。檔名 必須 (MUST) 符合原始碼發行版檔名規範或二進位發行版檔名慣例。索引 應該 (SHOULD) 在請求時驗證這些檔名,若檔名不符合規範,應傳回 Errors 節中描述的
400 Bad Request錯誤碼。 size(必要)- 正在上傳的檔案大小(以位元組為單位)。
hashes(必要)- 雜湊名稱與十六進位編碼摘要的映射。這些摘要分別是正在上傳的檔案經由名稱所識別的演算法雜湊後的檢查碼。
預設情況下,hashlib 中可用的任何雜湊演算法都可用作雜湊字典的鍵 [3]。必須 (MUST) 始終包含至少一種來自
hashlib.algorithms_guaranteed的安全演算法。本 PEP 特別推薦sha256。可以一次傳遞多個雜湊,但提供的所有雜湊 必須 (MUST) 對該檔案有效。
mechanism(必要)- 用戶端打算用於此檔案的檔案上傳機制。此機制 應該 (SHOULD) 從發佈工作階段建立回應主體中宣傳的機制列表中選擇。如果伺服器營運者記錄了可供「預發佈」使用的新的或即將推出的機制,用戶端 可以 (MAY) 發送未宣傳的機制。
metadata(選用)- 如果提供,這是一個包含檔案核心中繼資料的字串值。
伺服器 可以 (MAY) 使用此請求中提供的資料在允許上傳檔案之前進行一些健全性檢查。這些檢查可能包括但不限於:
- 檢查
filename是否已存在於已發佈的發行版本中; - 檢查
size是否會超出任何專案或檔案配額; - 檢查所提供的
metadata內容是否有效。
如果伺服器決定應繼續上傳,它將傳回 202 Accepted 回應,並附帶以下回應主體。發佈工作階段的狀態也將在 files 映射中包含該檔名。如果伺服器因為不支援用戶端提供的 mechanism 而無法繼續上傳,則 必須 (MUST) 傳回 422 Unprocessable Content。伺服器 可以 (MAY) 允許並行上傳檔案,但非必要。如果伺服器判斷上傳無法繼續,則 必須 (MUST) 傳回 409 Conflict。
回應主體
成功的回應包含以下內容:
{
"meta": {
"api-version": "2.0"
},
"links": {
"file-upload-session": "..."
},
"status": "pending",
"expires-at": "2025-08-01T13:00:00Z",
"mechanism": {
"identifier": "http-post-bytes",
"file_url": "...",
"attestations_url": "..."
}
}
必須 (MUST) 存在 Retry-After 回應標頭,以向用戶端指示何時應下次輪詢更新後的狀態。
除了與請求 JSON 格式相同的 meta 鍵外,成功回應還有以下鍵:
links- 將鍵映射到與此工作階段相關之 URL 的字典,詳情如下。
status- 值為
pending、processing、complete、error和canceled的字串,表示檔案上傳工作階段的當前狀態。 expires-at- 符合 RFC 3339 格式的時間戳記字串,代表伺服器何時將使此檔案上傳工作階段過期。此字串 必須 (MUST) 使用「Zulu」(即
Z)標記表示 UTC 時間戳記,且僅使用整數秒(即無秒小數部分)。除非用戶端取消或完成工作階段,否則工作階段 應該 (SHOULD) 至少在此時間前保持活動。伺服器 可以 (MAY) 選擇延長此過期時間,但絕不應提前。 mechanism- 包含由用戶端和伺服器協商之支援機制所需詳細資訊的映射。此映射 必須 (MUST) 包含一個
identifier鍵,該鍵映射到所選檔案上傳機制的識別碼字串。
檔案上傳工作階段連結
對於回應負載中的 links 鍵,以下子鍵是有效的:
file-upload-session- 執行此檔案上傳工作階段操作的端點。包括完成檔案上傳工作階段、取消並捨棄檔案上傳工作階段、查詢當前檔案上傳工作階段狀態,以及(如果伺服器支援的話)請求延長檔案上傳工作階段存留期。
完成檔案上傳工作階段
要完成檔案上傳工作階段(這表示檔案上傳機制已執行且未產生錯誤),用戶端向檔案上傳工作階段建立回應主體中的 file-upload-session 連結發送一個 POST。
請求如下所示:
{
"meta": {
"api-version": "2.0"
},
"action": "complete",
}
如果伺服器能夠立即完成檔案上傳工作階段,則可以這樣做並傳回 201 Created 回應,並將檔案上傳工作階段的狀態設置為 complete。如果無法立即完成(例如:如果需要進行可能超過單一 HTTP 請求合理時間的驗證),則可以傳回 202 Accepted 回應,並將檔案上傳工作階段的狀態設置為 processing。
在任何一種情況下,伺服器都應包含指向檔案上傳工作階段狀態 URL 的 Location 標頭。
伺服器 必須 (MUST) 允許用戶端輪詢檔案上傳工作階段狀態 URL 以監控狀態變化。如果伺服器以 202 Accepted 回應,用戶端可以輪詢檔案上傳工作階段狀態 URL 以監控狀態變化。用戶端 應該 (SHOULD) 遵守檔案上傳工作階段狀態回應的 Retry-After 標頭值。
取消與刪除
用戶端可以取消進行中的檔案上傳工作階段,或刪除已完全上傳的檔案。在兩種情況下,用戶端均透過向其想要刪除之檔案的檔案上傳工作階段建立回應中的 links.file-upload-session URL 發送 DELETE 請求來執行此操作。
成功的刪除請求 必須 (MUST) 回應 204 No Content。
一旦取消或刪除,用戶端 不得 (MUST NOT) 假設之前的檔案上傳工作階段資源或相關的檔案上傳機制可以重複使用。
更換部分或完全上傳的檔案
要更換工作階段檔案,檔案上傳 必須 (MUST) 是先前已完成、取消或刪除的。如果該檔案的上傳正在進行中,則無法更換檔案。
要更換工作階段檔案,用戶端應先取消並刪除進行中的上傳。在此之後,可以透過重新開始整個檔案上傳序列來啟動新的檔案上傳。這意味著再次提供中繼資料請求以檢索新的上傳資源 URL。用戶端 不得 (MUST NOT) 假設刪除後可以重複使用之前的上傳資源 URL。
檔案上傳工作階段狀態
用戶端可以透過向檔案上傳工作階段建立回應中的 links.file-upload-session URL 發送 GET 請求來查詢檔案上傳工作階段狀態。伺服器以與檔案上傳工作階段建立回應相同的負載回應此請求,但反映了 status 和 expires-at 的任何變化。
延長檔案上傳工作階段
伺服器 可以 (MAY) 允許用戶端延長檔案上傳工作階段,但總存留期和允許的延長次數由伺服器決定。要延長檔案上傳工作階段,用戶端向檔案上傳工作階段建立回應中的 links.file-upload-session URL 發送 POST 請求。
請求如下所示:
{
"meta": {
"api-version": "2.0"
},
"action": "extend",
"extend-for": 3600
}
指定的秒數僅是用戶端對伺服器關於延長當前檔案上傳工作階段額外秒數的建議。例如,如果用戶端希望將工作階段再延長一小時,extend-for 將為 3600。成功延長後,伺服器將以與初始建立發佈工作階段時相同的檔案上傳工作階段建立回應主體進行回應,但反映了 status 或 expires-at 的任何變化。
如果伺服器拒絕延長所請求的秒數,它 必須 (MUST) 仍然傳回成功回應,且 expires-at 鍵將僅反映工作階段當前的過期時間。
暫存預覽 (Stage Previews)
在發佈前預覽暫存發行版本的能力是本 PEP 的一項重要功能,它能在發行版本公開前實現更進一步的最終測試。索引 可以 (MAY) 透過建立發佈工作階段時傳回之 links 鍵中 stage 子鍵提供的 URL 來提供此功能。透過將 –extra-index-url 標記設置為此值,可以將 stage URL 傳遞給安裝程式(如 pip)。甚至可以透過多次重複此標記並賦予不同值來預覽多個暫存區。
如果受支援,索引將傳回向安裝工具公開暫存發行版本的視圖,使其可以下載並安裝到為最終測試建置的虛擬環境中。此選項允許現有安裝程式在無需對安裝工具進行任何更改的情況下預覽暫存發行版本。此使用者體驗的細節由安裝工具維護者決定。
檔案上傳機制
伺服器 必須 (MUST) 實作必要檔案上傳機制。如果不存在伺服器特定的實作,此類機制將作為後備方案。
上傳 API 的每個主要版本 必須 (MUST) 指定至少一種必要檔案上傳機制。
在未更新主要版本的情況下,不得 (MUST NOT) 新增必要機制,也 不得 (MUST NOT) 移除現有的必要機制。新增或移除任何伺服器特定或實驗性機制 不得 (MUST NOT) 更改本規範的主要或次要版本號。
必要檔案上傳機制
http-post-bytes
符合 Upload API 版本 2.0 的伺服器 必須 (MUST) 支援 http-post-bytes 機制。
此機制 必須 (MUST) 使用與 Upload 2.0 協定其餘端點相同的驗證方案。
用戶端透過向檔案上傳工作階段建立回應主體中 mechanism 映射之 http-post-bytes 映射傳回的 file_url 提交 POST 請求來執行此機制,如下所示:
Content-Type: application/octet-stream
<binary contents of the file to upload>
伺服器 可以 (MAY) 支援上傳檔案的數位證明(Digital Attestations)(參見 PEP 740)。此支援將透過在檔案上傳工作階段建立回應主體中 mechanism 映射之 http-post-bytes 映射內包含 attestations_url 鍵來指示。數位證明 必須 (MUST) 在檔案上傳工作階段完成之前上傳到 attestations_url。
要上傳數位證明,用戶端向 attestations_url 提交一個包含 數位證明物件 (attestation objects) 之 JSON 陣列的 POST 請求,如下所示:
Content-Type: application/json
[{"version": 1, "verification_material": {...}, "envelope": {...}},...]
伺服器特定檔案上傳機制
特定伺服器 可以 (MAY) 實作任意數量的伺服器特定機制,並負責記錄其用法。
伺服器特定實作的檔案上傳機制識別碼由三個部分組成:
<prefix>-<operator identifier>-<implementation identifier>
伺服器特定實作 必須 (MUST) 使用 vnd 作為其 prefix。operator identifier 應該 (SHOULD) 清楚地識別伺服器營運者,與其他知名索引區分開來,且僅包含英數字元 [a-z0-9]。implementation identifier 應該 (SHOULD) 簡明地描述底層實作,且僅包含英數字元 [a-z0-9] 和 -。
當伺服器營運者需要對其上傳機制進行重大變更時,應該 (SHOULD) 建立新的機制識別碼,而不是修改現有的。推薦的模式是在實作識別碼後附加版本後綴,如 -v1、-v2 等。這允許用戶端明確選擇使用新版本,同時保持與現有用戶端的向後相容性。
例如
| 檔案上傳機制字串 | 伺服器營運者 | 機制描述 |
|---|---|---|
vnd-pypi-s3multipart-presigned |
PyPI | 透過預簽署 URL 進行 S3 分段上傳 (S3 multipart upload) |
vnd-pypi-s3multipart-presigned-v2 |
PyPI | 透過預簽署 URL 進行 S3 分段上傳版本 2 |
vnd-pypi-http-fetch |
PyPI | 透過指示伺服器經由 HTTP 請求從 URL 抓取檔案來交付 |
vnd-acmecorp-http-fetch |
Acme Corp | 透過指示伺服器經由 HTTP 請求從 URL 抓取檔案來交付 |
vnd-acmecorp-postal |
Acme Corp | 透過郵政快遞交付檔案 |
vnd-widgetinc-stream-v1 |
Widget Inc. | 串流上傳協定版本 1 |
vnd-widgetinc-stream-v2 |
Widget Inc. | 串流上傳協定版本 2 |
vnd-madscience-quantumentanglement |
Mad Science Labs | 透過量子糾纏上傳 |
如果伺服器打算精確匹配另一個伺服器實作的行為,則 可以 (MAY) 回應該實作的檔案上傳機制名稱。
常見問題 (FAQ)
這是否意味著 PyPI 計劃停止支援現有的上傳 API?
目前 PyPI 沒有任何停止支援現有上傳 API 的具體計劃。
與 PEP 691 不同,這樣做有顯著好處,因此舊版上傳 API 很可能在未來某個時間點被(負責任地)廢棄並移除。此類未來的廢棄計劃明確超出了 本 PEP 的討論範圍。
我可以使用 upload 2.0 API 來保留專案名稱嗎?
可以!如果您還沒準備好透過上傳檔案來發佈版本,您仍然可以預留專案名稱(當然前提是該名稱尚未存在)。
為此,請先建立一個新的發佈工作階段,然後在不上傳任何檔案的情況下發佈該工作階段。雖然建立工作階段請求的 JSON 主體中需要 version 鍵,但您可以簡單地使用預留位置版本號(如 "0.0.0a0")。如果沒有上傳產物,版本資訊將被忽略。
通常建立工作階段的使用者將成為新專案的所有者,但是索引可以定義索引特定中繼資料,例如允許發佈者所屬的組織擁有該新專案。
開放性問題
Upload 2.0 協定的擴充
在本 PEP 審核期間討論了諸如用於上傳處理完成的非同步 Webhook 通知等功能。討論了上傳協定功能擴充的概念,這將允許實作者宣傳對選用功能(如非同步通知或 Webhook)的支援。
由於設計此類擴充協定的複雜性,以及確保其在推廣 Upload 2.0 時不會導致生態系統過度碎片化,此想法暫時擱置。
隨著 Upload 2.0 營運經驗的累積,上傳協定的未來修訂版本應探索此類擴充功能。
附註
變更歷史
- 2025 年 9 月 23 日
- 移除
nonce和gentoken()演算法。現在索引負責產生加密安全的發佈工作階段權杖和混淆的暫存區 URL(前提是其支援暫存預覽)。 - 澄清收到多個工作階段建立請求時的語義。
- 澄清發佈工作階段步驟,如狀態輪詢與工作階段延長。
- 要求
name符合正規化規則,並包含連結。 - 要求
version符合版本規範,並包含連結。 - 要求
filename符合原始碼或二進位發行版檔名慣例,並包含連結。 - 引用 RFC 3399 而非 ISO 8601 作為時間戳記規範。RFC 是一個更簡單的格式,是 ISO 標準的子集,更適合我們的使用場景。
- 其他協定澄清。
- 新增選用的索引特定中繼資料鍵。
- 移除
- 2025 年 8 月 6 日
- 增加 Dustin 為 PEP 委託人。
- 2025 年 4 月 14 日
- 基於 PyCon US 討論進行更新。
- 在先前說明不足之處添加了一些錯誤返回碼描述。
- 合併取消與刪除上傳檔案章節。
- 簡化更換已暫存但尚未發佈檔案的規則。
- 增加關於推遲暫存預覽的開放性問題。
- 修正一些拼寫錯誤和措辭不當之處。
- 2025 年 1 月 6 日
- 恢復並更新本 PEP。
- 增加 Barry 為共同作者。
- 將術語統一為「stage」(暫存區)而非「draft」(草案)。
- 建議 PyPI 的根 URL 為 https://upload.pypi.org/2.0
- 為工作階段混淆新增選用的
nonce鍵。 - 標準化 JSON 鍵並使其與術語一致。
- 新增與修改多個 API,填補空白並詳述細節。
- 使上傳協定與網際網路標準草案對齊。
版權
本文件已進入公有領域或遵循 CC0-1.0-Universal 授權,以較寬鬆者為準。
來源:https://github.com/python/peps/blob/main/peps/pep-0694.rst
最後修改日期:2025-09-28 21:11:14 GMT