CINXE.COM

PEP 592 – Adding “Yank” Support to the Simple API | peps.python.org

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="color-scheme" content="light dark"> <title>PEP 592 – Adding “Yank” Support to the Simple API | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0592/"> <link rel="stylesheet" href="../_static/style.css" type="text/css"> <link rel="stylesheet" href="../_static/mq.css" type="text/css"> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light"> <link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark"> <link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss"> <meta property="og:title" content='PEP 592 – Adding “Yank” Support to the Simple API | peps.python.org'> <meta property="og:description" content="This PEP proposes adding the ability to mark a particular file download on a simple repository as “yanked”. Yanking a file allows authors to effectively delete a file, without breaking things for people who have pinned to exactly a specific version."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0592/"> <meta property="og:site_name" content="Python Enhancement Proposals (PEPs)"> <meta property="og:image" content="https://peps.python.org/_static/og-image.png"> <meta property="og:image:alt" content="Python PEPs"> <meta property="og:image:width" content="200"> <meta property="og:image:height" content="200"> <meta name="description" content="This PEP proposes adding the ability to mark a particular file download on a simple repository as “yanked”. Yanking a file allows authors to effectively delete a file, without breaking things for people who have pinned to exactly a specific version."> <meta name="theme-color" content="#3776ab"> </head> <body> <svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all"> <title>Following system colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="9"></circle> <path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path> </svg> </symbol> <symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all"> <title>Selected dark colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path> </svg> </symbol> <symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all"> <title>Selected light colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="5"></circle> <line x1="12" y1="1" x2="12" y2="3"></line> <line x1="12" y1="21" x2="12" y2="23"></line> <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line> <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line> <line x1="1" y1="12" x2="3" y2="12"></line> <line x1="21" y1="12" x2="23" y2="12"></line> <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line> <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line> </svg> </symbol> </svg> <script> document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto" </script> <section id="pep-page-section"> <header> <h1>Python Enhancement Proposals</h1> <ul class="breadcrumbs"> <li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li> <li><a href="../pep-0000/">PEP Index</a> &raquo; </li> <li>PEP 592</li> </ul> <button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())"> <svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg> <span class="visually-hidden">Toggle light / dark / auto colour theme</span> </button> </header> <article> <section id="pep-content"> <h1 class="page-title">PEP 592 – Adding “Yank” Support to the Simple API</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Donald Stufft &lt;donald&#32;&#97;t&#32;stufft.io&gt;</dd> <dt class="field-even">BDFL-Delegate<span class="colon">:</span></dt> <dd class="field-even">Paul Moore &lt;p.f.moore&#32;&#97;t&#32;gmail.com&gt;</dd> <dt class="field-odd">Discussions-To<span class="colon">:</span></dt> <dd class="field-odd"><a class="reference external" href="https://discuss.python.org/t/1629">Discourse thread</a></dd> <dt class="field-even">Status<span class="colon">:</span></dt> <dd class="field-even"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd> <dt class="field-odd">Type<span class="colon">:</span></dt> <dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd> <dt class="field-even">Topic<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="../topic/packaging/">Packaging</a></dd> <dt class="field-odd">Created<span class="colon">:</span></dt> <dd class="field-odd">07-May-2019</dd> <dt class="field-even">Resolution<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/pep-592-support-for-yanked-files-in-the-simple-repository-api/1629/30">Discourse message</a></dd> </dl> <hr class="docutils" /> <section id="contents"> <details><summary>Table of Contents</summary><ul class="simple"> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#motivation">Motivation</a></li> <li><a class="reference internal" href="#specification">Specification</a><ul> <li><a class="reference internal" href="#installers">Installers</a></li> <li><a class="reference internal" href="#mirrors">Mirrors</a></li> </ul> </li> <li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a></li> <li><a class="reference internal" href="#warehouse-pypi-implementation-notes">Warehouse/PyPI Implementation Notes</a><ul> <li><a class="reference internal" href="#journal-handling">Journal Handling</a></li> </ul> </li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> </details></section> <section id="abstract"> <h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2> <p>This PEP proposes adding the ability to mark a particular file download on a simple repository as “yanked”. Yanking a file allows authors to effectively delete a file, without breaking things for people who have pinned to exactly a specific version.</p> <p>It also changes to the canonical source for the simple repository API to the <a class="reference external" href="https://packaging.python.org/specifications/simple-repository-api/">Simple Repository API</a> reference document.</p> </section> <section id="motivation"> <h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2> <p>Whenever a project detects that a particular release on PyPI might be broken, they oftentimes will want to prevent further users from inadvertently using that version. However, the obvious solution of deleting the existing file from a repository will break users who have followed best practices and pinned to a specific version of the project.</p> <p>This leaves projects in a catch-22 situation where new projects may be pulling down this known broken version, but if they do anything to prevent that they’ll break projects that are already using it.</p> <p>By allowing the ability to “yank” a file, but still make it available for users who are explicitly asking for it, this allows projects to mitigate the worst of the breakage while still keeping things working for projects who have otherwise worked around or didn’t hit the underlying issues.</p> <p>One of the main scenarios where this may happen, is when dropping support for a particular version of Python. The <code class="docutils literal notranslate"><span class="pre">python-requires</span></code> metadata allows for dropping support for a version of Python in a way that is not disruptive to users who are still using that Python. However, a common mistake is to either omit or forget to update that bit of metadata. When that mistake has been made, a project really only has three options:</p> <ul class="simple"> <li>Prevent that version from being installed through some mechanism (currently, the only mechanism is by deleting the release entirely).</li> <li>Re-release the version that worked as a higher version number, and then re-release the version that dropped support as an even higher version number with the correct metadata.</li> <li>Do nothing, and document that people using that older Python have to manually exclude that release.</li> </ul> <p>With this PEP, projects can choose the first option, but with a mechanism that is less likely to break the world for people who are <em>currently</em> successfully using said project.</p> </section> <section id="specification"> <h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2> <p>Links in the simple repository <strong>MAY</strong> have a <code class="docutils literal notranslate"><span class="pre">data-yanked</span></code> attribute which may have no value, or may have an arbitrary string as a value. The presence of a <code class="docutils literal notranslate"><span class="pre">data-yanked</span></code> attribute <strong>SHOULD</strong> be interpreted as indicating that the file pointed to by this particular link has been “Yanked”, and should not generally be selected by an installer, except under specific scenarios.</p> <p>The value of the <code class="docutils literal notranslate"><span class="pre">data-yanked</span></code> attribute, if present, is an arbitrary string that represents the reason for why the file has been yanked. Tools that process the simple repository API <strong>MAY</strong> surface this string to end users.</p> <p>The yanked attribute is not immutable once set, and may be rescinded in the future (and once rescinded, may be reset as well). Thus API users <strong>MUST</strong> be able to cope with a yanked file being “unyanked” (and even yanked again).</p> <section id="installers"> <h3><a class="toc-backref" href="#installers" role="doc-backlink">Installers</a></h3> <p>The desirable experience for users is that once a file is yanked, when a human being is currently trying to directly install a yanked file, that it fails as if that file had been deleted. However, when a human did that a while ago, and now a computer is just continuing to mechanically follow the original order to install the now yanked file, then it acts as if it had not been yanked.</p> <p>An installer <strong>MUST</strong> ignore yanked releases, if the selection constraints can be satisfied with a non-yanked version, and <strong>MAY</strong> refuse to use a yanked release even if it means that the request cannot be satisfied at all. An implementation <strong>SHOULD</strong> choose a policy that follows the spirit of the intention above, and that prevents “new” dependencies on yanked releases/files.</p> <p>What this means is left up to the specific installer, to decide how to best fit into the overall usage of their installer. However, there are two suggested approaches to take:</p> <ol class="arabic simple"> <li>Yanked files are always ignored, unless they are the only file that matches a version specifier that “pins” to an exact version using either <code class="docutils literal notranslate"><span class="pre">==</span></code> (without any modifiers that make it a range, such as <code class="docutils literal notranslate"><span class="pre">.*</span></code>) or <code class="docutils literal notranslate"><span class="pre">===</span></code>. Matching this version specifier should otherwise be done as per <a class="pep reference internal" href="../pep-0440/" title="PEP 440 – Version Identification and Dependency Specification">PEP 440</a> for things like local versions, zero padding, etc.</li> <li>Yanked files are always ignored, unless they are the only file that matches what a lock file (such as <code class="docutils literal notranslate"><span class="pre">Pipfile.lock</span></code> or <code class="docutils literal notranslate"><span class="pre">poetry.lock</span></code>) specifies to be installed. In this case, a yanked file <strong>SHOULD</strong> not be used when creating or updating a lock file from some input file or command.</li> </ol> <p>Regardless of the specific strategy that an installer chooses for deciding when to install yanked files, an installer <strong>SHOULD</strong> emit a warning when it does decide to install a yanked file. That warning <strong>MAY</strong> utilize the value of the <code class="docutils literal notranslate"><span class="pre">data-yanked</span></code> attribute (if it has a value) to provide more specific feedback to the user about why that file had been yanked.</p> </section> <section id="mirrors"> <h3><a class="toc-backref" href="#mirrors" role="doc-backlink">Mirrors</a></h3> <p>Mirrors can generally treat yanked files one of two ways:</p> <ol class="arabic simple"> <li>They may choose to omit them from their simple repository API completely, providing a view over the repository that shows only “active”, unyanked files.</li> <li>They may choose to include yanked files, and additionally mirror the <code class="docutils literal notranslate"><span class="pre">data-yanked</span></code> attribute as well.</li> </ol> <p>Mirrors <strong>MUST NOT</strong> mirror a yanked file without also mirroring the <code class="docutils literal notranslate"><span class="pre">data-yanked</span></code> attribute for it.</p> </section> </section> <section id="rejected-ideas"> <h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2> <p>A previous, undocumented, version of the simple repository API had version specific pages, like <code class="docutils literal notranslate"><span class="pre">/simple/&lt;project&gt;/&lt;version&gt;/</span></code>. If we were to add those back, the yanked files could only appear on those pages and not on the version-less page at all. However this would drastically reduce the cache-ability of the simple API and would directly impact our ability to scale it out to handle all of the incoming traffic.</p> <p>A previous iteration of this PEP had the <code class="docutils literal notranslate"><span class="pre">data-yanked</span></code> attribute act as a boolean value. However it was decided that allowing a string both simplified the implementation, and provided additional generalized functionality to allow projects to provide a mechanism to indicate <em>why</em> they were yanking a release.</p> <p>Another suggestion was to reserve some syntax in the arbitrary string to allow us to evolve the standard in the future if we ever need to. However, given we can add additional attributes in the future, this idea has been rejected, favoring instead to use additional attributes if the need ever arose.</p> </section> <section id="warehouse-pypi-implementation-notes"> <h2><a class="toc-backref" href="#warehouse-pypi-implementation-notes" role="doc-backlink">Warehouse/PyPI Implementation Notes</a></h2> <p>While this PEP implements yanking at the file level, that is largely due to the shape the simple repository API takes, not a specific decision made by this PEP.</p> <p>In Warehouse, the user experience will be implemented in terms of yanking or unyanking an entire release, rather than as an operation on individual files, which will then be exposed via the API as individual files being yanked.</p> <p>Other repository implementations may choose to expose this capability in a different way, or not expose it at all.</p> <section id="journal-handling"> <h3><a class="toc-backref" href="#journal-handling" role="doc-backlink">Journal Handling</a></h3> <p>Whenever a release has been yanked, an entry will be recorded in the journal using one of the following string patterns:</p> <ul class="simple"> <li><code class="docutils literal notranslate"><span class="pre">yank</span> <span class="pre">release</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">unyank</span> <span class="pre">release</span></code></li> </ul> <p>In both cases, the standard journal structure will indicate which release of which project has been yanked or unyanked.</p> </section> </section> <section id="copyright"> <h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2> <p>This document has been placed in the public domain.</p> </section> </section> <hr class="docutils" /> <p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0592.rst">https://github.com/python/peps/blob/main/peps/pep-0592.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0592.rst">2025-02-01 08:55:40 GMT</a></p> </article> <nav id="pep-sidebar"> <h2>Contents</h2> <ul> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#motivation">Motivation</a></li> <li><a class="reference internal" href="#specification">Specification</a><ul> <li><a class="reference internal" href="#installers">Installers</a></li> <li><a class="reference internal" href="#mirrors">Mirrors</a></li> </ul> </li> <li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a></li> <li><a class="reference internal" href="#warehouse-pypi-implementation-notes">Warehouse/PyPI Implementation Notes</a><ul> <li><a class="reference internal" href="#journal-handling">Journal Handling</a></li> </ul> </li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> <br> <a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0592.rst">Page Source (GitHub)</a> </nav> </section> <script src="../_static/colour_scheme.js"></script> <script src="../_static/wrap_tables.js"></script> <script src="../_static/sticky_banner.js"></script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10