|
1 | 1 | PEP: 694 |
2 | 2 | Title: Upload 2.0 API for Python Package Indexes |
3 | | -Author: Barry Warsaw <barry@python.org>, Donald Stufft <donald@stufft.io>, Ee Durbin <ee@python.org> |
| 3 | +Author: Barry Warsaw <barry@python.org>, Donald Stufft <donald@stufft.io>, Ee Durbin <ee@python.org>, Cary Hawkins <hawkinscary23@gmail.com> |
4 | 4 | PEP-Delegate: Dustin Ingram <di@python.org> |
5 | 5 | Discussions-To: https://discuss.python.org/t/pep-694-pypi-upload-api-2-0-round-2/101483 |
6 | 6 | Status: Draft |
@@ -29,6 +29,9 @@ Along with standardization, the upload API provides additional useful features s |
29 | 29 | * "staging" a release, which can be used to test uploads before publicly publishing them, |
30 | 30 | without the need for `test.pypi.org <https://test.pypi.org/>`__; |
31 | 31 |
|
| 32 | +* entering the publishing session workflow from the existing legacy upload API, so that staging is |
| 33 | + available to publishers before their tooling adopts this API; |
| 34 | + |
32 | 35 | * artifacts which can be overwritten and replaced, until a session is published; |
33 | 36 |
|
34 | 37 | * detailed status on the state of artifact uploads; |
@@ -285,6 +288,14 @@ The unguessable :ref:`stage preview URL <staged-preview>` is a separate capabili |
285 | 288 | governed by this authorization check; it grants read-only preview access to any client that holds the token, |
286 | 289 | so that (for example) a CI job can install-test a staged release without project upload credentials. |
287 | 290 |
|
| 291 | +As one such stricter policy, an index **MAY** require *additional* authorization, beyond upload permission, to |
| 292 | +:ref:`publish <publishing-session-completion>` or :ref:`cancel <publishing-session-cancellation>` a session, |
| 293 | +while still allowing session creation and file upload with upload permission alone. This lets a publisher |
| 294 | +separate duties: an automated system can be given a credential that can create a session and upload files to |
| 295 | +it but cannot publish it, while the authorization to publish is held elsewhere, so that compromise of the |
| 296 | +automated system alone does not result in a published release. How such an additional authorization is |
| 297 | +expressed, and whether it is offered at all, is determined by the index operator. |
| 298 | + |
288 | 299 |
|
289 | 300 | .. _session-errors: |
290 | 301 |
|
@@ -743,6 +754,20 @@ change: deferred processing resolves to either ``published`` on success or ``err |
743 | 754 | resolves to ``error``, the session remains editable and the reason is reported in the session's ``notices``, |
744 | 755 | as described in :ref:`publishing-session-states`. |
745 | 756 |
|
| 757 | +The ``processing`` state **MAY** be used to run asynchronous review of a session's files before it is |
| 758 | +published, such as malware scanning. If the review completes without an adverse result, the session resolves |
| 759 | +to ``published`` as normal. If the review does not complete within a period chosen by the index, the server |
| 760 | +**MAY** treat it as though it had completed without an adverse result and publish the session, so that a |
| 761 | +backlogged or unavailable review system does not indefinitely prevent publication. If the review produces an |
| 762 | +adverse result, the session resolves to ``error`` with the reason reported in the session's ``notices``; the |
| 763 | +server **MAY** decline to publish such a session on any subsequent retry, in which case it is eventually |
| 764 | +:ref:`canceled <publishing-session-cancellation>` and its data discarded. Where a session has resolved to |
| 765 | +``error`` because of an adverse result, an index **MAY** provide a way for the publisher to request that the |
| 766 | +result be re-examined. Such a re-examination **SHOULD** be performed by a human, to confirm whether the |
| 767 | +adverse result was a false positive; if it was, the index **SHOULD** allow the session to be published. The |
| 768 | +review itself, including which checks run, how long they are allowed to take, and how such requests are |
| 769 | +handled, is determined by the index operator and is out of scope for this specification. |
| 770 | + |
746 | 771 | A publish attempt that fails *synchronously* (i.e. within the publish request itself) is returned to the |
747 | 772 | client as an :ref:`error response <session-errors>` and leaves the session in its current editable state; it |
748 | 773 | does **not** move the session to ``error``. |
@@ -1348,6 +1373,42 @@ changes to the installer tool required. |
1348 | 1373 | The details of this user experience are left to installer tool maintainers. |
1349 | 1374 |
|
1350 | 1375 |
|
| 1376 | +.. _legacy-interop: |
| 1377 | + |
| 1378 | +Legacy Upload API Interoperability |
| 1379 | +---------------------------------- |
| 1380 | + |
| 1381 | +Publishers cannot use the features of this API until their upload tooling adopts it, and the legacy API is |
| 1382 | +expected to remain available for a long time. To make staging available to those publishers sooner, an index |
| 1383 | +**MAY** allow a legacy upload to create a publishing session, so that everything from session creation onward |
| 1384 | +happens exactly as described in this PEP. |
| 1385 | + |
| 1386 | +An index that supports this **MUST** document it, and **SHOULD** accept a ``staged`` field with the value |
| 1387 | +``true`` in the legacy ``multipart/form-data`` upload request. When that field is present, the index creates a |
| 1388 | +publishing session in the ``open`` state for the uploaded file's project and version, adds the file to it as a |
| 1389 | +:ref:`completed <file-upload-session-states>` file upload, and does not publish it. The index **SHOULD** |
| 1390 | +return the :ref:`publishing session creation response body <publishing-session-response>` from that upload, |
| 1391 | +including the ``links`` and ``session-token`` keys, so that the publisher can then use the endpoints in this |
| 1392 | +PEP to :ref:`preview <staged-preview>`, :ref:`publish <publishing-session-completion>`, or :ref:`cancel |
| 1393 | +<publishing-session-cancellation>` the session. An index **MAY** also create a session for an upload based on |
| 1394 | +its own policy or the project's configuration, without the field being present; this allows a project to |
| 1395 | +require that its releases are staged in a way that an upload client cannot bypass. |
| 1396 | + |
| 1397 | +Because the legacy API uploads a single file per request, subsequent legacy uploads for the same project and |
| 1398 | +version **SHOULD** be added to the same open session, so that the release is still published as a unit. |
| 1399 | + |
| 1400 | +A legacy client that is unaware of this PEP cannot issue a :ref:`publish request |
| 1401 | +<publishing-session-completion>`. Where an index has created a session on such a client's behalf, and the |
| 1402 | +session is subject only to automated processing, the index **MAY** publish the session itself once that |
| 1403 | +processing resolves without an adverse result, or once the period allowed for it elapses (see |
| 1404 | +:ref:`publishing-session-completion`). An index **MUST NOT** publish a session this way if the publisher has |
| 1405 | +configured the project to require a separate publishing authorization (see :ref:`authentication`). |
| 1406 | + |
| 1407 | +Aside from how the session is created, this section changes no part of this API: the :ref:`state machine |
| 1408 | +<publishing-session-states>`, the status endpoints, stage previews, and the publish and cancel actions are the |
| 1409 | +same whether the session was created through this API or through a legacy upload. |
| 1410 | + |
| 1411 | + |
1351 | 1412 | .. _file-upload-mechanisms: |
1352 | 1413 |
|
1353 | 1414 | File Upload Mechanisms |
@@ -1847,6 +1908,17 @@ as experience is gained operating Upload 2.0. |
1847 | 1908 | Change History |
1848 | 1909 | ============== |
1849 | 1910 |
|
| 1911 | +* `01-Aug-2026 <https://discuss.python.org/t/pre-pep-staged-releases-separated-from-pep-694/107804/58>`__ |
| 1912 | + |
| 1913 | + * Add :ref:`Legacy Upload API Interoperability <legacy-interop>`, allowing a legacy upload to create a |
| 1914 | + publishing session (via a ``staged`` field, or by index or project configuration that an upload client |
| 1915 | + cannot bypass) so that staging is usable before upload tooling adopts this API, and allowing an index to |
| 1916 | + publish such a session itself once automated processing resolves or its window elapses. |
| 1917 | + * Note that the ``processing`` state **MAY** be used for asynchronous review such as malware scanning, with |
| 1918 | + an index-chosen window after which the review is treated as having produced no adverse result. |
| 1919 | + * Allow an index to require additional authorization, beyond upload permission, to publish or cancel a |
| 1920 | + session, so that duties can be separated. |
| 1921 | + |
1850 | 1922 | * `29-Jul-2026 <https://discuss.python.org/t/pep-694-pypi-upload-api-2-0-round-4/108320>`__ |
1851 | 1923 |
|
1852 | 1924 | * Add an **Atomic Publication and Conflicts** section. Specify that publication is atomic with respect to |
|
0 commit comments