python-peps/pep-0751/index.html

1528 lines
157 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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 751 A file format to record Python dependencies for installation reproducibility | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0751/">
<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 751 A file format to record Python dependencies for installation reproducibility | peps.python.org'>
<meta property="og:description" content="This PEP proposes a new file format for dependency specification to enable reproducible installation in a Python environment. The format is designed to be human-readable and machine-generated. Installers consuming the file should be able to calculate wh...">
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0751/">
<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 a new file format for dependency specification to enable reproducible installation in a Python environment. The format is designed to be human-readable and machine-generated. Installers consuming the file should be able to calculate wh...">
<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 751</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 751 A file format to record Python dependencies for installation reproducibility</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Brett Cannon &lt;brett&#32;&#97;t&#32;python.org&gt;</dd>
<dt class="field-even">Status<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Proposal under active discussion and revision">Draft</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">24-Jul-2024</dd>
<dt class="field-even">Post-History<span class="colon">:</span></dt>
<dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/59173" title="Discourse thread">25-Jul-2024</a>
<a class="reference external" href="https://discuss.python.org/t/69721" title="Discourse thread">30-Oct-2024</a></dd>
<dt class="field-odd">Replaces<span class="colon">:</span></dt>
<dd class="field-odd"><a class="reference external" href="../pep-0665/">665</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="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#file-name">File Name</a></li>
<li><a class="reference internal" href="#file-format">File Format</a><ul>
<li><a class="reference internal" href="#version"><code class="docutils literal notranslate"><span class="pre">version</span></code></a></li>
<li><a class="reference internal" href="#hash-algorithm"><code class="docutils literal notranslate"><span class="pre">hash-algorithm</span></code></a></li>
<li><a class="reference internal" href="#locker"><code class="docutils literal notranslate"><span class="pre">[locker]</span></code></a><ul>
<li><a class="reference internal" href="#locker-name"><code class="docutils literal notranslate"><span class="pre">locker.name</span></code></a></li>
<li><a class="reference internal" href="#locker-version"><code class="docutils literal notranslate"><span class="pre">locker.version</span></code></a></li>
<li><a class="reference internal" href="#locker-run"><code class="docutils literal notranslate"><span class="pre">locker.run</span></code></a><ul>
<li><a class="reference internal" href="#locker-run-module"><code class="docutils literal notranslate"><span class="pre">locker.run.module</span></code></a></li>
<li><a class="reference internal" href="#locker-run-args"><code class="docutils literal notranslate"><span class="pre">locker.run.args</span></code></a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#groups"><code class="docutils literal notranslate"><span class="pre">[[groups]]</span></code></a><ul>
<li><a class="reference internal" href="#groups-name"><code class="docutils literal notranslate"><span class="pre">groups.name</span></code></a></li>
<li><a class="reference internal" href="#groups-project"><code class="docutils literal notranslate"><span class="pre">groups.project</span></code></a></li>
<li><a class="reference internal" href="#groups-requirements"><code class="docutils literal notranslate"><span class="pre">groups.requirements</span></code></a><ul>
<li><a class="reference internal" href="#groups-requirements-name"><code class="docutils literal notranslate"><span class="pre">groups.requirements.name</span></code></a></li>
<li><a class="reference internal" href="#groups-requirements-extras"><code class="docutils literal notranslate"><span class="pre">groups.requirements.extras</span></code></a></li>
<li><a class="reference internal" href="#groups-requirements-version"><code class="docutils literal notranslate"><span class="pre">groups.requirements.version</span></code></a></li>
<li><a class="reference internal" href="#groups-requirements-marker"><code class="docutils literal notranslate"><span class="pre">groups.requirements.marker</span></code></a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#packages"><code class="docutils literal notranslate"><span class="pre">[[packages]]</span></code></a><ul>
<li><a class="reference internal" href="#packages-name"><code class="docutils literal notranslate"><span class="pre">packages.name</span></code></a></li>
<li><a class="reference internal" href="#packages-version"><code class="docutils literal notranslate"><span class="pre">packages.version</span></code></a></li>
<li><a class="reference internal" href="#packages-groups"><code class="docutils literal notranslate"><span class="pre">packages.groups</span></code></a></li>
<li><a class="reference internal" href="#packages-index-url"><code class="docutils literal notranslate"><span class="pre">packages.index-url</span></code></a></li>
<li><a class="reference internal" href="#packages-direct"><code class="docutils literal notranslate"><span class="pre">packages.direct</span></code></a></li>
<li><a class="reference internal" href="#packages-requires-python"><code class="docutils literal notranslate"><span class="pre">packages.requires-python</span></code></a></li>
<li><a class="reference internal" href="#packages-dependencies"><code class="docutils literal notranslate"><span class="pre">[[packages.dependencies]]</span></code></a><ul>
<li><a class="reference internal" href="#packages-dependencies-name"><code class="docutils literal notranslate"><span class="pre">packages.dependencies.name</span></code></a></li>
<li><a class="reference internal" href="#packages-dependencies-extras"><code class="docutils literal notranslate"><span class="pre">packages.dependencies.extras</span></code></a></li>
<li><a class="reference internal" href="#packages-dependencies-version"><code class="docutils literal notranslate"><span class="pre">packages.dependencies.version</span></code></a></li>
<li><a class="reference internal" href="#packages-dependencies-marker"><code class="docutils literal notranslate"><span class="pre">packages.dependencies.marker</span></code></a></li>
<li><a class="reference internal" href="#packages-dependencies-feature"><code class="docutils literal notranslate"><span class="pre">packages.dependencies.feature</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#packages-editable"><code class="docutils literal notranslate"><span class="pre">packages.editable</span></code></a></li>
<li><a class="reference internal" href="#packages-source-tree"><code class="docutils literal notranslate"><span class="pre">[packages.source-tree]</span></code></a><ul>
<li><a class="reference internal" href="#packages-source-tree-vcs"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.vcs</span></code></a></li>
<li><a class="reference internal" href="#packages-source-tree-path"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.path</span></code></a></li>
<li><a class="reference internal" href="#packages-source-tree-url"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.url</span></code></a></li>
<li><a class="reference internal" href="#packages-source-tree-commit"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.commit</span></code></a></li>
<li><a class="reference internal" href="#packages-source-tree-size"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.size</span></code></a></li>
<li><a class="reference internal" href="#packages-source-tree-hash"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.hash</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#packages-sdist"><code class="docutils literal notranslate"><span class="pre">[packages.sdist]</span></code></a><ul>
<li><a class="reference internal" href="#packages-sdist-url"><code class="docutils literal notranslate"><span class="pre">packages.sdist.url</span></code></a></li>
<li><a class="reference internal" href="#packages-sdist-path"><code class="docutils literal notranslate"><span class="pre">packages.sdist.path</span></code></a></li>
<li><a class="reference internal" href="#packages-sdist-upload-time"><code class="docutils literal notranslate"><span class="pre">packages.sdist.upload-time</span></code></a></li>
<li><a class="reference internal" href="#packages-sdist-size"><code class="docutils literal notranslate"><span class="pre">packages.sdist.size</span></code></a></li>
<li><a class="reference internal" href="#packages-sdist-hash"><code class="docutils literal notranslate"><span class="pre">packages.sdist.hash</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#packages-wheels"><code class="docutils literal notranslate"><span class="pre">[[packages.wheels]]</span></code></a><ul>
<li><a class="reference internal" href="#packages-wheels-tags"><code class="docutils literal notranslate"><span class="pre">packages.wheels.tags</span></code></a></li>
<li><a class="reference internal" href="#packages-wheels-build"><code class="docutils literal notranslate"><span class="pre">packages.wheels.build</span></code></a></li>
<li><a class="reference internal" href="#packages-wheels-url"><code class="docutils literal notranslate"><span class="pre">packages.wheels.url</span></code></a></li>
<li><a class="reference internal" href="#packages-wheels-path"><code class="docutils literal notranslate"><span class="pre">packages.wheels.path</span></code></a></li>
<li><a class="reference internal" href="#packages-wheels-upload-time"><code class="docutils literal notranslate"><span class="pre">packages.wheels.upload-time</span></code></a></li>
<li><a class="reference internal" href="#packages-wheels-size"><code class="docutils literal notranslate"><span class="pre">packages.wheels.size</span></code></a></li>
<li><a class="reference internal" href="#packages-wheels-hash"><code class="docutils literal notranslate"><span class="pre">packages.wheels.hash</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#packages-tool"><code class="docutils literal notranslate"><span class="pre">[packages.tool]</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#tool"><code class="docutils literal notranslate"><span class="pre">[tool]</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#examples">Examples</a></li>
<li><a class="reference internal" href="#expectations-for-lockers">Expectations for Lockers</a></li>
<li><a class="reference internal" href="#expectations-for-installers">Expectations for Installers</a><ul>
<li><a class="reference internal" href="#pseudo-code">Pseudo-Code</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li>
<li><a class="reference internal" href="#security-implications">Security Implications</a></li>
<li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
<li><a class="reference internal" href="#a-flat-set-of-packages-to-install">A flat set of packages to install</a></li>
<li><a class="reference internal" href="#specifying-a-new-core-metadata-version-that-requires-consistent-metadata-across-files">Specifying a new core metadata version that requires consistent metadata across files</a></li>
<li><a class="reference internal" href="#have-the-installer-do-dependency-resolution">Have the installer do dependency resolution</a></li>
<li><a class="reference internal" href="#requiring-specific-hash-algorithm-support">Requiring specific hash algorithm support</a></li>
<li><a class="reference internal" href="#require-a-url-or-file-path-for-files">Require a URL or file path for files</a></li>
<li><a class="reference internal" href="#file-naming">File naming</a><ul>
<li><a class="reference internal" href="#using-pylock-toml-as-the-file-name">Using <code class="docutils literal notranslate"><span class="pre">*.pylock.toml</span></code> as the file name</a></li>
<li><a class="reference internal" href="#using-pylock-as-the-file-name">Using <code class="docutils literal notranslate"><span class="pre">*.pylock</span></code> as the file name</a></li>
<li><a class="reference internal" href="#not-having-a-naming-convention-for-the-file">Not having a naming convention for the file</a></li>
</ul>
</li>
<li><a class="reference internal" href="#id1">File format</a><ul>
<li><a class="reference internal" href="#use-json-over-toml">Use JSON over TOML</a></li>
<li><a class="reference internal" href="#use-yaml-over-toml">Use YAML over TOML</a></li>
</ul>
</li>
<li><a class="reference internal" href="#other-keys">Other keys</a><ul>
<li><a class="reference internal" href="#multiple-hashes-per-file">Multiple hashes per file</a></li>
<li><a class="reference internal" href="#hashing-the-contents-of-the-lock-file-itself">Hashing the contents of the lock file itself</a></li>
<li><a class="reference internal" href="#recording-the-creation-date-of-the-lock-file">Recording the creation date of the lock file</a></li>
<li><a class="reference internal" href="#recording-the-package-indexes-used">Recording the package indexes used</a></li>
<li><a class="reference internal" href="#locking-build-requirements-for-sdists">Locking build requirements for sdists</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#open-issues">Open Issues</a><ul>
<li><a class="reference internal" href="#specify-requires-python-at-the-file-level">Specify <code class="docutils literal notranslate"><span class="pre">requires-python</span></code> at the file level?</a></li>
<li><a class="reference internal" href="#don-t-pre-parse-data">Dont pre-parse data?</a></li>
</ul>
</li>
<li><a class="reference internal" href="#deferred-ideas">Deferred Ideas</a><ul>
<li><a class="reference internal" href="#per-file-locking">Per-file locking</a></li>
<li><a class="reference internal" href="#allowing-for-multiple-lock-files">Allowing for multiple lock files</a></li>
</ul>
</li>
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></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 a new file format for dependency specification
to enable reproducible installation in a Python environment. The format is
designed to be human-readable and machine-generated. Installers consuming the
file should be able to calculate what to install without the need for dependency
resolution at install-time.</p>
</section>
<section id="motivation">
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
<p>Currently, no standard exists to create an immutable record, such as a lock
file, which specifies what direct and indirect dependencies should be installed
into a virtual environment.</p>
<p>Considering there are at least five well-known solutions to this problem in the
community (<code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">freeze</span></code>, <a class="reference external" href="https://pypi.org/project/pip-tools/">pip-tools</a>, <a class="reference external" href="https://github.com/astral-sh/uv">uv</a>, <a class="reference external" href="https://python-poetry.org/">Poetry</a>, and <a class="reference external" href="https://pypi.org/project/pdm/">PDM</a>), there seems to
be an appetite for lock files in general.</p>
<p>Those tools also vary in what locking scenarios they support. For instance,
<code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">freeze</span></code> and pip-tools only generate lock files for the current
environment while PDM and Poetry try to lock for <em>any</em> environment to some
degree. Theres also concerns around the lack of secure defaults in the face of
supply chain attacks (e.g., always including hashes for files).</p>
<p>The lack of a standard also has some drawbacks. For instance, any tooling that
wants to work with lock files must choose which format to support, potentially
leaving users unsupported (e.g., <a class="reference external" href="https://docs.github.com/en/code-security/dependabot">Dependabot</a> only supporting select tools,
same for cloud providers who can do dependency installations on your behalf,
etc.). It also impacts portability between tools, which causes vendor lock-in.
By not having compatibility and interoperability it fractures tooling around
lock files where both users and tools have to choose what lock file format to
use upfront and making it costly to use/switch to other formats. Rallying
around a single format removes that cost/barrier.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>Much of the motivation from <a class="pep reference internal" href="../pep-0665/" title="PEP 665 A file format to list Python dependencies for reproducibility of an application">PEP 665</a> also applies to this PEP.</p>
</div>
</section>
<section id="rationale">
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
<p>The format is designed so that a <em>locker</em> which produces the lock file
and an <em>installer</em> which consumes the lock file can be separate tools. This
allows for situations such as cloud hosting providers to use their own installer
thats optimized for their system which is independent of what locker the user
used to create their lock file.</p>
<p>The file format is designed to be human-readable. This is so that the contents
of the file can be audited by a human to make sure no undesired dependencies end
up being included in the lock file.</p>
<p>The file format is also designed to not require a resolver at install time. This
greatly simplifies installers and thus reasoning about what would be installed
when consuming a lock file. It should also lead to faster installs which are
much more frequent than creating a lock file.</p>
<p>Finally, the lock file is meant to be flexible enough to meets the various needs
tools have for choosing what to install. That means the lock file records the
dependency graph of what _may_ be installed. This allows tools to enter the
graph at any point and still have reproducible results from that root of the
graph. Flexibility also means supporting different installation scenarios within
the same lock file (e.g., with or without test dependencies).</p>
</section>
<section id="specification">
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
<section id="file-name">
<h3><a class="toc-backref" href="#file-name" role="doc-backlink">File Name</a></h3>
<p>A lock file MUST be named <code class="file docutils literal notranslate"><span class="pre">pylock.toml</span></code>. The use of the <code class="docutils literal notranslate"><span class="pre">.toml</span></code> file
extension is to make syntax highlighting in editors easier and to reinforce the
fact that the file format is meant to be human-readable.</p>
<p>The lock file SHOULD be located in the directory as appropriate for the scope of
the lock file. Locking against a single <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>, for instance, would
place the <code class="docutils literal notranslate"><span class="pre">pylock.toml</span></code> in the same directory. If the lock file covered
multiple projects in a monorepo, then the expectation is the <code class="docutils literal notranslate"><span class="pre">pylock.toml</span></code>
file would be in the directory that held all the projects being locked.</p>
</section>
<section id="file-format">
<h3><a class="toc-backref" href="#file-format" role="doc-backlink">File Format</a></h3>
<p>The format of the file is <a class="reference external" href="https://toml.io/">TOML</a>.</p>
<p>All keys listed below are required unless otherwise noted. If two keys are
mutually exclusive to one another, then one of the keys is required while the
other is disallowed.</p>
<p>Keys in tables including the top-level table SHOULD be emitted by lockers
in the order they are listed in this PEP when applicable unless another sort
order is specified to minimize noise in diffs. If the keys are not explicitly
specified in this PEP, then the keys SHOULD be sorted by lexicographic order.</p>
<p>As well, lockers SHOULD sort arrays in lexicographic order unless otherwise
specified for the same reason.</p>
<section id="version">
<h4><a class="toc-backref" href="#version" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">version</span></code></a></h4>
<ul class="simple">
<li>String</li>
<li>The version of the lock file format.</li>
<li>This PEP specifies the initial version and only valid value until future
updates to the standard change it as <code class="docutils literal notranslate"><span class="pre">&quot;1.0&quot;</span></code>.</li>
<li>If an installer supports the major version but not the minor version, a tool
SHOULD warn when an unknown key is seen.</li>
<li>If an installer doesnt support a major version, it MUST raise an error.</li>
</ul>
</section>
<section id="hash-algorithm">
<h4><a class="toc-backref" href="#hash-algorithm" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">hash-algorithm</span></code></a></h4>
<ul class="simple">
<li>String</li>
<li>The name of the hash algorithm used for calculating all hash values.</li>
<li>Only a single hash algorithm is used for the entire file to allow hash values
to be written in inline tables for readability and compactness purposes by
only listing a single hash value instead of multiple values based on multiple
hash algorithms.</li>
<li>Specifying a single hash algorithm guarantees that an algorithm that the user
prefers is used consistently throughout the file without having to audit
each file hash value separately.</li>
<li>Allows for updating the entire file to a new hash algorithm without running
the risk of accidentally leaving an old hash value in the file.</li>
<li><a class="reference external" href="https://packaging.python.org/en/latest/specifications/simple-repository-api/#simple-repository-api-json" title="(in Python Packaging User Guide)"><span>JSON-based Simple API for Python Package Indexes</span></a> and the <code class="docutils literal notranslate"><span class="pre">hashes</span></code> dictionary of
of the <code class="docutils literal notranslate"><span class="pre">files</span></code> dictionary of the Project Details dictionary specifies what
values are valid and guidelines on what hash algorithms to use.</li>
<li>Failure to validate any hash values for any file that is to be installed MUST
raise an error.</li>
</ul>
</section>
<section id="locker">
<h4><a class="toc-backref" href="#locker" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">[locker]</span></code></a></h4>
<ul class="simple">
<li>Table</li>
<li>Record of the tool that generated the lock file.</li>
<li>Enough details SHOULD be provided such that the lock
file from the details in this table can be reproduced (provided the same I/O
data is available, e.g., Dependabot if only files from a repository is
necessary to run the command).</li>
</ul>
<section id="locker-name">
<h5><a class="toc-backref" href="#locker-name" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">locker.name</span></code></a></h5>
<ul class="simple">
<li>String</li>
<li>The name of the tool used to create the lock file.</li>
<li>If the locker is a Python project, its normalized name SHOULD be used.</li>
</ul>
</section>
<section id="locker-version">
<h5><a class="toc-backref" href="#locker-version" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">locker.version</span></code></a></h5>
<ul class="simple">
<li>String</li>
<li>The version of the tool used.</li>
</ul>
</section>
<section id="locker-run">
<h5><a class="toc-backref" href="#locker-run" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">locker.run</span></code></a></h5>
<ul class="simple">
<li>Optional</li>
<li>Inline table</li>
<li>Records the command used to create the lock file.</li>
</ul>
<section id="locker-run-module">
<h6><a class="toc-backref" href="#locker-run-module" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">locker.run.module</span></code></a></h6>
<ul class="simple">
<li>Optional</li>
<li>String</li>
<li>The module name used for running the locker (i.e. what would be passed to
<code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">-m</span></code>).</li>
<li>Lockers MUST specify this key if the locker can be executed via <code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">-m</span></code>.</li>
</ul>
</section>
<section id="locker-run-args">
<h6><a class="toc-backref" href="#locker-run-args" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">locker.run.args</span></code></a></h6>
<ul class="simple">
<li>Optional</li>
<li>Array of strings</li>
<li>If the locker has a CLI, the arguments to pass to the locker.</li>
<li>All paths MUST be relative to the lock file so that another tool could use
the lock files location as the current working directory.</li>
</ul>
</section>
</section>
</section>
<section id="groups">
<h4><a class="toc-backref" href="#groups" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">[[groups]]</span></code></a></h4>
<ul class="simple">
<li>Array of tables</li>
<li>A named subset of packages as found in <code class="docutils literal notranslate"><span class="pre">[[packages]]</span></code>.</li>
<li>Act as roots into the dependency graph.</li>
<li>Installers MUST allow the user to select one or more groups by name to
install all relevant packages together.</li>
<li>Installers SHOULD let the user skip specifying a name if there is only one
entry in the array.</li>
</ul>
<section id="groups-name">
<h5><a class="toc-backref" href="#groups-name" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">groups.name</span></code></a></h5>
<ul class="simple">
<li>String</li>
<li>The name of the group.</li>
</ul>
</section>
<section id="groups-project">
<h5><a class="toc-backref" href="#groups-project" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">groups.project</span></code></a></h5>
<ul class="simple">
<li>Mutually-exclusive with <code class="docutils literal notranslate"><span class="pre">requirements</span></code></li>
<li>String</li>
<li>The normalized name of a package to act as the starting point into the
dependency graph.</li>
<li>Analogous to locking to the <code class="docutils literal notranslate"><span class="pre">[project]</span></code> table in <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>.</li>
<li>Installers MUST let a user specify any optional features/extras that the
package provides.</li>
<li>Lockers MUST NOT allow for ambiguity by specifying multiple package versions
of the same package under the same group name when a package is listed in any
<code class="docutils literal notranslate"><span class="pre">project</span></code> key.</li>
</ul>
</section>
<section id="groups-requirements">
<h5><a class="toc-backref" href="#groups-requirements" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">groups.requirements</span></code></a></h5>
<ul class="simple">
<li>Mutually-exclusive with <code class="docutils literal notranslate"><span class="pre">project</span></code></li>
<li>Array of tables</li>
<li>Represents the installation requirements for this group.</li>
<li>Analogous to a key in <code class="docutils literal notranslate"><span class="pre">[dependency-groups]</span></code> in <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>.</li>
<li>Lockers MUST make sure that resolving any requirement for any environment does
not lead to ambiguity by having multiple values in <code class="docutils literal notranslate"><span class="pre">[[packages]]</span></code> match the
same requirement.</li>
<li>Values in the array SHOULD be written as inline tables, sorted
lexicographically by <code class="docutils literal notranslate"><span class="pre">name</span></code>, then by <code class="docutils literal notranslate"><span class="pre">feature</span></code> with the lack of that key
sorting first.</li>
</ul>
<section id="groups-requirements-name">
<h6><a class="toc-backref" href="#groups-requirements-name" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">groups.requirements.name</span></code></a></h6>
<ul class="simple">
<li>String</li>
<li>Normalized name of the package.</li>
</ul>
</section>
<section id="groups-requirements-extras">
<h6><a class="toc-backref" href="#groups-requirements-extras" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">groups.requirements.extras</span></code></a></h6>
<ul class="simple">
<li>Optional</li>
<li>Array of strings</li>
<li>The names of the extras specified for the requirement
(i.e. what comes between <code class="docutils literal notranslate"><span class="pre">[...]</span></code>).</li>
</ul>
</section>
<section id="groups-requirements-version">
<h6><a class="toc-backref" href="#groups-requirements-version" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">groups.requirements.version</span></code></a></h6>
<ul class="simple">
<li>Optional</li>
<li>String</li>
<li>The <a class="reference external" href="https://packaging.python.org/en/latest/specifications/version-specifiers/">version specifiers</a> for the requirement.</li>
</ul>
</section>
<section id="groups-requirements-marker">
<h6><a class="toc-backref" href="#groups-requirements-marker" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">groups.requirements.marker</span></code></a></h6>
<ul class="simple">
<li>Optional</li>
<li>String</li>
<li>The <a class="reference external" href="https://packaging.python.org/en/latest/specifications/dependency-specifiers/#environment-markers">environment markers</a> for the requirement.</li>
</ul>
</section>
</section>
</section>
<section id="packages">
<h4><a class="toc-backref" href="#packages" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">[[packages]]</span></code></a></h4>
<ul class="simple">
<li>Array of tables</li>
<li>The array contains all data on the nodes of the dependency graph.</li>
<li>Lockers SHOULD record packages in order by <code class="docutils literal notranslate"><span class="pre">name</span></code>
lexicographically, <code class="docutils literal notranslate"><span class="pre">version</span></code> by its Python <a class="reference external" href="https://packaging.python.org/en/latest/specifications/version-specifiers/">version specifiers</a>
ordering, and then by <code class="docutils literal notranslate"><span class="pre">groups</span></code> following Pythons sort order for lists of
strings (i.e. item by item, then by length as a tiebreaker).</li>
</ul>
<section id="packages-name">
<h5><a class="toc-backref" href="#packages-name" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.name</span></code></a></h5>
<ul class="simple">
<li>String</li>
<li>The <a class="reference external" href="https://packaging.python.org/en/latest/specifications/name-normalization/#name-normalization">normalized name</a> of the package.</li>
</ul>
</section>
<section id="packages-version">
<h5><a class="toc-backref" href="#packages-version" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.version</span></code></a></h5>
<ul class="simple">
<li>String</li>
<li>The version of the package.</li>
</ul>
</section>
<section id="packages-groups">
<h5><a class="toc-backref" href="#packages-groups" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.groups</span></code></a></h5>
<ul class="simple">
<li>Array of strings</li>
<li>Associates this table with the <code class="docutils literal notranslate"><span class="pre">group.name</span></code> entries of the same names.</li>
</ul>
</section>
<section id="packages-index-url">
<h5><a class="toc-backref" href="#packages-index-url" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.index-url</span></code></a></h5>
<ul class="simple">
<li>Optional</li>
<li>String</li>
<li>Stores the <a class="reference external" href="https://packaging.python.org/en/latest/specifications/simple-repository-api/#project-list">project index</a> URL from the <a class="reference external" href="https://packaging.python.org/en/latest/specifications/simple-repository-api/">Simple Repository API</a>.</li>
<li>Useful for generating Packaging URLs (aka PURLs).</li>
<li>When possible, lockers SHOULD include this to assist with generating
<a class="reference external" href="https://www.cisa.gov/sbom">software bill of materials</a> (aka SBOMs).</li>
</ul>
</section>
<section id="packages-direct">
<h5><a class="toc-backref" href="#packages-direct" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.direct</span></code></a></h5>
<ul class="simple">
<li>Optional (defaults to <code class="docutils literal notranslate"><span class="pre">false</span></code>)</li>
<li>Boolean</li>
<li>Represents whether the installation is via a <a class="reference external" href="https://packaging.python.org/en/latest/specifications/direct-url/">direct URL reference</a>.</li>
</ul>
</section>
<section id="packages-requires-python">
<h5><a class="toc-backref" href="#packages-requires-python" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.requires-python</span></code></a></h5>
<ul class="simple">
<li>String</li>
<li>Holds the <a class="reference external" href="https://packaging.python.org/en/latest/specifications/version-specifiers/">version specifiers</a> for Python version compatibility for the
package and version.</li>
<li>The value MUST match whats provided by the package version, if available, via
<a class="reference external" href="https://packaging.python.org/en/latest/specifications/core-metadata/#core-metadata-requires-python" title="(in Python Packaging User Guide)"><span>Requires-Python</span></a>.</li>
</ul>
</section>
<section id="packages-dependencies">
<h5><a class="toc-backref" href="#packages-dependencies" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">[[packages.dependencies]]</span></code></a></h5>
<ul class="simple">
<li>Array of tables</li>
<li>A record of the dependency requirements of the package and version.</li>
<li>The values MUST semantically match whats provided by the package version via
<a class="reference external" href="https://packaging.python.org/en/latest/specifications/core-metadata/#core-metadata-requires-dist" title="(in Python Packaging User Guide)"><span>Requires-Dist (multiple use)</span></a> for all dependencies referenced
in the lock file (i.e all base dependencies plus all dependencies for extras
referenced in the lock file); lock files MAY list all dependencies for unused
extras if desired.</li>
<li>Values in the array SHOULD be written as inline tables, sorted
lexicographically by <code class="docutils literal notranslate"><span class="pre">name</span></code>, then by <code class="docutils literal notranslate"><span class="pre">feature</span></code> with the lack of that key
sorting first.</li>
</ul>
<section id="packages-dependencies-name">
<h6><a class="toc-backref" href="#packages-dependencies-name" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.dependencies.name</span></code></a></h6>
<p>See <code class="docutils literal notranslate"><span class="pre">groups.requirements.name</span></code>.</p>
</section>
<section id="packages-dependencies-extras">
<h6><a class="toc-backref" href="#packages-dependencies-extras" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.dependencies.extras</span></code></a></h6>
<p>See <code class="docutils literal notranslate"><span class="pre">groups.requirements.extras</span></code>.</p>
</section>
<section id="packages-dependencies-version">
<h6><a class="toc-backref" href="#packages-dependencies-version" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.dependencies.version</span></code></a></h6>
<p>See <code class="docutils literal notranslate"><span class="pre">groups.requirements.version</span></code>.</p>
</section>
<section id="packages-dependencies-marker">
<h6><a class="toc-backref" href="#packages-dependencies-marker" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.dependencies.marker</span></code></a></h6>
<p>See <code class="docutils literal notranslate"><span class="pre">groups.requirements.marker</span></code>.</p>
</section>
<section id="packages-dependencies-feature">
<h6><a class="toc-backref" href="#packages-dependencies-feature" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.dependencies.feature</span></code></a></h6>
<ul class="simple">
<li>Optional</li>
<li>String</li>
<li>The optional feature/<a class="reference external" href="https://packaging.python.org/en/latest/specifications/core-metadata/#core-metadata-provides-extra" title="(in Python Packaging User Guide)"><span>Provides-Extra (multiple use)</span></a> that this
requirement is conditional on.</li>
</ul>
</section>
</section>
<section id="packages-editable">
<h5><a class="toc-backref" href="#packages-editable" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.editable</span></code></a></h5>
<ul class="simple">
<li>Optional (defaults to <code class="docutils literal notranslate"><span class="pre">false</span></code>)</li>
<li>Boolean</li>
<li>Specifies whether the package should be installed in editable mode.</li>
</ul>
</section>
<section id="packages-source-tree">
<h5><a class="toc-backref" href="#packages-source-tree" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">[packages.source-tree]</span></code></a></h5>
<ul class="simple">
<li>Optional</li>
<li>Table</li>
<li>For recording where to find the <a class="reference external" href="https://packaging.python.org/en/latest/specifications/source-distribution-format/#source-trees">source tree</a> for the package version.</li>
<li>Lockers SHOULD write this table inline.</li>
<li>Support for source trees by installers is optional.</li>
<li>If support is provided by an installer it SHOULD be opt-in.</li>
<li>If multiple source trees are provided, installers MUST prefer either the
<code class="docutils literal notranslate"><span class="pre">vcs</span></code> option or a file for security/reproducibility due to their commit or
hash, respectively.</li>
</ul>
<section id="packages-source-tree-vcs">
<h6><a class="toc-backref" href="#packages-source-tree-vcs" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.vcs</span></code></a></h6>
<ul class="simple">
<li>Optional</li>
<li>String</li>
<li>If specifying a VCS, the type of version control system used.</li>
<li>The valid values are specified by the
<a class="reference external" href="https://packaging.python.org/en/latest/specifications/direct-url-data-structure/#registered-vcs">registered VCSs</a>
of the direct URL data structure.</li>
</ul>
</section>
<section id="packages-source-tree-path">
<h6><a class="toc-backref" href="#packages-source-tree-path" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.path</span></code></a></h6>
<ul class="simple">
<li>Required if <code class="docutils literal notranslate"><span class="pre">url</span></code> is not set</li>
<li>String</li>
<li>A path to the source tree, which may be absolute or relative.</li>
<li>If the path is relative it MUST be relative to the lock file.</li>
<li>The path may either be to a directory, file archive, or VCS checkout if
<code class="docutils literal notranslate"><span class="pre">vcs</span></code> if is specified.</li>
</ul>
</section>
<section id="packages-source-tree-url">
<h6><a class="toc-backref" href="#packages-source-tree-url" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.url</span></code></a></h6>
<ul class="simple">
<li>Required if <code class="docutils literal notranslate"><span class="pre">path</span></code> is not set</li>
<li>String</li>
<li>A URL to a file archive containing the source tree, or a VCS checkout if
<code class="docutils literal notranslate"><span class="pre">vcs</span></code> is specified.</li>
</ul>
</section>
<section id="packages-source-tree-commit">
<h6><a class="toc-backref" href="#packages-source-tree-commit" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.commit</span></code></a></h6>
<ul class="simple">
<li>Required if <code class="docutils literal notranslate"><span class="pre">vcs</span></code> is set</li>
<li>String</li>
<li>The commit ID for the repository which represents the package and version.</li>
<li>The value MUST be immutable for the VCS for security purposes
(e.g. no Git tags).</li>
</ul>
</section>
<section id="packages-source-tree-size">
<h6><a class="toc-backref" href="#packages-source-tree-size" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.size</span></code></a></h6>
<ul class="simple">
<li>Optional</li>
<li>Integer</li>
<li>The size in bytes for the source tree if it is a file.</li>
<li>Installers MUST verify the file size matches this value.</li>
</ul>
</section>
<section id="packages-source-tree-hash">
<h6><a class="toc-backref" href="#packages-source-tree-hash" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.hash</span></code></a></h6>
<ul class="simple">
<li>Required if <code class="docutils literal notranslate"><span class="pre">url</span></code> or <code class="docutils literal notranslate"><span class="pre">path</span></code> points to a file</li>
<li>String</li>
<li>The hash value of the file contents using the hash algorithm specified by
<code class="docutils literal notranslate"><span class="pre">hash-algorithm</span></code>.</li>
<li>Installers MUST verify the hash matches the file.</li>
</ul>
</section>
</section>
<section id="packages-sdist">
<h5><a class="toc-backref" href="#packages-sdist" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">[packages.sdist]</span></code></a></h5>
<ul class="simple">
<li>Optional</li>
<li>Table</li>
<li>The location of a source distribution as specified by
<a class="reference external" href="https://packaging.python.org/en/latest/specifications/source-distribution-format/#source-distribution-format" title="(in Python Packaging User Guide)"><span>Source distribution format</span></a>.</li>
<li>Lockers SHOULD write the table inline.</li>
<li>Support for source distributions by installers is optional.</li>
<li>If support is provided by an installer it SHOULD be opt-in.</li>
</ul>
<section id="packages-sdist-url">
<h6><a class="toc-backref" href="#packages-sdist-url" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.sdist.url</span></code></a></h6>
<ul class="simple">
<li>Optional; mutually-exclusive with <code class="docutils literal notranslate"><span class="pre">path</span></code></li>
<li>String</li>
<li>The URL to the file.</li>
</ul>
</section>
<section id="packages-sdist-path">
<h6><a class="toc-backref" href="#packages-sdist-path" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.sdist.path</span></code></a></h6>
<ul class="simple">
<li>Optional; mutually-exclusive with <code class="docutils literal notranslate"><span class="pre">url</span></code></li>
<li>String</li>
<li>A path to the file, which may be absolute or relative.</li>
<li>If the path is relative it MUST be relative to the lock file.</li>
</ul>
</section>
<section id="packages-sdist-upload-time">
<h6><a class="toc-backref" href="#packages-sdist-upload-time" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.sdist.upload-time</span></code></a></h6>
<ul class="simple">
<li>Optional and only applicable when <code class="docutils literal notranslate"><span class="pre">url</span></code> is specified</li>
<li>Offset date time</li>
<li>The upload date and time of the file as specified by a valid ISO 8601
date/time string for the <code class="docutils literal notranslate"><span class="pre">.files[].&quot;upload-time&quot;</span></code> field in the JSON
version of <a class="reference external" href="https://packaging.python.org/en/latest/specifications/simple-repository-api/#simple-repository-api" title="(in Python Packaging User Guide)"><span>Simple repository API</span></a>.</li>
</ul>
</section>
<section id="packages-sdist-size">
<h6><a class="toc-backref" href="#packages-sdist-size" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.sdist.size</span></code></a></h6>
<ul class="simple">
<li>Optional</li>
<li>Integer</li>
<li>The size of the file in bytes.</li>
<li>Installers MUST verify the file size matches this value.</li>
</ul>
</section>
<section id="packages-sdist-hash">
<h6><a class="toc-backref" href="#packages-sdist-hash" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.sdist.hash</span></code></a></h6>
<ul class="simple">
<li>String</li>
<li>The hash value of the file contents using the hash algorithm specified by
<code class="docutils literal notranslate"><span class="pre">hash-algorithm</span></code>.</li>
<li>Installers MUST verify the hash matches the file.</li>
</ul>
</section>
</section>
<section id="packages-wheels">
<h5><a class="toc-backref" href="#packages-wheels" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">[[packages.wheels]]</span></code></a></h5>
<ul class="simple">
<li>Optional</li>
<li>Array of tables</li>
<li>For recording the wheel files as specified by
<a class="reference external" href="https://packaging.python.org/en/latest/specifications/binary-distribution-format/#binary-distribution-format" title="(in Python Packaging User Guide)"><span>Binary distribution format</span></a> for the package version.</li>
<li>Lockers SHOULD write the table inline.</li>
<li>Lockers SHOULD sort the array values lexicographically by <code class="docutils literal notranslate"><span class="pre">tag</span></code>.</li>
</ul>
<section id="packages-wheels-tags">
<h6><a class="toc-backref" href="#packages-wheels-tags" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.wheels.tags</span></code></a></h6>
<ul class="simple">
<li>Array of string</li>
<li>The uncompressed tag portion of the wheel file: Python, ABI, and platform.</li>
<li>Lockers MUST make sure the tag values are unique within the
<code class="docutils literal notranslate"><span class="pre">packages.wheels</span></code> array.</li>
</ul>
</section>
<section id="packages-wheels-build">
<h6><a class="toc-backref" href="#packages-wheels-build" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.wheels.build</span></code></a></h6>
<ul class="simple">
<li>Optional</li>
<li>String</li>
<li>The build tag for the wheel file (if appropriate).</li>
</ul>
</section>
<section id="packages-wheels-url">
<h6><a class="toc-backref" href="#packages-wheels-url" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.wheels.url</span></code></a></h6>
<p>See <code class="docutils literal notranslate"><span class="pre">packages.sdist.url</span></code>.</p>
</section>
<section id="packages-wheels-path">
<h6><a class="toc-backref" href="#packages-wheels-path" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.wheels.path</span></code></a></h6>
<p>See <code class="docutils literal notranslate"><span class="pre">packages.sdist.path</span></code>.</p>
</section>
<section id="packages-wheels-upload-time">
<h6><a class="toc-backref" href="#packages-wheels-upload-time" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.wheels.upload-time</span></code></a></h6>
<p>See <code class="docutils literal notranslate"><span class="pre">packages.sdist.upload-time</span></code>.</p>
</section>
<section id="packages-wheels-size">
<h6><a class="toc-backref" href="#packages-wheels-size" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.wheels.size</span></code></a></h6>
<p>See <code class="docutils literal notranslate"><span class="pre">packages.sdist.size</span></code>.</p>
</section>
<section id="packages-wheels-hash">
<h6><a class="toc-backref" href="#packages-wheels-hash" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">packages.wheels.hash</span></code></a></h6>
<p>See <code class="docutils literal notranslate"><span class="pre">packages.sdist.hash</span></code>.</p>
</section>
</section>
<section id="packages-tool">
<h5><a class="toc-backref" href="#packages-tool" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">[packages.tool]</span></code></a></h5>
<ul class="simple">
<li>Optional</li>
<li>Table</li>
<li>Similar usage as that of the <code class="docutils literal notranslate"><span class="pre">[tool]</span></code> table from the
<a class="reference external" href="https://packaging.python.org/en/latest/specifications/pyproject-toml/#pyproject-toml-specification">pyproject.toml specification</a> , but at the package version level instead of
at the lock file level (which is also available via <code class="docutils literal notranslate"><span class="pre">[tool]</span></code>).</li>
<li>Useful for scoping package version/release details (e.g., recording signing
identities to then use to verify package integrity separately from where the
package is hosted, prototyping future extensions to this file format, etc.).</li>
</ul>
</section>
</section>
<section id="tool">
<h4><a class="toc-backref" href="#tool" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">[tool]</span></code></a></h4>
<ul class="simple">
<li>Optional</li>
<li>Table</li>
<li>Same usage as that of the equivalent <code class="docutils literal notranslate"><span class="pre">[tool]</span></code> table from the
<a class="reference external" href="https://packaging.python.org/en/latest/specifications/pyproject-toml/#pyproject-toml-specification">pyproject.toml specification</a>.</li>
</ul>
</section>
</section>
<section id="examples">
<h3><a class="toc-backref" href="#examples" role="doc-backlink">Examples</a></h3>
<div class="highlight-TOML notranslate"><div class="highlight"><pre><span></span><span class="n">version</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;1.0&#39;</span>
<span class="n">hash-algorithm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;sha256&#39;</span>
<span class="k">[locker]</span>
<span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;mousebender&#39;</span>
<span class="n">version</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;pep&#39;</span>
<span class="n">run</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">module</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;mousebender&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">args</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;lock&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;--platform&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;cpython3.12-manylinux2014-x64&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;--platform&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;cpython3.12-windows-x64&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;cattrs&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;numpy&#39;</span><span class="p">]</span><span class="w"> </span><span class="p">}</span>
<span class="k">[[groups]]</span>
<span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;Default&#39;</span>
<span class="n">requirements</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;cattrs&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;numpy&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="p">]</span>
<span class="k">[[packages]]</span>
<span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;attrs&#39;</span>
<span class="n">version</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;24.2.0&#39;</span>
<span class="n">groups</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;Default&#39;</span><span class="p">]</span>
<span class="n">index_url</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;https://pypi.org/simple/attrs&#39;</span>
<span class="n">direct</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span>
<span class="n">requires_python</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;&gt;=3.7&#39;</span>
<span class="n">dependencies</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;importlib-metadata&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;python_version &lt; &quot;3.8&quot;&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;cloudpickle&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;platform_python_implementation == &quot;CPython&quot;&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;benchmark&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;hypothesis&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;benchmark&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;mypy&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=1.11.1&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;platform_python_implementation == &quot;CPython&quot; and python_version &gt;= &quot;3.9&quot;&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;benchmark&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pympler&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;benchmark&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pytest-codspeed&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;benchmark&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pytest-mypy-plugins&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;platform_python_implementation == &quot;CPython&quot; and python_version &gt;= &quot;3.9&quot; and python_version &lt; &quot;3.13&quot;&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;benchmark&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pytest-xdist&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">extras</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;psutil&#39;</span><span class="p">],</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;benchmark&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pytest&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=4.3.0&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;benchmark&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;cloudpickle&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;platform_python_implementation == &quot;CPython&quot;&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;cov&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;coverage&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">extras</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;toml&#39;</span><span class="p">],</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=5.3&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;cov&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;hypothesis&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;cov&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;mypy&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=1.11.1&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;platform_python_implementation == &quot;CPython&quot; and python_version &gt;= &quot;3.9&quot;&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;cov&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pympler&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;cov&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pytest-mypy-plugins&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;platform_python_implementation == &quot;CPython&quot; and python_version &gt;= &quot;3.9&quot; and python_version &lt; &quot;3.13&quot;&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;cov&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pytest-xdist&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">extras</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;psutil&#39;</span><span class="p">],</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;cov&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pytest&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=4.3.0&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;cov&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;cloudpickle&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;platform_python_implementation == &quot;CPython&quot;&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;dev&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;hypothesis&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;dev&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;mypy&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=1.11.1&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;platform_python_implementation == &quot;CPython&quot; and python_version &gt;= &quot;3.9&quot;&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;dev&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pre-commit&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;dev&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pympler&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;dev&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pytest-mypy-plugins&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;platform_python_implementation == &quot;CPython&quot; and python_version &gt;= &quot;3.9&quot; and python_version &lt; &quot;3.13&quot;&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;dev&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pytest-xdist&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">extras</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;psutil&#39;</span><span class="p">],</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;dev&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pytest&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=4.3.0&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;dev&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;cogapp&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;docs&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;furo&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;docs&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;myst-parser&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;docs&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;sphinx&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;docs&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;sphinx-notfound-page&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;docs&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;sphinxcontrib-towncrier&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;docs&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;towncrier&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&lt;24.7&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;docs&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;cloudpickle&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;platform_python_implementation == &quot;CPython&quot;&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;tests&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;hypothesis&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;tests&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;mypy&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=1.11.1&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;platform_python_implementation == &quot;CPython&quot; and python_version &gt;= &quot;3.9&quot;&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;tests&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pympler&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;tests&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pytest-mypy-plugins&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;platform_python_implementation == &quot;CPython&quot; and python_version &gt;= &quot;3.9&quot; and python_version &lt; &quot;3.13&quot;&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;tests&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pytest-xdist&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">extras</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;psutil&#39;</span><span class="p">],</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;tests&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pytest&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=4.3.0&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;tests&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;mypy&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=1.11.1&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;platform_python_implementation == &quot;CPython&quot; and python_version &gt;= &quot;3.9&quot;&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;tests-mypy&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pytest-mypy-plugins&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;platform_python_implementation == &quot;CPython&quot; and python_version &gt;= &quot;3.9&quot; and python_version &lt; &quot;3.13&quot;&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;tests-mypy&#39;</span><span class="w"> </span><span class="p">}</span>
<span class="p">]</span>
<span class="n">editable</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span>
<span class="n">wheels</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">tags</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;py3-none-any&#39;</span><span class="p">],</span><span class="w"> </span><span class="n">url</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;https://files.pythonhosted.org/packages/6a/21/5b6702a7f963e95456c0de2d495f67bf5fd62840ac655dc451586d23d39a/attrs-24.2.0-py3-none-any.whl&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">hash</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">upload_time</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="ld">2024-08-06T14:37:36.958006+00:00</span><span class="p">,</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">63001</span><span class="w"> </span><span class="p">}</span>
<span class="p">]</span>
<span class="k">[[packages]]</span>
<span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;cattrs&#39;</span>
<span class="n">version</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;24.1.2&#39;</span>
<span class="n">groups</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;Default&#39;</span><span class="p">]</span>
<span class="n">index_url</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;https://pypi.org/simple/cattrs&#39;</span>
<span class="n">direct</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span>
<span class="n">requires_python</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;&gt;=3.8&#39;</span>
<span class="n">dependencies</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;attrs&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=23.1.0&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;exceptiongroup&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=1.1.1&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;python_version &lt; &quot;3.11&quot;&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;typing-extensions&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;!=4.6.3,&gt;=4.1.0&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;python_version &lt; &quot;3.11&quot;&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pymongo&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=4.4.0&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;bson&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;cbor2&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=5.4.6&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;cbor2&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;msgpack&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=1.0.5&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;msgpack&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;msgspec&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=0.18.5&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;implementation_name == &quot;cpython&quot;&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;msgspec&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;orjson&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=3.9.2&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">marker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;implementation_name == &quot;cpython&quot;&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;orjson&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pyyaml&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=6.0&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;pyyaml&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;tomlkit&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=0.11.8&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;tomlkit&#39;</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;ujson&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;&gt;=5.7.0&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">feature</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;ujson&#39;</span><span class="w"> </span><span class="p">}</span>
<span class="p">]</span>
<span class="n">editable</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span>
<span class="n">wheels</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">tags</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;py3-none-any&#39;</span><span class="p">],</span><span class="w"> </span><span class="n">url</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;https://files.pythonhosted.org/packages/c8/d5/867e75361fc45f6de75fe277dd085627a9db5ebb511a87f27dc1396b5351/cattrs-24.1.2-py3-none-any.whl&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">hash</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;67c7495b760168d931a10233f979b28dc04daf853b30752246f4f8471c6d68d0&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">upload_time</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="ld">2024-09-22T14:58:34.812643+00:00</span><span class="p">,</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">66446</span><span class="w"> </span><span class="p">}</span>
<span class="p">]</span>
<span class="k">[[packages]]</span>
<span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;numpy&#39;</span>
<span class="n">version</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;2.1.2&#39;</span>
<span class="n">groups</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;Default&#39;</span><span class="p">]</span>
<span class="n">index_url</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;https://pypi.org/simple/numpy&#39;</span>
<span class="n">direct</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span>
<span class="n">requires_python</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;&gt;=3.10&#39;</span>
<span class="n">dependencies</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span>
<span class="p">]</span>
<span class="n">editable</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span>
<span class="n">wheels</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">tags</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;cp312-cp312-manylinux2014_x86_64&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;cp312-cp312-manylinux_2_17_x86_64&#39;</span><span class="p">],</span><span class="w"> </span><span class="n">url</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;https://files.pythonhosted.org/packages/9b/b4/e3c7e6fab0f77fff6194afa173d1f2342073d91b1d3b4b30b17c3fb4407a/numpy-2.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">hash</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;6d95f286b8244b3649b477ac066c6906fbb2905f8ac19b170e2175d3d799f4df&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">upload_time</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="ld">2024-10-05T18:36:20.729642+00:00</span><span class="p">,</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">16041825</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">tags</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;cp312-cp312-win_amd64&#39;</span><span class="p">],</span><span class="w"> </span><span class="n">url</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;https://files.pythonhosted.org/packages/4c/79/73735a6a5dad6059c085f240a4e74c9270feccd2bc66e4d31b5ca01d329c/numpy-2.1.2-cp312-cp312-win_amd64.whl&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">hash</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s1">&#39;456e3b11cb79ac9946c822a56346ec80275eaf2950314b249b512896c0d2505e&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">upload_time</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="ld">2024-10-05T18:37:38.159022+00:00</span><span class="p">,</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">12568254</span><span class="w"> </span><span class="p">}</span>
<span class="p">]</span>
</pre></div>
</div>
</section>
<section id="expectations-for-lockers">
<h3><a class="toc-backref" href="#expectations-for-lockers" role="doc-backlink">Expectations for Lockers</a></h3>
<ul class="simple">
<li>Lockers MUST make sure that entering the dependency graph via a specific group
will not lead to ambiguity for installers as to which value in
<code class="docutils literal notranslate"><span class="pre">[[packages]]</span></code> to install for any environment (this can be controlled for
via <code class="docutils literal notranslate"><span class="pre">packages.version</span></code> and <code class="docutils literal notranslate"><span class="pre">packages.groups</span></code>).</li>
<li>Lockers SHOULD try to make all logically related groups resolve together
(i.e. no ambiguity if grouped together).</li>
<li>If a <code class="docutils literal notranslate"><span class="pre">groups.project</span></code> would have extras that cause ambiguity or installation
failure due to conflicts between the extras, the locker MAY create
separate <code class="docutils literal notranslate"><span class="pre">groups.requirements</span></code> entries instead, otherwise the locker MUST
raise an error.</li>
<li>Lockers MAY try to lock for multiple environments in a single lock file.</li>
<li>Lockers MAY try to update a lock file containing <code class="docutils literal notranslate"><span class="pre">[tool]</span></code> and
<code class="docutils literal notranslate"><span class="pre">[packages.tool]</span></code> for other tools than themselves.</li>
<li>Lockers MAY want to provide a way to let users provide the information
necessary to lock for other environments, e.g., supporting a JSON
file format which specifies wheel tags and marker values.</li>
</ul>
<div class="highlight-JSON notranslate"><div class="highlight"><pre><span></span><span class="p">{</span>
<span class="w"> </span><span class="nt">&quot;marker-values&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nt">&quot;&lt;marker&gt;&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;&lt;value&gt;&quot;</span><span class="p">},</span>
<span class="w"> </span><span class="nt">&quot;wheel-tags&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;&lt;tag&gt;&quot;</span><span class="p">]</span>
<span class="p">}</span>
</pre></div>
</div>
</section>
<section id="expectations-for-installers">
<h3><a class="toc-backref" href="#expectations-for-installers" role="doc-backlink">Expectations for Installers</a></h3>
<ul class="simple">
<li>Installers MAY support installation of non-binary files
(i.e. source trees and source distributions), but are not required to.</li>
<li>Installers MUST provide a way to avoid non-binary file installation for
reproducibility and security purposes.</li>
<li>Installers SHOULD make it opt-in to use non-binary file installation to
facilitate a secure-by-default approach.</li>
<li>If a traversal of the graph leads to any ambiguity as to what package version
to install (i.e. more than one package version qualifies), an error MUST be
raised.</li>
<li>Installers MUST only consider package versions included in any selected
groups (i.e. installers cannot consider packages outside of the groups
selected to install from).</li>
<li>Installers MUST error out if a package version lacks a way to install into the
chosen environment.</li>
<li>Installers MUST support installing into an empty environment.</li>
</ul>
<section id="pseudo-code">
<h4><a class="toc-backref" href="#pseudo-code" role="doc-backlink">Pseudo-Code</a></h4>
<div class="highlight-Python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">UnsatisfiableError</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Raised when a requirement cannot be satisfied.&quot;&quot;&quot;</span>
<span class="k">class</span> <span class="nc">AmbiguityError</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Raised when a requirement has multiple solutions.&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">install_packages</span><span class="p">(</span><span class="n">lock_file_contents</span><span class="p">):</span>
<span class="c1"># Hard-coded out of laziness.</span>
<span class="n">packages</span> <span class="o">=</span> <span class="n">choose_packages</span><span class="p">(</span><span class="n">lock_file_contents</span><span class="p">,</span> <span class="p">(</span><span class="n">GROUP_NAME</span><span class="p">,</span> <span class="nb">frozenset</span><span class="p">()))</span>
<span class="k">for</span> <span class="n">package</span> <span class="ow">in</span> <span class="n">packages</span><span class="p">:</span>
<span class="n">tags</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">packaging</span><span class="o">.</span><span class="n">tags</span><span class="o">.</span><span class="n">sys_tags</span><span class="p">())</span>
<span class="k">for</span> <span class="n">tag</span> <span class="ow">in</span> <span class="n">tags</span><span class="p">:</span> <span class="c1"># Prioritize by tag order.</span>
<span class="n">tag_str</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">tag</span><span class="p">)</span>
<span class="k">for</span> <span class="n">wheel</span> <span class="ow">in</span> <span class="n">package</span><span class="p">[</span><span class="s2">&quot;wheels&quot;</span><span class="p">]:</span>
<span class="k">if</span> <span class="n">tag_str</span> <span class="ow">in</span> <span class="n">wheel</span><span class="p">[</span><span class="s2">&quot;tags&quot;</span><span class="p">]:</span>
<span class="k">break</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">continue</span>
<span class="k">break</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">UnsatisfiableError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;No wheel for </span><span class="si">{</span><span class="n">package</span><span class="p">[</span><span class="s1">&#39;name&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">package</span><span class="p">[</span><span class="s1">&#39;version&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Installing </span><span class="si">{</span><span class="n">package</span><span class="p">[</span><span class="s1">&#39;name&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">package</span><span class="p">[</span><span class="s1">&#39;version&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">tag_str</span><span class="si">}</span><span class="s2">)&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">choose_packages</span><span class="p">(</span><span class="n">lock_file_data</span><span class="p">,</span> <span class="o">*</span><span class="n">selected_groups</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Select the package versions that should be installed based on the requested groups.</span>
<span class="sd"> &#39;selected_groups&#39; is a sequence of two-item tuples, representing a group name and</span>
<span class="sd"> optionally any requested extras if the group is a project.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">group_names</span> <span class="o">=</span> <span class="nb">frozenset</span><span class="p">(</span><span class="n">operator</span><span class="o">.</span><span class="n">itemgetter</span><span class="p">(</span><span class="mi">0</span><span class="p">)(</span><span class="n">group</span><span class="p">)</span> <span class="k">for</span> <span class="n">group</span> <span class="ow">in</span> <span class="n">selected_groups</span><span class="p">)</span>
<span class="n">available_packages</span> <span class="o">=</span> <span class="p">{}</span> <span class="c1"># The packages in the selected groups.</span>
<span class="k">for</span> <span class="n">pkg</span> <span class="ow">in</span> <span class="n">lock_file_data</span><span class="p">[</span><span class="s2">&quot;packages&quot;</span><span class="p">]:</span>
<span class="k">if</span> <span class="nb">frozenset</span><span class="p">(</span><span class="n">pkg</span><span class="p">[</span><span class="s2">&quot;groups&quot;</span><span class="p">])</span> <span class="o">&amp;</span> <span class="n">group_names</span><span class="p">:</span>
<span class="n">available_packages</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="n">pkg</span><span class="p">[</span><span class="s2">&quot;name&quot;</span><span class="p">],</span> <span class="p">[])</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">pkg</span><span class="p">)</span>
<span class="n">selected_packages</span> <span class="o">=</span> <span class="p">{}</span> <span class="c1"># The package versions that have been selected.</span>
<span class="n">handled_extras</span> <span class="o">=</span> <span class="p">{}</span> <span class="c1"># The extras that have been handled.</span>
<span class="n">requirements</span> <span class="o">=</span> <span class="p">[]</span> <span class="c1"># A stack of requirements to satisfy.</span>
<span class="c1"># First, get our starting list of requirements.</span>
<span class="k">for</span> <span class="n">group</span> <span class="ow">in</span> <span class="n">selected_groups</span><span class="p">:</span>
<span class="n">requirements</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">gather_requirements</span><span class="p">(</span><span class="n">lock_file_data</span><span class="p">,</span> <span class="n">group</span><span class="p">))</span>
<span class="c1"># Next, go through the requirements and try to find a **single** package version</span>
<span class="c1"># that satisfies each requirement.</span>
<span class="k">while</span> <span class="n">requirements</span><span class="p">:</span>
<span class="n">req</span> <span class="o">=</span> <span class="n">requirements</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
<span class="c1"># Ignore requirements whose markers disqualify it.</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">applies_to_env</span><span class="p">(</span><span class="n">req</span><span class="p">):</span>
<span class="k">continue</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">req</span><span class="p">[</span><span class="s2">&quot;name&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="n">pkg</span> <span class="o">:=</span> <span class="n">selected_packages</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
<span class="c1"># Safety check that the cross-section of groups doesn&#39;t cause issues.</span>
<span class="c1"># It somewhat assumes the locker didn&#39;t mess up such that there would be</span>
<span class="c1"># ambiguity by what package version was initially selected.</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">version_satisfies</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">pkg</span><span class="p">):</span>
<span class="k">raise</span> <span class="n">UnsatisfiableError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;requirement </span><span class="si">{</span><span class="n">req</span><span class="si">!r}</span><span class="s2"> not satisfied by &quot;</span>
<span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">selected_packages</span><span class="p">[</span><span class="n">req</span><span class="p">[</span><span class="s1">&#39;name&#39;</span><span class="p">]]</span><span class="si">!r}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="k">if</span> <span class="s2">&quot;extras&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">req</span><span class="p">:</span>
<span class="k">continue</span>
<span class="n">needed_extras</span> <span class="o">=</span> <span class="n">req</span><span class="p">[</span><span class="s2">&quot;extras&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="n">extras</span> <span class="o">:=</span> <span class="n">handled_extras</span><span class="o">.</span><span class="n">set_default</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="nb">set</span><span class="p">()))</span><span class="o">.</span><span class="n">difference</span><span class="p">(</span>
<span class="n">needed_extras</span>
<span class="p">):</span>
<span class="k">continue</span>
<span class="c1"># This isn&#39;t optimal as we may tread over the same extras multiple times,</span>
<span class="c1"># but eventually the maximum set of extras for the package will be handled</span>
<span class="c1"># and thus the above guard will short-circuit adding any more requirements.</span>
<span class="n">extras</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">needed_extras</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># Raises UnsatisfiableError or AmbiguityError if no suitable, single package</span>
<span class="c1"># version is found.</span>
<span class="n">pkg</span> <span class="o">=</span> <span class="n">compatible_package_version</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">available_packages</span><span class="p">[</span><span class="n">req</span><span class="p">[</span><span class="s2">&quot;name&quot;</span><span class="p">]])</span>
<span class="n">selected_packages</span><span class="p">[</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="n">pkg</span>
<span class="n">requirements</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">dependencies</span><span class="p">(</span><span class="n">pkg</span><span class="p">,</span> <span class="n">req</span><span class="p">))</span>
<span class="k">return</span> <span class="n">selected_packages</span><span class="o">.</span><span class="n">values</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">gather_requirements</span><span class="p">(</span><span class="n">locked_file_data</span><span class="p">,</span> <span class="n">group</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Return a collection of all requirements for a group.&quot;&quot;&quot;</span>
<span class="c1"># Hard-coded to support `groups.requirements` out of laziness.</span>
<span class="n">group_name</span><span class="p">,</span> <span class="n">_extras</span> <span class="o">=</span> <span class="n">group</span>
<span class="k">for</span> <span class="n">group</span> <span class="ow">in</span> <span class="n">locked_file_data</span><span class="p">[</span><span class="s2">&quot;groups&quot;</span><span class="p">]:</span>
<span class="k">if</span> <span class="n">group</span><span class="p">[</span><span class="s2">&quot;name&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="n">group_name</span><span class="p">:</span>
<span class="k">return</span> <span class="n">group</span><span class="p">[</span><span class="s2">&quot;requirements&quot;</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Group </span><span class="si">{</span><span class="n">group_name</span><span class="si">!r}</span><span class="s2"> not found in lock file&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">applies_to_env</span><span class="p">(</span><span class="n">requirement</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Check if the requirement applies to the current environment.&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">markers</span> <span class="o">=</span> <span class="n">requirement</span><span class="p">[</span><span class="s2">&quot;marker&quot;</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">True</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="n">packaging</span><span class="o">.</span><span class="n">markers</span><span class="o">.</span><span class="n">Marker</span><span class="p">(</span><span class="n">markers</span><span class="p">)</span><span class="o">.</span><span class="n">evaluate</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">version_satisfies</span><span class="p">(</span><span class="n">requirement</span><span class="p">,</span> <span class="n">package</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Check if the package version satisfies the requirement.&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">raw_specifier</span> <span class="o">=</span> <span class="n">requirement</span><span class="p">[</span><span class="s2">&quot;version&quot;</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">True</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">specifier</span> <span class="o">=</span> <span class="n">packaging</span><span class="o">.</span><span class="n">specifiers</span><span class="o">.</span><span class="n">SpecifierSet</span><span class="p">(</span><span class="n">raw_specifier</span><span class="p">)</span>
<span class="k">return</span> <span class="n">specifier</span><span class="o">.</span><span class="n">contains</span><span class="p">(</span><span class="n">package</span><span class="p">[</span><span class="s2">&quot;version&quot;</span><span class="p">],</span> <span class="n">prereleases</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">compatible_package_version</span><span class="p">(</span><span class="n">requirement</span><span class="p">,</span> <span class="n">available_packages</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Return the package version that satisfies the requirement.</span>
<span class="sd"> If no package version can satisfy the requirement, raise UnsatisfiableError. If</span>
<span class="sd"> multiple package versions can satisfy the requirement, raise AmbiguityError.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">possible_packages</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">pkg</span> <span class="k">for</span> <span class="n">pkg</span> <span class="ow">in</span> <span class="n">available_packages</span> <span class="k">if</span> <span class="n">version_satisfies</span><span class="p">(</span><span class="n">requirement</span><span class="p">,</span> <span class="n">pkg</span><span class="p">)</span>
<span class="p">]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">possible_packages</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">UnsatisfiableError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;No package version satisfies </span><span class="si">{</span><span class="n">requirement</span><span class="si">!r}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">elif</span> <span class="nb">len</span><span class="p">(</span><span class="n">possible_packages</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">AmbiguityError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Multiple package versions satisfy </span><span class="si">{</span><span class="n">requirement</span><span class="si">!r}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">possible_packages</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">dependencies</span><span class="p">(</span><span class="n">package</span><span class="p">,</span> <span class="n">requirement</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Return the dependencies of the package.</span>
<span class="sd"> The extras from the requirement will extend the base requirements as needed.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">applicable_deps</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">extras</span> <span class="o">=</span> <span class="nb">frozenset</span><span class="p">(</span><span class="n">requirement</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;extras&quot;</span><span class="p">,</span> <span class="p">[]))</span>
<span class="k">for</span> <span class="n">dep</span> <span class="ow">in</span> <span class="n">package</span><span class="p">[</span><span class="s2">&quot;dependencies&quot;</span><span class="p">]:</span>
<span class="k">if</span> <span class="s2">&quot;feature&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">dep</span> <span class="ow">or</span> <span class="n">dep</span><span class="p">[</span><span class="s2">&quot;feature&quot;</span><span class="p">]</span> <span class="ow">in</span> <span class="n">extras</span><span class="p">:</span>
<span class="n">applicable_deps</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">dep</span><span class="p">)</span>
<span class="k">return</span> <span class="n">applicable_deps</span>
</pre></div>
</div>
</section>
</section>
</section>
<section id="backwards-compatibility">
<h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2>
<p>Because there is no preexisting lock file format, there are no explicit
backwards-compatibility concerns in terms of Python packaging standards.</p>
<p>As for packaging tools themselves, that will be a per-tool decision. For tools
that dont document their lock file format, they could choose to simply start
using the format internally and then transition to saving their lock files with
a name supported by this PEP. For tools with a preexisting, documented format,
they could provide an option to choose which format to emit.</p>
</section>
<section id="security-implications">
<h2><a class="toc-backref" href="#security-implications" role="doc-backlink">Security Implications</a></h2>
<p>The hope is that by standardizing on a lock file format that starts from a
security-first posture it will help make overall packaging installation safer.
However, this PEP does not solve all potential security concerns.</p>
<p>One potential concern is tampering with a lock file. If a lock file is not kept
in source control and properly audited, a bad actor could change the file in
nefarious ways (e.g. point to a malware version of a package). Tampering could
also occur in transit to e.g. a cloud provider who will perform an installation
on the users behalf. Both could be mitigated by signing the lock file either
within the file in a <code class="docutils literal notranslate"><span class="pre">[tool]</span></code> entry or via a side channel external to the lock
file itself.</p>
<p>This PEP does not do anything to prevent a user from installing an incorrect
packages. While including many details to help in auditing a packages inclusion,
there isnt any mechanism to stop e.g. name confusion attacks via typosquatting.
Lockers may be able to provide some UX to help with this (e.g. by providing
download counts for a package).</p>
</section>
<section id="how-to-teach-this">
<h2><a class="toc-backref" href="#how-to-teach-this" role="doc-backlink">How to Teach This</a></h2>
<p>Users should be informed that when they ask to install some package, that
package may have its own dependencies, those dependencies may have dependencies,
and so on. Without writing down what gets installed as part of installing the
package they requested, things could change from underneath them (e.g., package
versions). Changes to the underlying dependencies can lead to accidental
breakage of their code. Lock files help deal with that by providing a way to
write down what was (and should be) installed.</p>
<p>Having what to install written down also helps in collaborating with others. By
agreeing to a lock files contents, everyone ends up with the same packages
installed. This helps make sure no one relies on e.g. an API thats only
available in a certain version that not everyone working on the project has
installed.</p>
<p>Lock files also help with security by making sure you always get the same files
installed and not a malicious one that someone may have slipped in. It also
lets one be more deliberate in upgrading their dependencies and thus making sure
the change is on purpose and not one slipped in by a bad actor.</p>
</section>
<section id="reference-implementation">
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
<p>A proof-of-concept implementing most of this PEP for wheels can be found at
<a class="reference external" href="https://github.com/brettcannon/mousebender/tree/pep">https://github.com/brettcannon/mousebender/tree/pep</a> .</p>
</section>
<section id="rejected-ideas">
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2>
<section id="a-flat-set-of-packages-to-install">
<h3><a class="toc-backref" href="#a-flat-set-of-packages-to-install" role="doc-backlink">A flat set of packages to install</a></h3>
<p>An earlier version of this PEP proposed to use a flat set of package versions
instead of a graph. The idea was that each package version could be evaluated in
isolation as to whether it applied to an environment for installation. The hope
was that would lend itself to easier auditing as one wouldnt have to worry
about how a package version fit into the graph when looking at e.g., a diff for
a lock file.</p>
<p>Unfortunately this was deemed not as flexible as using a graph. For instance,
recording the graph
<a class="reference external" href="https://discuss.python.org/t/pep-751-lock-files-again/59173/327">assists in dependency analysis for tools like GitHub</a>.
A graph also makes following how you ended up with dependencies within your lock
file from any point in the graph. It also balances out the implementation costs
a bit more between lockers and installers by alleviating the complexity off of
lockers a bit for only a minor increase in complexity for installers by
involving standard graph-traversing algorithms instead of a linear walk.</p>
<p>And if the dependency graph is already being recorded for the above benefits,
then recording that same data in a flattened manner is redundant that makes
lock files larger and potentially more unruly.</p>
</section>
<section id="specifying-a-new-core-metadata-version-that-requires-consistent-metadata-across-files">
<h3><a class="toc-backref" href="#specifying-a-new-core-metadata-version-that-requires-consistent-metadata-across-files" role="doc-backlink">Specifying a new core metadata version that requires consistent metadata across files</a></h3>
<p>At one point, to handle the issue of metadata varying between files and thus
require examining every released file for a package and version for accurate
locking results, the idea was floated to introduce a new core metadata version
which would require all metadata for all wheel files be the same for a single
version of a packages. Ultimately, though, it was deemed unnecessary as this PEP
will put pressure on people to make files consistent for performance reasons or
to make indexes provide all the metadata separate from the wheel files
themselves. As well, theres no easy enforcement mechanism, and so community
expectation would work as well as a new metadata version.</p>
</section>
<section id="have-the-installer-do-dependency-resolution">
<h3><a class="toc-backref" href="#have-the-installer-do-dependency-resolution" role="doc-backlink">Have the installer do dependency resolution</a></h3>
<p>In order to support a format more akin to how Poetry worked when this PEP was
drafted, it was suggested that lockers effectively record the packages and their
versions which may be necessary to make an install work in any possible
scenario, and then the installer resolves what to install. But that complicates
auditing a lock file by requiring much more mental effort to know what packages
may be installed in any given scenario. Also, one of the Poetry developers
<a class="reference external" href="https://discuss.python.org/t/lock-files-again-but-this-time-w-sdists/46593/83">suggested</a>
that markers as represented in the package locking approach of this PEP may be
sufficient to cover the needs of Poetry. Not having the installer do a
resolution also simplifies their implementation, centralizing complexity in
lockers.</p>
</section>
<section id="requiring-specific-hash-algorithm-support">
<h3><a class="toc-backref" href="#requiring-specific-hash-algorithm-support" role="doc-backlink">Requiring specific hash algorithm support</a></h3>
<p>It was proposed to require a baseline hash algorithm for the files. This was
rejected as no other Python packaging specification requires specific hash
algorithm support. As well, the minimum hash algorithm suggested may eventually
become an outdated/unsafe suggestion, requiring further updates. In order to
promote using the best algorithm at all times, no baseline is provided to avoid
simply defaulting to the baseline in tools without considering the security
ramifications of that hash algorithm.</p>
</section>
<section id="require-a-url-or-file-path-for-files">
<h3><a class="toc-backref" href="#require-a-url-or-file-path-for-files" role="doc-backlink">Require a URL or file path for files</a></h3>
<p>Originally references to files were required, e.g., <code class="docutils literal notranslate"><span class="pre">packages.sdist.url</span></code> or
<code class="docutils literal notranslate"><span class="pre">packages.sdist.path</span></code>. But at least
<a class="reference external" href="https://discuss.python.org/t/pep-751-now-with-graphs/69721/34">one use-case</a>
surfaced during discussions about this PEP where statically specifying the
location of files would be problematic. And in earlier discussions the idea of
the location being a hint wasnt preferred. Hence the PEP now makes the data
optional, but considers the locations accurate if specified.</p>
</section>
<section id="file-naming">
<h3><a class="toc-backref" href="#file-naming" role="doc-backlink">File naming</a></h3>
<section id="using-pylock-toml-as-the-file-name">
<h4><a class="toc-backref" href="#using-pylock-toml-as-the-file-name" role="doc-backlink">Using <code class="docutils literal notranslate"><span class="pre">*.pylock.toml</span></code> as the file name</a></h4>
<p>It was proposed to put the <code class="docutils literal notranslate"><span class="pre">pylock</span></code> constant part of the file name after the
identifier for the purpose of the lock file. It was decided not to do this so
that lock files would sort together when looking at directory contents instead
of purely based on their purpose which could spread them out in a directory.</p>
</section>
<section id="using-pylock-as-the-file-name">
<h4><a class="toc-backref" href="#using-pylock-as-the-file-name" role="doc-backlink">Using <code class="docutils literal notranslate"><span class="pre">*.pylock</span></code> as the file name</a></h4>
<p>Not using <code class="docutils literal notranslate"><span class="pre">.toml</span></code> as the file extension and instead making it <code class="docutils literal notranslate"><span class="pre">.pylock</span></code>
itself was proposed. This was decided against so that code editors would know
how to provide syntax highlighting to a lock file without having special
knowledge about the file extension.</p>
</section>
<section id="not-having-a-naming-convention-for-the-file">
<h4><a class="toc-backref" href="#not-having-a-naming-convention-for-the-file" role="doc-backlink">Not having a naming convention for the file</a></h4>
<p>Having no requirements or guidance for a lock files name was considered, but
ultimately rejected. By having a standardized naming convention it makes it easy
to identify a lock file for both a human and a code editor. This helps
facilitate discovery when e.g. a tool wants to know all of the lock files that
are available.</p>
</section>
</section>
<section id="id1">
<h3><a class="toc-backref" href="#id1" role="doc-backlink">File format</a></h3>
<section id="use-json-over-toml">
<h4><a class="toc-backref" href="#use-json-over-toml" role="doc-backlink">Use JSON over TOML</a></h4>
<p>Since having a format that is machine-writable was a goal of this PEP, it was
suggested to use JSON. But it was deemed less human-readable than TOML while
not improving on the machine-writable aspect enough to warrant the change.</p>
</section>
<section id="use-yaml-over-toml">
<h4><a class="toc-backref" href="#use-yaml-over-toml" role="doc-backlink">Use YAML over TOML</a></h4>
<p>Some argued that YAML met the machine-writable/human-readable requirement in a
better way than TOML. But as thats subjective and <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> already
existed as the human-writable file used by Python packaging standards it was
deemed more important to keep using TOML.</p>
</section>
</section>
<section id="other-keys">
<h3><a class="toc-backref" href="#other-keys" role="doc-backlink">Other keys</a></h3>
<section id="multiple-hashes-per-file">
<h4><a class="toc-backref" href="#multiple-hashes-per-file" role="doc-backlink">Multiple hashes per file</a></h4>
<p>An initial version of this PEP proposed supporting multiple hashes per file. The
idea was to allow one to choose which hashing algorithm they wanted to go with
when installing. But upon reflection it seemed like an unnecessary complication
as there was no guarantee the hashes provided would satisfy the users needs.
As well, if the single hash algorithm used in the lock file wasnt sufficient,
rehashing the files involved as a way to migrate to a different algorithm didnt
seem insurmountable.</p>
</section>
<section id="hashing-the-contents-of-the-lock-file-itself">
<h4><a class="toc-backref" href="#hashing-the-contents-of-the-lock-file-itself" role="doc-backlink">Hashing the contents of the lock file itself</a></h4>
<p>Hashing the contents of the bytes of the file and storing hash value within the
file itself was proposed at some point. This was removed to make it easier
when merging changes to the lock file as each merge would have to recalculate
the hash value to avoid a merge conflict.</p>
<p>Hashing the semantic contents of the file was also proposed, but it would lead
to the same merge conflict issue.</p>
<p>Regardless of which contents were hashed, either approach could have the hash
value stored outside of the file if such a hash was desired.</p>
</section>
<section id="recording-the-creation-date-of-the-lock-file">
<h4><a class="toc-backref" href="#recording-the-creation-date-of-the-lock-file" role="doc-backlink">Recording the creation date of the lock file</a></h4>
<p>To know how potentially stale the lock file was, an earlier proposal suggested
recording the creation date of the lock file. But for some same merge conflict
reasons as storing the hash of the file contents, this idea was dropped.</p>
</section>
<section id="recording-the-package-indexes-used">
<h4><a class="toc-backref" href="#recording-the-package-indexes-used" role="doc-backlink">Recording the package indexes used</a></h4>
<p>Recording what package indexes were used by the locker to decide what to lock
for was considered. In the end, though, it was rejected as it was deemed
unnecessary bookkeeping.</p>
</section>
<section id="locking-build-requirements-for-sdists">
<h4><a class="toc-backref" href="#locking-build-requirements-for-sdists" role="doc-backlink">Locking build requirements for sdists</a></h4>
<p>An earlier version of this PEP tried to lock the build requirements for sdists
under a <code class="docutils literal notranslate"><span class="pre">packages.build-requires</span></code> key. Unfortunately it confused enough people
about how it was expected to operate and there were enough edge case issues to
decide it wasnt worth trying to do in this PEP upfront. Instead, a future PEP
could propose a solution.</p>
</section>
</section>
</section>
<section id="open-issues">
<h2><a class="toc-backref" href="#open-issues" role="doc-backlink">Open Issues</a></h2>
<section id="specify-requires-python-at-the-file-level">
<h3><a class="toc-backref" href="#specify-requires-python-at-the-file-level" role="doc-backlink">Specify <code class="docutils literal notranslate"><span class="pre">requires-python</span></code> at the file level?</a></h3>
<p>The lock file formats from <a class="reference external" href="https://pypi.org/project/pdm/">PDM</a>, <a class="reference external" href="https://python-poetry.org/">Poetry</a>, and <a class="reference external" href="https://github.com/astral-sh/uv">uv</a> all specify
<code class="docutils literal notranslate"><span class="pre">requires-python</span></code> at the top level for the absolute minimum Python version
needed for the lock file. This can be inferred, though, by examining all
<code class="docutils literal notranslate"><span class="pre">packages.requires-python</span></code> values. The global value might also not be
accurate for all platforms depending on how environment markers influence what
package versions are installed and what their Python version requirements are.</p>
</section>
<section id="don-t-pre-parse-data">
<h3><a class="toc-backref" href="#don-t-pre-parse-data" role="doc-backlink">Dont pre-parse data?</a></h3>
<p>This PEP currently takes the viewpoint that if a piece of data is going to be
parsed by installers everytime they run, then trying to pre-parse as much as
possible so the TOML parser can help is a good thing. The thinking is TOML
parsers have a higher chance of being optimized, and so letting them do more
parsing leads to a faster outcome. It should also increase readability by
breaking apart data upfront more.</p>
<p>But in the case of doing this to wheel file names, some might consider it too
much. The question becomes whether separating out all the parts of a wheel
file name hinders readability because people are used to reading the file names
already, or by clearly separating its parts it actually helps make installers
faster, easier to write, and doesnt hinder readability.</p>
<p>This all equally applies to requirement specifiers.</p>
</section>
</section>
<section id="deferred-ideas">
<h2><a class="toc-backref" href="#deferred-ideas" role="doc-backlink">Deferred Ideas</a></h2>
<section id="per-file-locking">
<h3><a class="toc-backref" href="#per-file-locking" role="doc-backlink">Per-file locking</a></h3>
<p>An earlier version of this PEP supported two approaches to locking: <em>per-file</em>
and <strong>per-package</strong>. The idea for the former approach to locking was that if you
were locking for an a-priori set of environments you could lock to just the
files necessary to install into those environments. The thinking was that by
only listing a subset of files that auditing would be easier.</p>
<p>Unfortunately there was disagreement on how best to express upfront what the
supported environment requirements would be. Since what this PEP currently
proposes still prevents accidental success of installation into unsupported
environments, this idea has been deferred until such time someone can come up
with a representation that makes sense.</p>
</section>
<section id="allowing-for-multiple-lock-files">
<h3><a class="toc-backref" href="#allowing-for-multiple-lock-files" role="doc-backlink">Allowing for multiple lock files</a></h3>
<p>Before the introduction of <code class="docutils literal notranslate"><span class="pre">[[groups]]</span></code>, this PEP proposed supporting multiple
lock files that would match the regular expression
<code class="docutils literal notranslate"><span class="pre">r&quot;pylock\.(.+)\.toml&quot;</span></code> if a name for the lock file is desired or if multiple
lock files exist. But since <code class="docutils literal notranslate"><span class="pre">[[groups]]</span></code> subsumes a lot of the need to support
multiple lock files, this specific feature can be postponed until such time that
a need is shown to support multiple lock files.</p>
</section>
</section>
<section id="acknowledgements">
<h2><a class="toc-backref" href="#acknowledgements" role="doc-backlink">Acknowledgements</a></h2>
<p>Thanks to everyone who participated in the discussions on discuss.python.org.
Also thanks to Randy Döring, Seth Michael Larson, Paul Moore, and Ofek Lev for
providing feedback on a draft version of this PEP.</p>
</section>
<section id="copyright">
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
<p>This document is placed in the public domain or under the
CC0-1.0-Universal license, whichever is more permissive.</p>
</section>
</section>
<hr class="docutils" />
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0751.rst">https://github.com/python/peps/blob/main/peps/pep-0751.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0751.rst">2024-11-05 19:18:47 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="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#file-name">File Name</a></li>
<li><a class="reference internal" href="#file-format">File Format</a><ul>
<li><a class="reference internal" href="#version"><code class="docutils literal notranslate"><span class="pre">version</span></code></a></li>
<li><a class="reference internal" href="#hash-algorithm"><code class="docutils literal notranslate"><span class="pre">hash-algorithm</span></code></a></li>
<li><a class="reference internal" href="#locker"><code class="docutils literal notranslate"><span class="pre">[locker]</span></code></a><ul>
<li><a class="reference internal" href="#locker-name"><code class="docutils literal notranslate"><span class="pre">locker.name</span></code></a></li>
<li><a class="reference internal" href="#locker-version"><code class="docutils literal notranslate"><span class="pre">locker.version</span></code></a></li>
<li><a class="reference internal" href="#locker-run"><code class="docutils literal notranslate"><span class="pre">locker.run</span></code></a><ul>
<li><a class="reference internal" href="#locker-run-module"><code class="docutils literal notranslate"><span class="pre">locker.run.module</span></code></a></li>
<li><a class="reference internal" href="#locker-run-args"><code class="docutils literal notranslate"><span class="pre">locker.run.args</span></code></a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#groups"><code class="docutils literal notranslate"><span class="pre">[[groups]]</span></code></a><ul>
<li><a class="reference internal" href="#groups-name"><code class="docutils literal notranslate"><span class="pre">groups.name</span></code></a></li>
<li><a class="reference internal" href="#groups-project"><code class="docutils literal notranslate"><span class="pre">groups.project</span></code></a></li>
<li><a class="reference internal" href="#groups-requirements"><code class="docutils literal notranslate"><span class="pre">groups.requirements</span></code></a><ul>
<li><a class="reference internal" href="#groups-requirements-name"><code class="docutils literal notranslate"><span class="pre">groups.requirements.name</span></code></a></li>
<li><a class="reference internal" href="#groups-requirements-extras"><code class="docutils literal notranslate"><span class="pre">groups.requirements.extras</span></code></a></li>
<li><a class="reference internal" href="#groups-requirements-version"><code class="docutils literal notranslate"><span class="pre">groups.requirements.version</span></code></a></li>
<li><a class="reference internal" href="#groups-requirements-marker"><code class="docutils literal notranslate"><span class="pre">groups.requirements.marker</span></code></a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#packages"><code class="docutils literal notranslate"><span class="pre">[[packages]]</span></code></a><ul>
<li><a class="reference internal" href="#packages-name"><code class="docutils literal notranslate"><span class="pre">packages.name</span></code></a></li>
<li><a class="reference internal" href="#packages-version"><code class="docutils literal notranslate"><span class="pre">packages.version</span></code></a></li>
<li><a class="reference internal" href="#packages-groups"><code class="docutils literal notranslate"><span class="pre">packages.groups</span></code></a></li>
<li><a class="reference internal" href="#packages-index-url"><code class="docutils literal notranslate"><span class="pre">packages.index-url</span></code></a></li>
<li><a class="reference internal" href="#packages-direct"><code class="docutils literal notranslate"><span class="pre">packages.direct</span></code></a></li>
<li><a class="reference internal" href="#packages-requires-python"><code class="docutils literal notranslate"><span class="pre">packages.requires-python</span></code></a></li>
<li><a class="reference internal" href="#packages-dependencies"><code class="docutils literal notranslate"><span class="pre">[[packages.dependencies]]</span></code></a><ul>
<li><a class="reference internal" href="#packages-dependencies-name"><code class="docutils literal notranslate"><span class="pre">packages.dependencies.name</span></code></a></li>
<li><a class="reference internal" href="#packages-dependencies-extras"><code class="docutils literal notranslate"><span class="pre">packages.dependencies.extras</span></code></a></li>
<li><a class="reference internal" href="#packages-dependencies-version"><code class="docutils literal notranslate"><span class="pre">packages.dependencies.version</span></code></a></li>
<li><a class="reference internal" href="#packages-dependencies-marker"><code class="docutils literal notranslate"><span class="pre">packages.dependencies.marker</span></code></a></li>
<li><a class="reference internal" href="#packages-dependencies-feature"><code class="docutils literal notranslate"><span class="pre">packages.dependencies.feature</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#packages-editable"><code class="docutils literal notranslate"><span class="pre">packages.editable</span></code></a></li>
<li><a class="reference internal" href="#packages-source-tree"><code class="docutils literal notranslate"><span class="pre">[packages.source-tree]</span></code></a><ul>
<li><a class="reference internal" href="#packages-source-tree-vcs"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.vcs</span></code></a></li>
<li><a class="reference internal" href="#packages-source-tree-path"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.path</span></code></a></li>
<li><a class="reference internal" href="#packages-source-tree-url"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.url</span></code></a></li>
<li><a class="reference internal" href="#packages-source-tree-commit"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.commit</span></code></a></li>
<li><a class="reference internal" href="#packages-source-tree-size"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.size</span></code></a></li>
<li><a class="reference internal" href="#packages-source-tree-hash"><code class="docutils literal notranslate"><span class="pre">packages.source-tree.hash</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#packages-sdist"><code class="docutils literal notranslate"><span class="pre">[packages.sdist]</span></code></a><ul>
<li><a class="reference internal" href="#packages-sdist-url"><code class="docutils literal notranslate"><span class="pre">packages.sdist.url</span></code></a></li>
<li><a class="reference internal" href="#packages-sdist-path"><code class="docutils literal notranslate"><span class="pre">packages.sdist.path</span></code></a></li>
<li><a class="reference internal" href="#packages-sdist-upload-time"><code class="docutils literal notranslate"><span class="pre">packages.sdist.upload-time</span></code></a></li>
<li><a class="reference internal" href="#packages-sdist-size"><code class="docutils literal notranslate"><span class="pre">packages.sdist.size</span></code></a></li>
<li><a class="reference internal" href="#packages-sdist-hash"><code class="docutils literal notranslate"><span class="pre">packages.sdist.hash</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#packages-wheels"><code class="docutils literal notranslate"><span class="pre">[[packages.wheels]]</span></code></a><ul>
<li><a class="reference internal" href="#packages-wheels-tags"><code class="docutils literal notranslate"><span class="pre">packages.wheels.tags</span></code></a></li>
<li><a class="reference internal" href="#packages-wheels-build"><code class="docutils literal notranslate"><span class="pre">packages.wheels.build</span></code></a></li>
<li><a class="reference internal" href="#packages-wheels-url"><code class="docutils literal notranslate"><span class="pre">packages.wheels.url</span></code></a></li>
<li><a class="reference internal" href="#packages-wheels-path"><code class="docutils literal notranslate"><span class="pre">packages.wheels.path</span></code></a></li>
<li><a class="reference internal" href="#packages-wheels-upload-time"><code class="docutils literal notranslate"><span class="pre">packages.wheels.upload-time</span></code></a></li>
<li><a class="reference internal" href="#packages-wheels-size"><code class="docutils literal notranslate"><span class="pre">packages.wheels.size</span></code></a></li>
<li><a class="reference internal" href="#packages-wheels-hash"><code class="docutils literal notranslate"><span class="pre">packages.wheels.hash</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#packages-tool"><code class="docutils literal notranslate"><span class="pre">[packages.tool]</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#tool"><code class="docutils literal notranslate"><span class="pre">[tool]</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#examples">Examples</a></li>
<li><a class="reference internal" href="#expectations-for-lockers">Expectations for Lockers</a></li>
<li><a class="reference internal" href="#expectations-for-installers">Expectations for Installers</a><ul>
<li><a class="reference internal" href="#pseudo-code">Pseudo-Code</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li>
<li><a class="reference internal" href="#security-implications">Security Implications</a></li>
<li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
<li><a class="reference internal" href="#a-flat-set-of-packages-to-install">A flat set of packages to install</a></li>
<li><a class="reference internal" href="#specifying-a-new-core-metadata-version-that-requires-consistent-metadata-across-files">Specifying a new core metadata version that requires consistent metadata across files</a></li>
<li><a class="reference internal" href="#have-the-installer-do-dependency-resolution">Have the installer do dependency resolution</a></li>
<li><a class="reference internal" href="#requiring-specific-hash-algorithm-support">Requiring specific hash algorithm support</a></li>
<li><a class="reference internal" href="#require-a-url-or-file-path-for-files">Require a URL or file path for files</a></li>
<li><a class="reference internal" href="#file-naming">File naming</a><ul>
<li><a class="reference internal" href="#using-pylock-toml-as-the-file-name">Using <code class="docutils literal notranslate"><span class="pre">*.pylock.toml</span></code> as the file name</a></li>
<li><a class="reference internal" href="#using-pylock-as-the-file-name">Using <code class="docutils literal notranslate"><span class="pre">*.pylock</span></code> as the file name</a></li>
<li><a class="reference internal" href="#not-having-a-naming-convention-for-the-file">Not having a naming convention for the file</a></li>
</ul>
</li>
<li><a class="reference internal" href="#id1">File format</a><ul>
<li><a class="reference internal" href="#use-json-over-toml">Use JSON over TOML</a></li>
<li><a class="reference internal" href="#use-yaml-over-toml">Use YAML over TOML</a></li>
</ul>
</li>
<li><a class="reference internal" href="#other-keys">Other keys</a><ul>
<li><a class="reference internal" href="#multiple-hashes-per-file">Multiple hashes per file</a></li>
<li><a class="reference internal" href="#hashing-the-contents-of-the-lock-file-itself">Hashing the contents of the lock file itself</a></li>
<li><a class="reference internal" href="#recording-the-creation-date-of-the-lock-file">Recording the creation date of the lock file</a></li>
<li><a class="reference internal" href="#recording-the-package-indexes-used">Recording the package indexes used</a></li>
<li><a class="reference internal" href="#locking-build-requirements-for-sdists">Locking build requirements for sdists</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#open-issues">Open Issues</a><ul>
<li><a class="reference internal" href="#specify-requires-python-at-the-file-level">Specify <code class="docutils literal notranslate"><span class="pre">requires-python</span></code> at the file level?</a></li>
<li><a class="reference internal" href="#don-t-pre-parse-data">Dont pre-parse data?</a></li>
</ul>
</li>
<li><a class="reference internal" href="#deferred-ideas">Deferred Ideas</a><ul>
<li><a class="reference internal" href="#per-file-locking">Per-file locking</a></li>
<li><a class="reference internal" href="#allowing-for-multiple-lock-files">Allowing for multiple lock files</a></li>
</ul>
</li>
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></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-0751.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>