Skip to content

Commit 185eacf

Browse files
woodruffwAA-Turnerhugovk
authored
PEP 792: Project status markers in the simple index (#4432)
Signed-off-by: William Woodruff <william@yossarian.net> Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
1 parent 816c057 commit 185eacf

File tree

2 files changed

+384
-0
lines changed

2 files changed

+384
-0
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,7 @@ peps/pep-0788.rst @ZeroIntensity @vstinner
670670
peps/pep-0789.rst @njsmith
671671
peps/pep-0790.rst @hugovk
672672
peps/pep-0791.rst @vstinner
673+
peps/pep-0792.rst @dstufft
673674
# ...
674675
peps/pep-0793.rst @encukou
675676
# ...

peps/pep-0792.rst

Lines changed: 383 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,383 @@
1+
PEP: 792
2+
Title: Project status markers in the simple index
3+
Author: William Woodruff <william@yossarian.net>,
4+
Facundo Tuesca <facundo.tuesca@trailofbits.com>,
5+
Sponsor: Donald Stufft <donald@stufft.io>
6+
PEP-Delegate: Donald Stufft <donald@stufft.io>
7+
Discussions-To: Pending
8+
Status: Draft
9+
Type: Standards Track
10+
Topic: Packaging
11+
Created: 21-May-2025
12+
Post-History: `03-Feb-2025 <https://discuss.python.org/t/79356/>`__,
13+
14+
Abstract
15+
========
16+
17+
This PEP proposes a standardized set of index-supplied project status markers,
18+
as well as a mechanism for communicating those markers in the HTML and JSON
19+
simple indices.
20+
21+
Rationale and Motivation
22+
========================
23+
24+
The "status" of a project is an important piece of metadata, made more important
25+
by growth in both the size and complexity of the Python packaging ecosystem.
26+
Project status (or proxies such as recent activity) is useful to know in
27+
determining whether a project is maintained or otherwise suitable for consumption.
28+
29+
Python packaging has at least three different mechanisms for communicating
30+
the "status" of a project:
31+
32+
1. Distribution packages can include *Trove classifiers* in their metadata, as
33+
originally specified in :pep:`301`. The list of supported classifiers is
34+
`maintained by the PyPA <https://github.com/pypa/trove-classifiers>`_,
35+
and includes the ``Development Status`` hierarchy. For example, a
36+
distribution can include the ``Development Status :: 7 - Inactive``
37+
classifier to indicate that the distribution's project is inactive.
38+
39+
Trove classifiers are flexible, but also come with significant limitations:
40+
they're machine-readable and are rendered on indices like PyPI, but
41+
they also require the maintainer to push one or more *new* distributions
42+
each time they wish to update their project's development status.
43+
Furthermore, because distributions are *de facto* immutable in the Python
44+
packaging ecosystem, older distributions can't have their classifiers
45+
updated to reflect the current status of the project.
46+
47+
2. Indices can mark distributions and releases as "yanked", as originally
48+
specified in :pep:`592`. Yanked distributions are not considered
49+
eligible for dependency resolution.
50+
51+
When a distribution has been yanked, it is marked with ``data-yanked``
52+
in the HTML index and with ``yanked: bool | str`` in the JSON index.
53+
Additionally, indices like PyPI will hide yanked distributions by default
54+
and will render them with a notice when the user navigates directly to them.
55+
56+
Yanking is machine-readable like Trove classifiers, but is single-purpose
57+
rather than general-purpose: users can specify a free-text reason for
58+
yanking a given distribution package, but the semantics of yanking are
59+
fixed, and no reliable inference of project status can be made by a machine
60+
based upon that free-text reason.
61+
62+
3. PyPI itself has *project statuses*, which apply to the entire project
63+
(i.e., all releases and distributions). Project statuses have both
64+
maintainer- and index-admin-controllable states:
65+
66+
* PyPI administrators can "quarantine" a project. Quarantine behaves like
67+
a strengthened yank: the entire project remains uninstallable while
68+
quarantined, and only an administrator can un-quarantine it.
69+
70+
* Project owners can "archive" a project. Archiving a project
71+
disables new release and distribution uploads to that project,
72+
but otherwise has no effect on the ability to download a project.
73+
74+
Project statuses are machine-readable *in principle*, but are not currently
75+
exposed via any of PyPI's APIs. Instead, PyPI renders project statuses on
76+
each project's user-facing (i.e. non-index) webpage.
77+
78+
In summary, there are multiple ways to communicate the "status" of a project in
79+
Python packaging. However, none of them satisfy the four characteristics we
80+
desire. There is no current project status indicator that is machine-readable,
81+
general (i.e. conveys more than one possible state), index-agnostic, and applies
82+
to the entire project, instead of per-release or per-distribution.
83+
84+
===================== ================ ======= ============== ============
85+
Mechanism Machine-readable General Index-agnostic Project-wide
86+
===================== ================ ======= ============== ============
87+
Trove classifiers ✅ ✅ ✅ ❌
88+
Yanking ✅ ❌ ✅ ✅
89+
PyPI project statuses ✅ ✅ ❌ ✅
90+
===================== ================ ======= ============== ============
91+
92+
This PEP proposes adopting PyPI's project statuses as an index-agnostic
93+
mechanism, satisfying all four conditions.
94+
95+
Specification
96+
=============
97+
98+
This PEP specifies two aspects: a set of project status markers,
99+
as well as their presentation in the standard HTML and JSON indices.
100+
101+
Project status markers
102+
----------------------
103+
104+
This PEP proposes the following project status markers.
105+
106+
A project always has exactly one status. If no status is explicitly noted,
107+
then the project is considered to be in the ``active`` state.
108+
109+
Indices **MAY** implement any subset of the status markers specified in this
110+
PEP, as applicable.
111+
112+
This PEP does not prescribe *which* principals (i.e. project maintainers,
113+
index administrators, etc.) are allowed to set and unset which statuses.
114+
115+
``active``
116+
~~~~~~~~~~
117+
118+
Description: The project is active. This is the default status for a project.
119+
120+
Index semantics:
121+
122+
* The index hosting the project **MUST** allow uploads of new distributions to
123+
the project.
124+
* The index **MUST** offer existing distributions of the project for download.
125+
126+
Installer semantics: none.
127+
128+
``archived``
129+
~~~~~~~~~~~~
130+
131+
Description: The project does not expect to be updated in the future.
132+
133+
Index semantics:
134+
135+
* The index hosting the project **MUST NOT** allow uploads of new distributions to
136+
the project.
137+
* The index **MUST** offer existing distributions of the project for download.
138+
139+
Installer semantics:
140+
141+
* Installers **MAY** produce warnings about a project's archival.
142+
143+
``quarantined``
144+
~~~~~~~~~~~~~~~
145+
146+
Description: The project is considered generally unsafe for use, e.g. due to
147+
malware.
148+
149+
Index semantics:
150+
151+
* The index hosting the project **MUST NOT** allow uploads of new distributions to
152+
the project.
153+
* The index **MUST NOT** offer any distributions of the project for download.
154+
155+
Installer semantics:
156+
157+
* Installers **MAY** produce warnings about a project's quarantine, although
158+
doing so is effectively moot (as the index will not offer any distributions
159+
for installation).
160+
161+
``deprecated``
162+
~~~~~~~~~~~~~~
163+
164+
Description: The project is considered obsolete, and may have been superseded
165+
by another project.
166+
167+
Index semantics:
168+
169+
* This status shares the same semantics as ``active``.
170+
171+
Installer semantics:
172+
173+
* Installers **MAY** produce warnings about a project's deprecation.
174+
175+
Status markers in the index APIs
176+
--------------------------------
177+
178+
This PEP defines version 1.4 of the index APIs.
179+
180+
HTML index
181+
~~~~~~~~~~
182+
183+
The following changes are made to the
184+
:ref:`simple repository API <packaging:simple-repository-api-base>`:
185+
186+
* The index **SHALL** define the ``pypi:repository-version`` as ``1.4``.
187+
* The index **SHOULD** add an appropriate ``pypi:project-status`` meta tag, with
188+
a ``content`` of the project's status marker. The index **MAY** choose to omit
189+
the ``pypi:project-status`` meta tag if the project is marked as ``active``.
190+
191+
For example, the following would be a valid HTML index response for
192+
``sampleproject`` after is has been marked as ``quarantined``:
193+
194+
.. code-block:: html
195+
:emphasize-lines: 5
196+
197+
<!DOCTYPE html>
198+
<html>
199+
<head>
200+
<meta name="pypi:repository-version" content="1.4">
201+
<meta name="pypi:project-status" content="quarantined">
202+
<title>Links for sampleproject</title>
203+
</head>
204+
<body>
205+
<h1>Links for sampleproject</h1>
206+
</body>
207+
</html>
208+
209+
Observe that, per the ``quarantined`` semantics above, the index response
210+
contains no distribution links for the project.
211+
212+
JSON index
213+
~~~~~~~~~~
214+
215+
The following changes are made to the
216+
:ref:`JSON simple index <packaging:simple-repository-api-json>`:
217+
218+
* The index **SHALL** define the ``meta.api-version`` as ``1.4``.
219+
* The index **SHOULD** include a ``project-status`` key in the JSON response,
220+
with a value of the project's status marker. The index **MAY** choose to omit
221+
the ``project-status`` key if the project is marked as ``active``.
222+
223+
For example, the following would be a valid JSON index response for
224+
``sampleproject`` after is has been marked as ``quarantined``:
225+
226+
.. code-block:: json
227+
:emphasize-lines: 5
228+
229+
{
230+
"meta": {
231+
"api-version": "1.4"
232+
},
233+
"project-status": "quarantined",
234+
"alternate-locations": [],
235+
"files": [],
236+
"name": "sampleproject",
237+
"versions": [
238+
"1.2.0",
239+
"1.3.0",
240+
"1.3.1",
241+
"2.0.0",
242+
"3.0.0",
243+
"4.0.0"
244+
]
245+
}
246+
247+
Observe that, like with the HTML index, the JSON response contains no
248+
distribution links for the ``quarantined`` project.
249+
250+
Future Considerations
251+
=====================
252+
253+
This PEP defines only four project status markers: ``active``, ``archived``,
254+
``quarantined``, and ``deprecated``.
255+
256+
Future PEPs (or PyPA standards processes) may define additional project status
257+
markers, as needed. Any future status markers may require a metadata version
258+
bump, unless a future metadata change is made to allow for "open-ended" status
259+
markers (i.e., where indices and installers do not necessarily share a single
260+
common list of allowed statuses).
261+
262+
As specified in this PEP, project status markers are "bare," i.e. they
263+
convey no additional user-controlled metadata (such as an explanation
264+
for a project's archival).
265+
266+
A future PEP may choose to extend the project
267+
status mechanism to include user-controlled metadata, in a manner similar
268+
to the free-form text allowed during release yanking.
269+
270+
Security Implications
271+
=====================
272+
273+
This PEP does not identify any positive or negative security implications
274+
associated with adding project status markers.
275+
276+
How to Teach This
277+
=================
278+
279+
Educating the Python community about this PEP has two aspects:
280+
281+
* Ordinary package maintainers will need to be informed of their ability to
282+
set project status markers, e.g. to inform their downstreams that
283+
a project has been archived or deprecated.
284+
285+
If this PEP is accepted, the authors of this PEP will coordinate with
286+
PyPI on appropriate maintainer-oriented documentation and communication,
287+
including feature announcement blog posts and updates to
288+
`PyPI's user documentation <https://docs.pypi.org>`_.
289+
290+
* Installer and index maintainers will need to be informed of the new project
291+
status markers, and how to interpret them.
292+
293+
If this PEP is accepted, the authors of this PEP will perform its
294+
implementation on PyPI, serving as a reference implementation for other
295+
indices.
296+
297+
This PEP does not *mandate* any changes in installer behavior. However,
298+
if this PEP is accepted, the authors of this PEP will coordinate with
299+
the maintainers of popular installers (e.g. ``pip``) to help each determine
300+
the extent to which they wish to surface project statuses.
301+
302+
Rejected Ideas
303+
==============
304+
305+
Using "reserved" keys
306+
---------------------
307+
308+
One alternative to this PEP is to avoid standardizing project status
309+
markers directly, but instead use existing mechanisms within the standards
310+
to communicate them in a non-standard fashion.
311+
312+
For example, the `JSON simple index <packaging:simple-repository-api-json>`_
313+
says the following:
314+
315+
Keys (at any level) with a leading underscore are reserved as private for
316+
index server use. No future standard will assign a meaning to any such key.
317+
318+
In effect, this means that the following would be standards-compliant:
319+
320+
.. code-block:: json
321+
:emphasize-lines: 5
322+
323+
{
324+
"meta": {
325+
"api-version": "1.4"
326+
},
327+
"_project-status": "quarantined",
328+
"alternate-locations": [],
329+
"files": [],
330+
"name": "sampleproject",
331+
"versions": [
332+
"1.2.0",
333+
"1.3.0",
334+
"1.3.1",
335+
"2.0.0",
336+
"3.0.0",
337+
"4.0.0"
338+
]
339+
}
340+
341+
However, this approach has several drawbacks:
342+
343+
* Standards-aligned tools (such as ``pip``, ``pip-audit``, and ``uv``)
344+
may find it unacceptable to use a "reserved" key, since that key will
345+
have no standard semantics or compatibility properties.
346+
* The "reserved" approach is only suitable for the JSON simple index;
347+
no equivalent mechanism exists for the HTML simple index.
348+
This would disadvantage consumers of the HTML simple index, as well as
349+
mirror implementations that may consume the JSON index but only expose
350+
an HTML index.
351+
352+
Project markers in PyPI's non-standard JSON API
353+
-----------------------------------------------
354+
355+
Another standardization-avoidance alternative is to expose project status
356+
markers, but only in PyPI's
357+
`non-standard JSON API <https://docs.pypi.org/api/json/>`_. PyPI has full
358+
control over the layout of this API, and could include a ``project-status``
359+
or similar key without needing a PEP or underscore prefix.
360+
361+
This has similar drawbacks as the "reserved" keys approach above,
362+
and more generally deepens the differences between the standard
363+
and non-standard APIs.
364+
365+
Multiple project status markers at once
366+
---------------------------------------
367+
368+
An earlier version of this PEP considered proposing support for
369+
multiple project markers at once. For example, a project could be marked
370+
as both ``archived`` and ``quarantined``.
371+
372+
After consideration, this was rejected for complexity reasons: having multiple
373+
project status markers requires the PEP to specify a conflict resolution
374+
mechanism when merging their semantics, as well as as state machine for which
375+
markers are exclusive (for example, ``active`` is conceptually exclusive with
376+
all other markers, while ``archived`` and ``quarantined`` are conceptually
377+
compatible with each other).
378+
379+
Copyright
380+
=========
381+
382+
This document is placed in the public domain or under the CC0-1.0-Universal
383+
license, whichever is more permissive.

0 commit comments

Comments
 (0)