CINXE.COM

PEP 503 – Simple Repository 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 503 – Simple Repository API | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0503/"> <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 503 – Simple Repository API | peps.python.org'> <meta property="og:description" content="There are many implementations of a Python package repository and many tools that consume them. Of these, the canonical implementation that defines what the “simple” repository API looks like is the implementation that powers PyPI. This document will sp..."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0503/"> <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="There are many implementations of a Python package repository and many tools that consume them. Of these, the canonical implementation that defines what the “simple” repository API looks like is the implementation that powers PyPI. This document will sp..."> <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 503</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 503 – Simple Repository 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">Donald Stufft &lt;donald&#32;&#97;t&#32;stufft.io&gt;</dd> <dt class="field-odd">Discussions-To<span class="colon">:</span></dt> <dd class="field-odd"><a class="reference external" href="https://mail.python.org/archives/list/distutils-sig&#64;python.org/">Distutils-SIG list</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">04-Sep-2015</dd> <dt class="field-even">Post-History<span class="colon">:</span></dt> <dd class="field-even">04-Sep-2015</dd> <dt class="field-odd">Resolution<span class="colon">:</span></dt> <dd class="field-odd"><a class="reference external" href="https://mail.python.org/pipermail/distutils-sig/2015-September/026899.html">Distutils-SIG 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="#specification">Specification</a><ul> <li><a class="reference internal" href="#normalized-names">Normalized Names</a></li> <li><a class="reference internal" href="#changes">Changes</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>There are many implementations of a Python package repository and many tools that consume them. Of these, the canonical implementation that defines what the “simple” repository API looks like is the implementation that powers PyPI. This document will specify that API, documenting what the correct behavior for any implementation of the simple repository API.</p> </section> <section id="specification"> <h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2> <p>A repository that implements the simple API is defined by its base URL, this is the top level URL that all additional URLs are below. The API is named the “simple” repository due to the fact that PyPI’s base URL is <code class="docutils literal notranslate"><span class="pre">https://pypi.org/simple/</span></code>.</p> <div class="admonition note"> <p class="admonition-title">Note</p> <p>All subsequent URLs in this document will be relative to this base URL (so given PyPI’s URL, a URL of <code class="docutils literal notranslate"><span class="pre">/foo/</span></code> would be <code class="docutils literal notranslate"><span class="pre">https://pypi.org/simple/foo/</span></code>.</p> </div> <p>Within a repository, the root URL (<code class="docutils literal notranslate"><span class="pre">/</span></code> for this PEP which represents the base URL) <strong>MUST</strong> be a valid HTML5 page with a single anchor element per project in the repository. The text of the anchor tag <strong>MUST</strong> be the name of the project and the href attribute <strong>MUST</strong> link to the URL for that particular project. As an example:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span>&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;body&gt; &lt;a href=&quot;/frob/&quot;&gt;frob&lt;/a&gt; &lt;a href=&quot;/spamspamspam/&quot;&gt;spamspamspam&lt;/a&gt; &lt;/body&gt; &lt;/html&gt; </pre></div> </div> <p>Below the root URL is another URL for each individual project contained within a repository. The format of this URL is <code class="docutils literal notranslate"><span class="pre">/&lt;project&gt;/</span></code> where the <code class="docutils literal notranslate"><span class="pre">&lt;project&gt;</span></code> is replaced by the normalized name for that project, so a project named “HolyGrail” would have a URL like <code class="docutils literal notranslate"><span class="pre">/holygrail/</span></code>. This URL must respond with a valid HTML5 page with a single anchor element per file for the project. The href attribute <strong>MUST</strong> be a URL that links to the location of the file for download, and the text of the anchor tag <strong>MUST</strong> match the final path component (the filename) of the URL. The URL <strong>SHOULD</strong> include a hash in the form of a URL fragment with the following syntax: <code class="docutils literal notranslate"><span class="pre">#&lt;hashname&gt;=&lt;hashvalue&gt;</span></code>, where <code class="docutils literal notranslate"><span class="pre">&lt;hashname&gt;</span></code> is the lowercase name of the hash function (such as <code class="docutils literal notranslate"><span class="pre">sha256</span></code>) and <code class="docutils literal notranslate"><span class="pre">&lt;hashvalue&gt;</span></code> is the hex encoded digest.</p> <p>In addition to the above, the following constraints are placed on the API:</p> <ul> <li>All URLs which respond with an HTML5 page <strong>MUST</strong> end with a <code class="docutils literal notranslate"><span class="pre">/</span></code> and the repository <strong>SHOULD</strong> redirect the URLs without a <code class="docutils literal notranslate"><span class="pre">/</span></code> to add a <code class="docutils literal notranslate"><span class="pre">/</span></code> to the end.</li> <li>URLs may be either absolute or relative as long as they point to the correct location.</li> <li>There are no constraints on where the files must be hosted relative to the repository.</li> <li>There may be any other HTML elements on the API pages as long as the required anchor elements exist.</li> <li>Repositories <strong>MAY</strong> redirect unnormalized URLs to the canonical normalized URL (e.g. <code class="docutils literal notranslate"><span class="pre">/Foobar/</span></code> may redirect to <code class="docutils literal notranslate"><span class="pre">/foobar/</span></code>), however clients <strong>MUST NOT</strong> rely on this redirection and <strong>MUST</strong> request the normalized URL.</li> <li>Repositories <strong>SHOULD</strong> choose a hash function from one of the ones guaranteed to be available via the <code class="docutils literal notranslate"><span class="pre">hashlib</span></code> module in the Python standard library (currently <code class="docutils literal notranslate"><span class="pre">md5</span></code>, <code class="docutils literal notranslate"><span class="pre">sha1</span></code>, <code class="docutils literal notranslate"><span class="pre">sha224</span></code>, <code class="docutils literal notranslate"><span class="pre">sha256</span></code>, <code class="docutils literal notranslate"><span class="pre">sha384</span></code>, <code class="docutils literal notranslate"><span class="pre">sha512</span></code>). The current recommendation is to use <code class="docutils literal notranslate"><span class="pre">sha256</span></code>.</li> <li>If there is a GPG signature for a particular distribution file it <strong>MUST</strong> live alongside that file with the same name with a <code class="docutils literal notranslate"><span class="pre">.asc</span></code> appended to it. So if the file <code class="docutils literal notranslate"><span class="pre">/packages/HolyGrail-1.0.tar.gz</span></code> existed and had an associated signature, the signature would be located at <code class="docutils literal notranslate"><span class="pre">/packages/HolyGrail-1.0.tar.gz.asc</span></code>.</li> <li>A repository <strong>MAY</strong> include a <code class="docutils literal notranslate"><span class="pre">data-gpg-sig</span></code> attribute on a file link with a value of either <code class="docutils literal notranslate"><span class="pre">true</span></code> or <code class="docutils literal notranslate"><span class="pre">false</span></code> to indicate whether or not there is a GPG signature. Repositories that do this <strong>SHOULD</strong> include it on every link.</li> <li>A repository <strong>MAY</strong> include a <code class="docutils literal notranslate"><span class="pre">data-requires-python</span></code> attribute on a file link. This exposes the <em>Requires-Python</em> metadata field, specified in <a class="pep reference internal" href="../pep-0345/" title="PEP 345 – Metadata for Python Software Packages 1.2">PEP 345</a>, for the corresponding release. Where this is present, installer tools <strong>SHOULD</strong> ignore the download when installing to a Python version that doesn’t satisfy the requirement. For example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">&lt;</span><span class="n">a</span> <span class="n">href</span><span class="o">=</span><span class="s2">&quot;...&quot;</span> <span class="n">data</span><span class="o">-</span><span class="n">requires</span><span class="o">-</span><span class="n">python</span><span class="o">=</span><span class="s2">&quot;&amp;gt;=3&quot;</span><span class="o">&gt;...&lt;/</span><span class="n">a</span><span class="o">&gt;</span> </pre></div> </div> <p>In the attribute value, &lt; and &gt; have to be HTML encoded as <code class="docutils literal notranslate"><span class="pre">&amp;lt;</span></code> and <code class="docutils literal notranslate"><span class="pre">&amp;gt;</span></code>, respectively.</p> </li> </ul> <section id="normalized-names"> <h3><a class="toc-backref" href="#normalized-names" role="doc-backlink">Normalized Names</a></h3> <p>This PEP references the concept of a “normalized” project name. As per <a class="pep reference internal" href="../pep-0426/" title="PEP 426 – Metadata for Python Software Packages 2.0">PEP 426</a> the only valid characters in a name are the ASCII alphabet, ASCII numbers, <code class="docutils literal notranslate"><span class="pre">.</span></code>, <code class="docutils literal notranslate"><span class="pre">-</span></code>, and <code class="docutils literal notranslate"><span class="pre">_</span></code>. The name should be lowercased with all runs of the characters <code class="docutils literal notranslate"><span class="pre">.</span></code>, <code class="docutils literal notranslate"><span class="pre">-</span></code>, or <code class="docutils literal notranslate"><span class="pre">_</span></code> replaced with a single <code class="docutils literal notranslate"><span class="pre">-</span></code> character. This can be implemented in Python with the <code class="docutils literal notranslate"><span class="pre">re</span></code> module:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">re</span> <span class="k">def</span><span class="w"> </span><span class="nf">normalize</span><span class="p">(</span><span class="n">name</span><span class="p">):</span> <span class="k">return</span> <span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">r</span><span class="s2">&quot;[-_.]+&quot;</span><span class="p">,</span> <span class="s2">&quot;-&quot;</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> </pre></div> </div> </section> <section id="changes"> <h3><a class="toc-backref" href="#changes" role="doc-backlink">Changes</a></h3> <ul class="simple"> <li>The optional <code class="docutils literal notranslate"><span class="pre">data-requires-python</span></code> attribute was added in July 2016.</li> </ul> </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-0503.rst">https://github.com/python/peps/blob/main/peps/pep-0503.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0503.rst">2025-02-01 08:59:27 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="#specification">Specification</a><ul> <li><a class="reference internal" href="#normalized-names">Normalized Names</a></li> <li><a class="reference internal" href="#changes">Changes</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-0503.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