1515 lines
143 KiB
HTML
1515 lines
143 KiB
HTML
|
||
<!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 735 – Dependency Groups in pyproject.toml | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0735/">
|
||
<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 735 – Dependency Groups in pyproject.toml | peps.python.org'>
|
||
<meta property="og:description" content="This PEP specifies a mechanism for storing package requirements in pyproject.toml files such that they are not included in any built distribution of the project.">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0735/">
|
||
<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 specifies a mechanism for storing package requirements in pyproject.toml files such that they are not included in any built distribution of the project.">
|
||
<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> » </li>
|
||
<li><a href="../pep-0000/">PEP Index</a> » </li>
|
||
<li>PEP 735</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 735 – Dependency Groups in pyproject.toml</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Stephen Rosen <sirosen0 at gmail.com></dd>
|
||
<dt class="field-even">Sponsor<span class="colon">:</span></dt>
|
||
<dd class="field-even">Brett Cannon <brett at python.org></dd>
|
||
<dt class="field-odd">PEP-Delegate<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Paul Moore <p.f.moore at gmail.com></dd>
|
||
<dt class="field-even">Discussions-To<span class="colon">:</span></dt>
|
||
<dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/39233">Discourse thread</a></dd>
|
||
<dt class="field-odd">Status<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><abbr title="Normative proposal accepted for implementation">Accepted</abbr></dd>
|
||
<dt class="field-even">Type<span class="colon">:</span></dt>
|
||
<dd class="field-even"><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-odd">Topic<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><a class="reference external" href="../topic/packaging/">Packaging</a></dd>
|
||
<dt class="field-even">Created<span class="colon">:</span></dt>
|
||
<dd class="field-even">20-Nov-2023</dd>
|
||
<dt class="field-odd">Post-History<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><a class="reference external" href="https://discuss.python.org/t/29684" title="Discourse thread">14-Nov-2023</a>, <a class="reference external" href="https://discuss.python.org/t/39233" title="Discourse thread">20-Nov-2023</a></dd>
|
||
<dt class="field-even">Resolution<span class="colon">:</span></dt>
|
||
<dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/39233/312">10-Oct-2024</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><ul>
|
||
<li><a class="reference internal" href="#limitations-of-requirements-txt-files">Limitations of <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files</a></li>
|
||
<li><a class="reference internal" href="#limitations-of-extras">Limitations of <code class="docutils literal notranslate"><span class="pre">extras</span></code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rationale">Rationale</a><ul>
|
||
<li><a class="reference internal" href="#use-cases">Use Cases</a></li>
|
||
<li><a class="reference internal" href="#regarding-poetry-and-pdm-dependency-groups">Regarding Poetry and PDM Dependency Groups</a></li>
|
||
<li><a class="reference internal" href="#dependency-groups-are-not-hidden-extras">Dependency Groups are not Hidden Extras</a></li>
|
||
<li><a class="reference internal" href="#future-compatibility-invalid-data">Future Compatibility & Invalid Data</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#specification">Specification</a><ul>
|
||
<li><a class="reference internal" href="#dependency-object-specifiers">Dependency Object Specifiers</a><ul>
|
||
<li><a class="reference internal" href="#dependency-group-include">Dependency Group Include</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#example-dependency-groups-table">Example Dependency Groups Table</a></li>
|
||
<li><a class="reference internal" href="#package-building">Package Building</a></li>
|
||
<li><a class="reference internal" href="#installing-dependency-groups">Installing Dependency Groups</a><ul>
|
||
<li><a class="reference internal" href="#overlapping-install-ux-with-extras">Overlapping Install UX with Extras</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#validation-and-compatibility">Validation and Compatibility</a><ul>
|
||
<li><a class="reference internal" href="#linters-and-validators-may-be-stricter">Linters and Validators may be stricter</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
|
||
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a><ul>
|
||
<li><a class="reference internal" href="#audit-and-update-tools">Audit and Update Tools</a></li>
|
||
</ul>
|
||
</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><ul>
|
||
<li><a class="reference internal" href="#interfaces-for-use-of-dependency-groups">Interfaces for Use of Dependency Groups</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
|
||
<li><a class="reference internal" href="#why-not-define-each-dependency-group-as-a-table">Why not define each Dependency Group as a table?</a></li>
|
||
<li><a class="reference internal" href="#why-not-define-a-special-string-syntax-to-extend-dependency-specifiers">Why not define a special string syntax to extend Dependency Specifiers?</a></li>
|
||
<li><a class="reference internal" href="#why-not-allow-for-more-non-pep-508-dependency-specifiers">Why not allow for more non-PEP 508 dependency specifiers?</a></li>
|
||
<li><a class="reference internal" href="#why-is-the-table-not-named-run-project-dependency-groups">Why is the table not named <code class="docutils literal notranslate"><span class="pre">[run]</span></code>, <code class="docutils literal notranslate"><span class="pre">[project.dependency-groups]</span></code>, …?</a></li>
|
||
<li><a class="reference internal" href="#why-is-pip-s-planned-implementation-of-only-deps-not-sufficient">Why is pip’s planned implementation of <code class="docutils literal notranslate"><span class="pre">--only-deps</span></code> not sufficient?</a></li>
|
||
<li><a class="reference internal" href="#why-isn-t-environment-manager-a-solution">Why isn’t <environment manager> a solution?</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#deferred-ideas">Deferred Ideas</a><ul>
|
||
<li><a class="reference internal" href="#why-not-support-dependency-group-includes-in-project-dependencies-or-project-optional-dependencies">Why not support Dependency Group Includes in <code class="docutils literal notranslate"><span class="pre">[project.dependencies]</span></code> or <code class="docutils literal notranslate"><span class="pre">[project.optional-dependencies]</span></code>?</a><ul>
|
||
<li><a class="reference internal" href="#use-cases-for-dependency-group-includes-from-project">Use Cases for Dependency Group Includes From <code class="docutils literal notranslate"><span class="pre">[project]</span></code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#why-not-support-dependency-group-includes-in-build-system-requires">Why not support Dependency Group Includes in <code class="docutils literal notranslate"><span class="pre">[build-system.requires]</span></code>?</a></li>
|
||
<li><a class="reference internal" href="#why-not-support-a-dependency-group-which-includes-the-current-project">Why not support a Dependency Group which includes the current project?</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#appendix-a-prior-art-in-non-python-languages">Appendix A: Prior Art in Non-Python Languages</a><ul>
|
||
<li><a class="reference internal" href="#javascript-and-package-json">JavaScript and <code class="docutils literal notranslate"><span class="pre">package.json</span></code></a><ul>
|
||
<li><a class="reference internal" href="#dependencies-data"><code class="docutils literal notranslate"><span class="pre">"dependencies"</span></code> data</a></li>
|
||
<li><a class="reference internal" href="#dependencies-referencing-urls-and-local-paths">Dependencies Referencing URLs and Local Paths</a></li>
|
||
<li><a class="reference internal" href="#devdependencies-data"><code class="docutils literal notranslate"><span class="pre">"devDependencies"</span></code> data</a></li>
|
||
<li><a class="reference internal" href="#peerdependencies-and-optionaldependencies"><code class="docutils literal notranslate"><span class="pre">"peerDependencies"</span></code> and <code class="docutils literal notranslate"><span class="pre">"optionalDependencies"</span></code></a><ul>
|
||
<li><a class="reference internal" href="#peerdependenciesmeta"><code class="docutils literal notranslate"><span class="pre">"peerDependenciesMeta"</span></code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#omit-and-include"><code class="docutils literal notranslate"><span class="pre">--omit</span></code> and <code class="docutils literal notranslate"><span class="pre">--include</span></code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#ruby-ruby-gems">Ruby & Ruby Gems</a><ul>
|
||
<li><a class="reference internal" href="#gemfiles-bundle">Gemfiles & bundle</a></li>
|
||
<li><a class="reference internal" href="#gemspec-and-packaged-dependency-data">gemspec and packaged dependency data</a><ul>
|
||
<li><a class="reference internal" href="#gemspec-development-dependency-example">gemspec development dependency example</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#appendix-b-prior-art-in-python">Appendix B: Prior Art in Python</a><ul>
|
||
<li><a class="reference internal" href="#projects-are-packages">Projects are Packages</a></li>
|
||
<li><a class="reference internal" href="#non-standard-dependency-specifiers">Non-Standard Dependency Specifiers</a></li>
|
||
<li><a class="reference internal" href="#installing-and-referring-to-dependency-groups">Installing and Referring to Dependency Groups</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#appendix-c-use-cases">Appendix C: Use Cases</a><ul>
|
||
<li><a class="reference internal" href="#web-applications">Web Applications</a></li>
|
||
<li><a class="reference internal" href="#libraries">Libraries</a></li>
|
||
<li><a class="reference internal" href="#data-science-projects">Data Science Projects</a></li>
|
||
<li><a class="reference internal" href="#lockfile-generation">Lockfile Generation</a></li>
|
||
<li><a class="reference internal" href="#environment-manager-inputs">Environment Manager Inputs</a></li>
|
||
<li><a class="reference internal" href="#ide-and-editor-use-of-requirements-data">IDE and Editor Use of Requirements Data</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
</details></section>
|
||
<section id="abstract">
|
||
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
|
||
<p>This PEP specifies a mechanism for storing package requirements in
|
||
<code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> files such that they are not included in any built distribution of
|
||
the project.</p>
|
||
<p>This is suitable for creating named groups of dependencies, similar to
|
||
<code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files, which launchers, IDEs, and other tools can find and
|
||
identify by name.</p>
|
||
<p>The feature defined here is referred to as “Dependency Groups”.</p>
|
||
</section>
|
||
<section id="motivation">
|
||
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
|
||
<p>There are two major use cases for which the Python community has no
|
||
standardized answer:</p>
|
||
<ul class="simple">
|
||
<li>How should development dependencies be defined for packages?</li>
|
||
<li>How should dependencies be defined for projects which do not build
|
||
distributions (non-package projects)?</li>
|
||
</ul>
|
||
<p>In support of these two needs, there are two common solutions which are similar
|
||
to this proposal:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files</li>
|
||
<li>package <a class="reference external" href="https://packaging.python.org/en/latest/specifications/dependency-specifiers/#extras">extras</a></li>
|
||
</ul>
|
||
<p>Both <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files and <code class="docutils literal notranslate"><span class="pre">extras</span></code> have limitations which this
|
||
standard seeks to overcome.</p>
|
||
<p>Note that the two use cases above describe two different types of projects
|
||
which this PEP seeks to support:</p>
|
||
<ul class="simple">
|
||
<li>Python packages, such as libraries</li>
|
||
<li>non-package projects, such as data science projects</li>
|
||
</ul>
|
||
<p>Several motivating use cases are defined in detail in the <a class="reference internal" href="#id1"><span class="std std-ref">Use Cases Appendix</span></a>.</p>
|
||
<section id="limitations-of-requirements-txt-files">
|
||
<h3><a class="toc-backref" href="#limitations-of-requirements-txt-files" role="doc-backlink">Limitations of <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files</a></h3>
|
||
<p>Many projects may define one or more <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files,
|
||
and may arrange them either at the project root (e.g. <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">test-requirements.txt</span></code>) or else in a directory (e.g.
|
||
<code class="docutils literal notranslate"><span class="pre">requirements/base.txt</span></code> and <code class="docutils literal notranslate"><span class="pre">requirements/test.txt</span></code>). However, there are
|
||
major issues with the use of requirements files in this way:</p>
|
||
<ul class="simple">
|
||
<li>There is no standardized naming convention such that tools can discover or
|
||
use these files by name.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files are <em>not standardized</em>, but instead provide
|
||
options to <code class="docutils literal notranslate"><span class="pre">pip</span></code>.</li>
|
||
</ul>
|
||
<p>As a result, it is difficult to define tool behaviors based on
|
||
<code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files. They are not trivial to discover or identify by
|
||
name, and their contents may contain a mix of package specifiers and additional
|
||
<code class="docutils literal notranslate"><span class="pre">pip</span></code> options.</p>
|
||
<p>The lack of a standard for <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> contents also means they are
|
||
not portable to any alternative tools which wish to process them other than
|
||
<code class="docutils literal notranslate"><span class="pre">pip</span></code>.</p>
|
||
<p>Additionally, <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files require a file per dependency list.
|
||
For some use-cases, this makes the marginal cost of dependency groupings high,
|
||
relative to their benefit.
|
||
A terser declaration is beneficial to projects with a number of small groups of
|
||
dependencies.</p>
|
||
<p>In contrast with this, Dependency Groups are defined at a well known location
|
||
in <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> with fully standardized contents. Not only will they have
|
||
immediate utility, but they will also serve as a starting point for future
|
||
standards.</p>
|
||
</section>
|
||
<section id="limitations-of-extras">
|
||
<h3><a class="toc-backref" href="#limitations-of-extras" role="doc-backlink">Limitations of <code class="docutils literal notranslate"><span class="pre">extras</span></code></a></h3>
|
||
<p><code class="docutils literal notranslate"><span class="pre">extras</span></code> are additional package metadata declared in the
|
||
<code class="docutils literal notranslate"><span class="pre">[project.optional-dependencies]</span></code> table. They provide names for lists of
|
||
package specifiers which are published as part of a package’s metadata, and
|
||
which a user can request under that name, as in <code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">'foo[bar]'</span></code> to
|
||
install <code class="docutils literal notranslate"><span class="pre">foo</span></code> with the <code class="docutils literal notranslate"><span class="pre">bar</span></code> extra.</p>
|
||
<p>Because <code class="docutils literal notranslate"><span class="pre">extras</span></code> are package metadata, they are not guaranteed to be
|
||
statically defined and may require a build system to resolve.
|
||
Furthermore, definition of a <code class="docutils literal notranslate"><span class="pre">[project.optional-dependencies]</span></code> indicates to
|
||
many tools that a project is a package, and may drive tool behaviors such as
|
||
validation of the <code class="docutils literal notranslate"><span class="pre">[project]</span></code> table.</p>
|
||
<p>For projects which are packages, <code class="docutils literal notranslate"><span class="pre">extras</span></code> are a common solution for defining
|
||
development dependencies, but even under these circumstances they have
|
||
downsides:</p>
|
||
<ul class="simple">
|
||
<li>Because an <code class="docutils literal notranslate"><span class="pre">extra</span></code> defines optional <em>additional</em> dependencies, it is not
|
||
possible to install an <code class="docutils literal notranslate"><span class="pre">extra</span></code> without installing the current package and
|
||
its dependencies.</li>
|
||
<li>Because they are user-installable, <code class="docutils literal notranslate"><span class="pre">extras</span></code> are part of the public interface
|
||
for packages. Because <code class="docutils literal notranslate"><span class="pre">extras</span></code> are published, package developers often are
|
||
concerned about ensuring that their development extras are not confused with
|
||
user-facing extras.</li>
|
||
</ul>
|
||
</section>
|
||
</section>
|
||
<section id="rationale">
|
||
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
|
||
<p>This PEP defines the storage of requirements data in lists within a
|
||
<code class="docutils literal notranslate"><span class="pre">[dependency-groups]</span></code> table.
|
||
This name was chosen to match the canonical name of the feature
|
||
(“Dependency Groups”).</p>
|
||
<p>This format should be as simple and learnable as possible, having a format
|
||
very similar to existing <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files for many cases. Each list
|
||
in <code class="docutils literal notranslate"><span class="pre">[dependency-groups]</span></code> is defined as a list of package specifiers. For
|
||
example:</p>
|
||
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="k">[dependency-groups]</span>
|
||
<span class="n">test</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"pytest>7"</span><span class="p">,</span><span class="w"> </span><span class="s2">"coverage"</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>There are a number of use cases for <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files which require
|
||
data which cannot be expressed in <a class="pep reference internal" href="../pep-0508/" title="PEP 508 – Dependency specification for Python Software Packages">PEP 508</a> dependency specifiers. Such
|
||
fields are not valid in Dependency Groups. Including many of the data and
|
||
fields which <code class="docutils literal notranslate"><span class="pre">pip</span></code> supports, such as index servers, hashes, and path
|
||
dependencies, requires new standards. This standard leaves room for new
|
||
standards and developments, but does not attempt to support all valid
|
||
<code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> contents.</p>
|
||
<p>The only exception to this is the <code class="docutils literal notranslate"><span class="pre">-r</span></code> flag which <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files
|
||
use to include one file in another. Dependency Groups support an “include”
|
||
mechanism which is similar in meaning, allowing one dependency group to extend
|
||
another.</p>
|
||
<p>Dependency Groups have two additional features which are similar to
|
||
<code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files:</p>
|
||
<ul class="simple">
|
||
<li>they are not published as distinct metadata in any built distribution</li>
|
||
<li>installation of a dependency group does not imply installation of a package’s
|
||
dependencies or the package itself</li>
|
||
</ul>
|
||
<section id="use-cases">
|
||
<h3><a class="toc-backref" href="#use-cases" role="doc-backlink">Use Cases</a></h3>
|
||
<p>The following use cases are considered important targets for this PEP. They are
|
||
defined in greater detail in the <a class="reference internal" href="#id1"><span class="std std-ref">Use Cases Appendix</span></a>.</p>
|
||
<ul class="simple">
|
||
<li>Web Applications deployed via a non-python-packaging build process</li>
|
||
<li>Libraries with unpublished dev dependency groups</li>
|
||
<li>Data science projects with groups of dependencies but no core package</li>
|
||
<li><em>Input data</em> to lockfile generation (Dependency Groups should generally not
|
||
be used as a location for locked dependency data)</li>
|
||
<li>Input data to an environment manager, such as tox, Nox, or Hatch</li>
|
||
<li>Configurable IDE discovery of test and linter requirements</li>
|
||
</ul>
|
||
</section>
|
||
<section id="regarding-poetry-and-pdm-dependency-groups">
|
||
<h3><a class="toc-backref" href="#regarding-poetry-and-pdm-dependency-groups" role="doc-backlink">Regarding Poetry and PDM Dependency Groups</a></h3>
|
||
<p>The existing Poetry and PDM tools already offer a feature which each calls
|
||
“Dependency Groups”. However, absent any standard for specifying collections
|
||
of dependencies, each tool defines these in a tool-specific way, in the
|
||
relevant sections of the <code class="docutils literal notranslate"><span class="pre">[tool]</span></code> table.</p>
|
||
<p>(PDM also uses extras for some Dependency Groups, and overlaps the notion
|
||
heavily with extras.)</p>
|
||
<p>This PEP does not support all of the features of Poetry and PDM, which, like
|
||
<code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files for <code class="docutils literal notranslate"><span class="pre">pip</span></code>, support several non-standard extensions
|
||
to common dependency specifiers.</p>
|
||
<p>It should be possible for such tools to use standardized Dependency Groups as
|
||
extensions of their own Dependency Group mechanisms.
|
||
However, defining a new data format which replaces the existing Poetry and PDM
|
||
solutions is a non-goal. Doing so would require standardizing several
|
||
additional features, such as path dependencies, which are supported by these
|
||
tools.</p>
|
||
</section>
|
||
<section id="dependency-groups-are-not-hidden-extras">
|
||
<h3><a class="toc-backref" href="#dependency-groups-are-not-hidden-extras" role="doc-backlink">Dependency Groups are not Hidden Extras</a></h3>
|
||
<p>Dependency Groups are very similar to extras which go unpublished.
|
||
However, there are two major features which distinguish them from extras
|
||
further:</p>
|
||
<ul class="simple">
|
||
<li>they support non-package projects</li>
|
||
<li>installation of a Dependency Group does not imply installation of a package’s
|
||
dependencies (or the package itself)</li>
|
||
</ul>
|
||
</section>
|
||
<section id="future-compatibility-invalid-data">
|
||
<h3><a class="toc-backref" href="#future-compatibility-invalid-data" role="doc-backlink">Future Compatibility & Invalid Data</a></h3>
|
||
<p>Dependency Groups are intended to be extensible in future PEPs.
|
||
However, Dependency Groups should also be usable by multiple tools in a
|
||
single Python project.
|
||
With multiple tools using the same data, it is possible that one implements
|
||
a future PEP which extends Dependency Groups, while another does not.</p>
|
||
<p>To support users in this case, this PEP defines and recommends validation
|
||
behaviors in which tools only examine Dependency Groups which they are using.
|
||
This allows multiple tools, using different versions of Dependency Groups data,
|
||
to share a single table in <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>.</p>
|
||
</section>
|
||
</section>
|
||
<section id="specification">
|
||
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
|
||
<p>This PEP defines a new section (table) in <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> files named
|
||
<code class="docutils literal notranslate"><span class="pre">dependency-groups</span></code>. The <code class="docutils literal notranslate"><span class="pre">dependency-groups</span></code> table contains an arbitrary
|
||
number of user-defined keys, each of which has, as its value, a list of
|
||
requirements (defined below). These keys must be
|
||
<a class="reference external" href="https://packaging.python.org/en/latest/specifications/name-normalization/#valid-non-normalized-names">valid non-normalized names</a>,
|
||
and must be
|
||
<a class="reference external" href="https://packaging.python.org/en/latest/specifications/name-normalization/#normalization">normalized</a>
|
||
before comparisons.</p>
|
||
<p>Tools SHOULD prefer to present the original, non-normalized name to users by
|
||
default. If duplicate names, after normalization, are encountered, tools SHOULD
|
||
emit an error.</p>
|
||
<p>Requirement lists under <code class="docutils literal notranslate"><span class="pre">dependency-groups</span></code> may contain strings, tables
|
||
(“dicts” in Python), or a mix of strings and tables.</p>
|
||
<p>Strings in requirement lists must be valid
|
||
<a class="reference external" href="https://packaging.python.org/en/latest/specifications/dependency-specifiers/">Dependency Specifiers</a>,
|
||
as defined in <a class="pep reference internal" href="../pep-0508/" title="PEP 508 – Dependency specification for Python Software Packages">PEP 508</a>.</p>
|
||
<p>Tables in requirement lists must be valid Dependency Object Specifiers.</p>
|
||
<section id="dependency-object-specifiers">
|
||
<h3><a class="toc-backref" href="#dependency-object-specifiers" role="doc-backlink">Dependency Object Specifiers</a></h3>
|
||
<p>Dependency Object Specifiers are tables which define zero or more dependencies.</p>
|
||
<p>This PEP standardizes only one type of Dependency Object Specifier, a
|
||
“Dependency Group Include”. Other types may be added in future standards.</p>
|
||
<section id="dependency-group-include">
|
||
<h4><a class="toc-backref" href="#dependency-group-include" role="doc-backlink">Dependency Group Include</a></h4>
|
||
<p>A Dependency Group Include includes the dependencies of another Dependency
|
||
Group in the current Dependency Group.</p>
|
||
<p>An include is defined as a table with exactly one key, <code class="docutils literal notranslate"><span class="pre">"include-group"</span></code>,
|
||
whose value is a string, the name of another Dependency Group.</p>
|
||
<p>For example, <code class="docutils literal notranslate"><span class="pre">{include-group</span> <span class="pre">=</span> <span class="pre">"test"}</span></code> is an include which expands to the
|
||
contents of the <code class="docutils literal notranslate"><span class="pre">test</span></code> Dependency Group.</p>
|
||
<p>Includes are defined to be exactly equivalent to the contents of the named
|
||
Dependency Group, inserted into the current group at the location of the include.
|
||
For example, if <code class="docutils literal notranslate"><span class="pre">foo</span> <span class="pre">=</span> <span class="pre">["a",</span> <span class="pre">"b"]</span></code> is one group, and
|
||
<code class="docutils literal notranslate"><span class="pre">bar</span> <span class="pre">=</span> <span class="pre">["c",</span> <span class="pre">{include-group</span> <span class="pre">=</span> <span class="pre">"foo"},</span> <span class="pre">"d"]</span></code> is another, then <code class="docutils literal notranslate"><span class="pre">bar</span></code> should
|
||
evaluate to <code class="docutils literal notranslate"><span class="pre">["c",</span> <span class="pre">"a",</span> <span class="pre">"b",</span> <span class="pre">"d"]</span></code> when Dependency Group Includes are expanded.</p>
|
||
<p>Dependency Group Includes may specify the same package multiple times. Tools
|
||
SHOULD NOT deduplicate or otherwise alter the list contents produced by the
|
||
include. For example, given the following table:</p>
|
||
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="k">[dependency-groups]</span>
|
||
<span class="n">group-a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"foo"</span><span class="p">]</span>
|
||
<span class="n">group-b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"foo>1.0"</span><span class="p">]</span>
|
||
<span class="n">group-c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"foo<1.0"</span><span class="p">]</span>
|
||
<span class="n">all</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"foo"</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="n">include-group</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s2">"group-a"</span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="n">include-group</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s2">"group-b"</span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="n">include-group</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s2">"group-c"</span><span class="p">}]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The resolved value of <code class="docutils literal notranslate"><span class="pre">all</span></code> SHOULD be <code class="docutils literal notranslate"><span class="pre">["foo",</span> <span class="pre">"foo",</span> <span class="pre">"foo>1.0",</span> <span class="pre">"foo<1.0"]</span></code>.
|
||
Tools should handle such a list exactly as they would handle any other case in
|
||
which they are asked to process the same requirement multiple times with
|
||
different version constraints.</p>
|
||
<p>Dependency Group Includes may include lists containing Dependency Group
|
||
Includes, in which case those includes should be expanded as well. Dependency
|
||
Group Includes MUST NOT include cycles, and tools SHOULD report an error if
|
||
they detect a cycle.</p>
|
||
</section>
|
||
</section>
|
||
<section id="example-dependency-groups-table">
|
||
<h3><a class="toc-backref" href="#example-dependency-groups-table" role="doc-backlink">Example Dependency Groups Table</a></h3>
|
||
<p>The following is an example of a partial <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> which uses this to
|
||
define four Dependency Groups: <code class="docutils literal notranslate"><span class="pre">test</span></code>, <code class="docutils literal notranslate"><span class="pre">docs</span></code>, <code class="docutils literal notranslate"><span class="pre">typing</span></code>, and
|
||
<code class="docutils literal notranslate"><span class="pre">typing-test</span></code>:</p>
|
||
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="k">[dependency-groups]</span>
|
||
<span class="n">test</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"pytest"</span><span class="p">,</span><span class="w"> </span><span class="s2">"coverage"</span><span class="p">]</span>
|
||
<span class="n">docs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"sphinx"</span><span class="p">,</span><span class="w"> </span><span class="s2">"sphinx-rtd-theme"</span><span class="p">]</span>
|
||
<span class="n">typing</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"mypy"</span><span class="p">,</span><span class="w"> </span><span class="s2">"types-requests"</span><span class="p">]</span>
|
||
<span class="n">typing-test</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[{</span><span class="n">include-group</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s2">"typing"</span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="n">include-group</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s2">"test"</span><span class="p">},</span><span class="w"> </span><span class="s2">"useful-types"</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that none of these Dependency Group declarations implicitly install the
|
||
current package, its dependencies, or any optional dependencies.
|
||
Use of a Dependency Group like <code class="docutils literal notranslate"><span class="pre">test</span></code> to test a package requires that the
|
||
user’s configuration or toolchain also installs the current package (<code class="docutils literal notranslate"><span class="pre">.</span></code>).
|
||
For example,</p>
|
||
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span><span class="nv">$TOOL</span><span class="w"> </span>install-dependency-group<span class="w"> </span><span class="nb">test</span>
|
||
pip<span class="w"> </span>install<span class="w"> </span>-e<span class="w"> </span>.
|
||
</pre></div>
|
||
</div>
|
||
<p>could be used (supposing <code class="docutils literal notranslate"><span class="pre">$TOOL</span></code> is a tool which supports installing
|
||
Dependency Groups) to build a testing environment.</p>
|
||
<p>This also allows for the <code class="docutils literal notranslate"><span class="pre">docs</span></code> dependency group to be used without
|
||
installing the project as a package:</p>
|
||
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span><span class="nv">$TOOL</span><span class="w"> </span>install-dependency-group<span class="w"> </span>docs
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="package-building">
|
||
<h3><a class="toc-backref" href="#package-building" role="doc-backlink">Package Building</a></h3>
|
||
<p>Build backends MUST NOT include Dependency Group data in built distributions as
|
||
package metadata. This means that PKG-INFO in sdists and METADATA in wheels
|
||
do not include any referencable fields containing Dependency Groups.</p>
|
||
<p>It is valid to use Dependency Groups in the evaluation of dynamic metadata, and
|
||
<code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> files included in sdists will naturally still contain the
|
||
<code class="docutils literal notranslate"><span class="pre">[dependency-groups]</span></code> table. However, the table contents are not part of a
|
||
published package’s interfaces.</p>
|
||
</section>
|
||
<section id="installing-dependency-groups">
|
||
<h3><a class="toc-backref" href="#installing-dependency-groups" role="doc-backlink">Installing Dependency Groups</a></h3>
|
||
<p>Tools which support Dependency Groups are expected to provide new options and
|
||
interfaces to allow users to install from Dependency Groups.</p>
|
||
<p>No syntax is defined for expressing the Dependency Group of a package, for two
|
||
reasons:</p>
|
||
<ul class="simple">
|
||
<li>it would not be valid to refer to the Dependency Groups of a third-party
|
||
package from PyPI (because the data is defined to be unpublished)</li>
|
||
<li>there is not guaranteed to be a current package for Dependency Groups – part
|
||
of their purpose is to support non-package projects</li>
|
||
</ul>
|
||
<p>For example, a possible pip interface for installing Dependency Groups
|
||
would be:</p>
|
||
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>pip<span class="w"> </span>install<span class="w"> </span>--dependency-groups<span class="o">=</span>test,typing
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that this is only an example. This PEP does not declare any requirements
|
||
for how tools support the installation of Dependency Groups.</p>
|
||
<section id="overlapping-install-ux-with-extras">
|
||
<h4><a class="toc-backref" href="#overlapping-install-ux-with-extras" role="doc-backlink">Overlapping Install UX with Extras</a></h4>
|
||
<p>Tools MAY choose to provide the same interfaces for installing Dependency
|
||
Groups as they do for installing extras.</p>
|
||
<p>Note that this specification does not forbid having an extra whose name matches
|
||
a Dependency Group.</p>
|
||
<p>Users are advised to avoid creating Dependency Groups whose names match extras.
|
||
Tools MAY treat such matching as an error.</p>
|
||
</section>
|
||
</section>
|
||
<section id="validation-and-compatibility">
|
||
<h3><a class="toc-backref" href="#validation-and-compatibility" role="doc-backlink">Validation and Compatibility</a></h3>
|
||
<p>Tools supporting Dependency Groups may want to validate data before using it.
|
||
However, tools implementing such validation behavior should be careful to allow
|
||
for future expansions to this spec, so that they do not unnecessarily emit
|
||
errors or warnings in the presence of new syntax.</p>
|
||
<p>Tools SHOULD error when evaluating or processing unrecognized data in
|
||
Dependency Groups.</p>
|
||
<p>Tools SHOULD NOT eagerly validate the list contents of <strong>all</strong> Dependency
|
||
Groups.</p>
|
||
<p>This means that in the presence of the following data, most tools will allow
|
||
the <code class="docutils literal notranslate"><span class="pre">foo</span></code> group to be used, and will only error when the <code class="docutils literal notranslate"><span class="pre">bar</span></code> group is
|
||
used:</p>
|
||
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="k">[dependency-groups]</span>
|
||
<span class="n">foo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"pyparsing"</span><span class="p">]</span>
|
||
<span class="n">bar</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[{</span><span class="n">set-phasers-to</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s2">"stun"</span><span class="p">}]</span>
|
||
</pre></div>
|
||
</div>
|
||
<section id="linters-and-validators-may-be-stricter">
|
||
<h4><a class="toc-backref" href="#linters-and-validators-may-be-stricter" role="doc-backlink">Linters and Validators may be stricter</a></h4>
|
||
<p>Eager validation is discouraged for tools which primarily install or resolve
|
||
Dependency Groups.
|
||
Linters and validation tools may have good cause to ignore this recommendation.</p>
|
||
</section>
|
||
</section>
|
||
</section>
|
||
<section id="reference-implementation">
|
||
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
|
||
<p>The following Reference Implementation prints the contents of a Dependency
|
||
Group to stdout, newline delimited.
|
||
The output is therefore valid <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> data.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">re</span>
|
||
<span class="kn">import</span> <span class="nn">sys</span>
|
||
<span class="kn">import</span> <span class="nn">tomllib</span>
|
||
<span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">defaultdict</span>
|
||
|
||
<span class="kn">from</span> <span class="nn">packaging.requirements</span> <span class="kn">import</span> <span class="n">Requirement</span>
|
||
|
||
|
||
<span class="k">def</span> <span class="nf">_normalize_name</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">r</span><span class="s2">"[-_.]+"</span><span class="p">,</span> <span class="s2">"-"</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
|
||
|
||
|
||
<span class="k">def</span> <span class="nf">_normalize_group_names</span><span class="p">(</span><span class="n">dependency_groups</span><span class="p">:</span> <span class="nb">dict</span><span class="p">)</span> <span class="o">-></span> <span class="nb">dict</span><span class="p">:</span>
|
||
<span class="n">original_names</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span>
|
||
<span class="n">normalized_groups</span> <span class="o">=</span> <span class="p">{}</span>
|
||
|
||
<span class="k">for</span> <span class="n">group_name</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">dependency_groups</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
|
||
<span class="n">normed_group_name</span> <span class="o">=</span> <span class="n">_normalize_name</span><span class="p">(</span><span class="n">group_name</span><span class="p">)</span>
|
||
<span class="n">original_names</span><span class="p">[</span><span class="n">normed_group_name</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">group_name</span><span class="p">)</span>
|
||
<span class="n">normalized_groups</span><span class="p">[</span><span class="n">normed_group_name</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
|
||
|
||
<span class="n">errors</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="k">for</span> <span class="n">normed_name</span><span class="p">,</span> <span class="n">names</span> <span class="ow">in</span> <span class="n">original_names</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
|
||
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">names</span><span class="p">)</span> <span class="o">></span> <span class="mi">1</span><span class="p">:</span>
|
||
<span class="n">errors</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">normed_name</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="s1">', '</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">names</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">errors</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">"Duplicate dependency group names: </span><span class="si">{</span><span class="s1">', '</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">errors</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
|
||
<span class="k">return</span> <span class="n">normalized_groups</span>
|
||
|
||
|
||
<span class="k">def</span> <span class="nf">_resolve_dependency_group</span><span class="p">(</span>
|
||
<span class="n">dependency_groups</span><span class="p">:</span> <span class="nb">dict</span><span class="p">,</span> <span class="n">group</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">past_groups</span><span class="p">:</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="o">...</span><span class="p">]</span> <span class="o">=</span> <span class="p">()</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
|
||
<span class="k">if</span> <span class="n">group</span> <span class="ow">in</span> <span class="n">past_groups</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">"Cyclic dependency group include: </span><span class="si">{</span><span class="n">group</span><span class="si">}</span><span class="s2"> -> </span><span class="si">{</span><span class="n">past_groups</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
|
||
<span class="k">if</span> <span class="n">group</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">dependency_groups</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">LookupError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Dependency group '</span><span class="si">{</span><span class="n">group</span><span class="si">}</span><span class="s2">' not found"</span><span class="p">)</span>
|
||
|
||
<span class="n">raw_group</span> <span class="o">=</span> <span class="n">dependency_groups</span><span class="p">[</span><span class="n">group</span><span class="p">]</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">raw_group</span><span class="p">,</span> <span class="nb">list</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">"Dependency group '</span><span class="si">{</span><span class="n">group</span><span class="si">}</span><span class="s2">' is not a list"</span><span class="p">)</span>
|
||
|
||
<span class="n">realized_group</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">raw_group</span><span class="p">:</span>
|
||
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
|
||
<span class="c1"># packaging.requirements.Requirement parsing ensures that this is a valid</span>
|
||
<span class="c1"># PEP 508 Dependency Specifier</span>
|
||
<span class="c1"># raises InvalidRequirement on failure</span>
|
||
<span class="n">Requirement</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
|
||
<span class="n">realized_group</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
|
||
<span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="nb">dict</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">keys</span><span class="p">())</span> <span class="o">!=</span> <span class="p">(</span><span class="s2">"include-group"</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">"Invalid dependency group item: </span><span class="si">{</span><span class="n">item</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
|
||
<span class="n">include_group</span> <span class="o">=</span> <span class="n">_normalize_name</span><span class="p">(</span><span class="nb">next</span><span class="p">(</span><span class="nb">iter</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">values</span><span class="p">())))</span>
|
||
<span class="n">realized_group</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span>
|
||
<span class="n">_resolve_dependency_group</span><span class="p">(</span>
|
||
<span class="n">dependency_groups</span><span class="p">,</span> <span class="n">include_group</span><span class="p">,</span> <span class="n">past_groups</span> <span class="o">+</span> <span class="p">(</span><span class="n">group</span><span class="p">,)</span>
|
||
<span class="p">)</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">"Invalid dependency group item: </span><span class="si">{</span><span class="n">item</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
|
||
<span class="k">return</span> <span class="n">realized_group</span>
|
||
|
||
|
||
<span class="k">def</span> <span class="nf">resolve</span><span class="p">(</span><span class="n">dependency_groups</span><span class="p">:</span> <span class="nb">dict</span><span class="p">,</span> <span class="n">group</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">dependency_groups</span><span class="p">,</span> <span class="nb">dict</span><span class="p">):</span>
|
||
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">"Dependency Groups table is not a dict"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">group</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
|
||
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">"Dependency group name is not a str"</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">_resolve_dependency_group</span><span class="p">(</span><span class="n">dependency_groups</span><span class="p">,</span> <span class="n">group</span><span class="p">)</span>
|
||
|
||
|
||
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
|
||
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s2">"pyproject.toml"</span><span class="p">,</span> <span class="s2">"rb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">fp</span><span class="p">:</span>
|
||
<span class="n">pyproject</span> <span class="o">=</span> <span class="n">tomllib</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">fp</span><span class="p">)</span>
|
||
|
||
<span class="n">dependency_groups_raw</span> <span class="o">=</span> <span class="n">pyproject</span><span class="p">[</span><span class="s2">"dependency-groups"</span><span class="p">]</span>
|
||
<span class="n">dependency_groups</span> <span class="o">=</span> <span class="n">_normalize_group_names</span><span class="p">(</span><span class="n">dependency_groups_raw</span><span class="p">)</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">resolve</span><span class="p">(</span><span class="n">pyproject</span><span class="p">[</span><span class="s2">"dependency-groups"</span><span class="p">],</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])))</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="backwards-compatibility">
|
||
<h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2>
|
||
<p>At time of writing, the <code class="docutils literal notranslate"><span class="pre">dependency-groups</span></code> namespace within a
|
||
<code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> file is unused. Since the top-level namespace is
|
||
reserved for use only by standards specified at packaging.python.org,
|
||
there are no direct backwards compatibility concerns.</p>
|
||
<p>However, the introduction of the feature has implications for a
|
||
number of ecosystem tools, especially those which attempt to support
|
||
examination of data in <code class="docutils literal notranslate"><span class="pre">setup.py</span></code> and <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code>.</p>
|
||
<section id="audit-and-update-tools">
|
||
<h3><a class="toc-backref" href="#audit-and-update-tools" role="doc-backlink">Audit and Update Tools</a></h3>
|
||
<p>A wide range of tools understand Python dependency data as expressed in
|
||
<code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files. (e.g., Dependabot, Tidelift, etc)</p>
|
||
<p>Such tools inspect dependency data and, in some cases, offer tool-assisted or
|
||
fully automated updates.
|
||
It is our expectation that no such tools would support the new Dependency
|
||
Groups at first, and broad ecosystem support could take many months or even some
|
||
number of years to arrive.</p>
|
||
<p>As a result, users of Dependency Groups would experience a degradation in their
|
||
workflows and tool support at the time that they start using Dependency Groups.
|
||
This is true of any new standard for where and how dependency data are encoded.</p>
|
||
</section>
|
||
</section>
|
||
<section id="security-implications">
|
||
<h2><a class="toc-backref" href="#security-implications" role="doc-backlink">Security Implications</a></h2>
|
||
<p>This PEP introduces new syntaxes and data formats for specifying dependency
|
||
information in projects. However, it does not introduce newly specified
|
||
mechanisms for handling or resolving dependencies.</p>
|
||
<p>It therefore does not carry security concerns other than those inherent in any
|
||
tools which may already be used to install dependencies – i.e. malicious
|
||
dependencies may be specified here, just as they may be specified in
|
||
<code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files.</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>This feature should be referred to by its canonical name, “Dependency Groups”.</p>
|
||
<p>The basic form of usage should be taught as a variant on typical
|
||
<code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> data. Standard dependency specifiers (<a class="pep reference internal" href="../pep-0508/" title="PEP 508 – Dependency specification for Python Software Packages">PEP 508</a>) can be
|
||
added to a named list. Rather than asking pip to install from a
|
||
<code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> file, either pip or a relevant workflow tool will install
|
||
from a named Dependency Group.</p>
|
||
<p>For new Python users, they may be taught directly to create a section in
|
||
<code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> containing their Dependency Groups, similarly to how they
|
||
are currently taught to use <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files.
|
||
This also allows new Python users to learn about <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> files
|
||
without needing to learn about package building.
|
||
A <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> file with only <code class="docutils literal notranslate"><span class="pre">[dependency-groups]</span></code> and no other tables
|
||
is valid.</p>
|
||
<p>For both new and experienced users, the Dependency Group Includes will need to
|
||
be explained. For users with experience using <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code>, this can be
|
||
described as an analogue for <code class="docutils literal notranslate"><span class="pre">-r</span></code>. For new users, they should be taught that
|
||
an include allows one Dependency Group to extend another. Similar configuration
|
||
interfaces and the Python <code class="docutils literal notranslate"><span class="pre">list.extend</span></code> method may be used to explain the
|
||
idea by analogy.</p>
|
||
<p>Python users who have used <code class="docutils literal notranslate"><span class="pre">setup.py</span></code> packaging may be familiar with common
|
||
practices which predate <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>, in which package metadata is
|
||
defined dynamically. Requirements loaded from <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files and
|
||
definitions of static lists prior to <code class="docutils literal notranslate"><span class="pre">setup()</span></code> invocation readily analogize
|
||
with Dependency Groups.</p>
|
||
<section id="interfaces-for-use-of-dependency-groups">
|
||
<h3><a class="toc-backref" href="#interfaces-for-use-of-dependency-groups" role="doc-backlink">Interfaces for Use of Dependency Groups</a></h3>
|
||
<p>This specification provides no universal interface for interacting with
|
||
Dependency Groups, other than inclusion in a built package via the <code class="docutils literal notranslate"><span class="pre">project</span></code>
|
||
table. This has implications both for tool authors and for users.</p>
|
||
<p>Tool authors should determine how or if Dependency Groups are relevant to their
|
||
user stories, and build their own interfaces to fit.
|
||
For environment managers, resolvers, installers, and related non-build tools,
|
||
they will be able to document that they support “PEP 735 Dependency Groups”,
|
||
but they will be responsible for documenting their usage modes.
|
||
For build backends, supporting Dependency Groups will require support for
|
||
inclusion from the <code class="docutils literal notranslate"><span class="pre">project</span></code> table, but set no other strict requirements.</p>
|
||
<p>For users, the primary consequence is that they must consult relevant tool
|
||
documentation whenever they wish to use Dependency Groups outside of package
|
||
builds.
|
||
Users should be advised by tools, either through documentation or runtime
|
||
warnings or errors, about usages which are disrecommended or not supported.
|
||
For example, if a tool wishes to require that all Dependency Groups are
|
||
mutually compatible, containing no contradictory package specifiers, it
|
||
should document that restriction and advise users on how to appropriately
|
||
leverage Dependency Groups for its purposes.</p>
|
||
</section>
|
||
</section>
|
||
<section id="rejected-ideas">
|
||
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2>
|
||
<section id="why-not-define-each-dependency-group-as-a-table">
|
||
<h3><a class="toc-backref" href="#why-not-define-each-dependency-group-as-a-table" role="doc-backlink">Why not define each Dependency Group as a table?</a></h3>
|
||
<p>If our goal is to allow for future expansion, then defining each Dependency
|
||
Group as a subtable, thus enabling us to attach future keys to each group,
|
||
allows for the greatest future flexibility.</p>
|
||
<p>However, it also makes the structure nested more deeply, and therefore harder
|
||
to teach and learn. One of the goals of this PEP is to be an easy replacement
|
||
for many <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> use-cases.</p>
|
||
</section>
|
||
<section id="why-not-define-a-special-string-syntax-to-extend-dependency-specifiers">
|
||
<h3><a class="toc-backref" href="#why-not-define-a-special-string-syntax-to-extend-dependency-specifiers" role="doc-backlink">Why not define a special string syntax to extend Dependency Specifiers?</a></h3>
|
||
<p>Earlier drafts of this specification defined syntactic forms for Dependency
|
||
Group Includes and Path Dependencies.</p>
|
||
<p>However, there were three major issues with this approach:</p>
|
||
<ul class="simple">
|
||
<li>it complicates the string syntax which must be taught, beyond PEP 508</li>
|
||
<li>the resulting strings would always need to be disambiguated from PEP 508
|
||
specifiers, complicating implementations</li>
|
||
</ul>
|
||
</section>
|
||
<section id="why-not-allow-for-more-non-pep-508-dependency-specifiers">
|
||
<h3><a class="toc-backref" href="#why-not-allow-for-more-non-pep-508-dependency-specifiers" role="doc-backlink">Why not allow for more non-PEP 508 dependency specifiers?</a></h3>
|
||
<p>Several use cases surfaced during discussion which need more expressive
|
||
specifiers than are possible with <a class="pep reference internal" href="../pep-0508/" title="PEP 508 – Dependency specification for Python Software Packages">PEP 508</a>.</p>
|
||
<p>“Path Dependencies”, referring to local paths, and references to
|
||
<code class="docutils literal notranslate"><span class="pre">[project.dependencies]</span></code> were of particular interest.</p>
|
||
<p>However, there are no existing standards for these features (excepting the
|
||
de-facto standard of <code class="docutils literal notranslate"><span class="pre">pip</span></code>’s implementation details).</p>
|
||
<p>As a result, attempting to include these features in this PEP results in a
|
||
significant growth in scope, to attempt to standardize these various features
|
||
and <code class="docutils literal notranslate"><span class="pre">pip</span></code> behaviors.</p>
|
||
<p>Special attention was devoted to attempting to standardize the expression of
|
||
editable installations, as expressed by <code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">-e</span></code> and <a class="pep reference internal" href="../pep-0660/" title="PEP 660 – Editable installs for pyproject.toml based builds (wheel based)">PEP 660</a>.
|
||
However, although the creation of editable installs is standardized for build
|
||
backends, the behavior of editables is not standardized for installers.
|
||
Inclusion of editables in this PEP requires that any supporting tool allows for
|
||
the installation of editables.</p>
|
||
<p>Therefore, although Poetry and PDM provide syntaxes for some of these features,
|
||
they are considered insufficiently standardized at present for inclusion in
|
||
Dependency Groups.</p>
|
||
</section>
|
||
<section id="why-is-the-table-not-named-run-project-dependency-groups">
|
||
<h3><a class="toc-backref" href="#why-is-the-table-not-named-run-project-dependency-groups" role="doc-backlink">Why is the table not named <code class="docutils literal notranslate"><span class="pre">[run]</span></code>, <code class="docutils literal notranslate"><span class="pre">[project.dependency-groups]</span></code>, …?</a></h3>
|
||
<p>There are many possible names for this concept.
|
||
It will have to live alongside the already existing <code class="docutils literal notranslate"><span class="pre">[project.dependencies]</span></code>
|
||
and <code class="docutils literal notranslate"><span class="pre">[project.optional-dependencies]</span></code> tables, and possibly a new
|
||
<code class="docutils literal notranslate"><span class="pre">[external]</span></code> dependency table as well (at time of writing, <a class="pep reference internal" href="../pep-0725/" title="PEP 725 – Specifying external dependencies in pyproject.toml">PEP 725</a>, which
|
||
defines the <code class="docutils literal notranslate"><span class="pre">[external]</span></code> table, is in progress).</p>
|
||
<p><code class="docutils literal notranslate"><span class="pre">[run]</span></code> was a leading proposal in earlier discussions, but its proposed usage
|
||
centered around a single set of runtime dependencies. This PEP explicitly
|
||
outlines multiple groups of dependencies, which makes <code class="docutils literal notranslate"><span class="pre">[run]</span></code> a less
|
||
appropriate fit – this is not just dependency data for a specific runtime
|
||
context, but for multiple contexts.</p>
|
||
<p><code class="docutils literal notranslate"><span class="pre">[project.dependency-groups]</span></code> would offer a nice parallel with
|
||
<code class="docutils literal notranslate"><span class="pre">[project.dependencies]</span></code> and <code class="docutils literal notranslate"><span class="pre">[project.optional-dependencies]</span></code>, but has
|
||
major downsides for non-package projects.
|
||
<code class="docutils literal notranslate"><span class="pre">[project]</span></code> requires several keys to be defined, such as <code class="docutils literal notranslate"><span class="pre">name</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">version</span></code>. Using this name would either require redefining the <code class="docutils literal notranslate"><span class="pre">[project]</span></code>
|
||
table to allow for these keys to be absent, or else would impose a requirement
|
||
on non-package projects to define and use these keys. By extension, it would
|
||
effectively require any non-package project allow itself to be treated as a
|
||
package.</p>
|
||
</section>
|
||
<section id="why-is-pip-s-planned-implementation-of-only-deps-not-sufficient">
|
||
<h3><a class="toc-backref" href="#why-is-pip-s-planned-implementation-of-only-deps-not-sufficient" role="doc-backlink">Why is pip’s planned implementation of <code class="docutils literal notranslate"><span class="pre">--only-deps</span></code> not sufficient?</a></h3>
|
||
<p>pip currently has a feature on the roadmap to add an
|
||
<a class="reference external" href="https://github.com/pypa/pip/issues/11440">–only-deps flag</a>.
|
||
This flag is intended to allow users to install package dependencies and extras
|
||
without installing the current package.</p>
|
||
<p>It does not address the needs of non-package projects, nor does it allow for
|
||
the installation of an extra without the package dependencies.</p>
|
||
</section>
|
||
<section id="why-isn-t-environment-manager-a-solution">
|
||
<h3><a class="toc-backref" href="#why-isn-t-environment-manager-a-solution" role="doc-backlink">Why isn’t <environment manager> a solution?</a></h3>
|
||
<p>Existing environment managers like tox, Nox, and Hatch already have
|
||
the ability to list inlined dependencies as part of their configuration data.
|
||
This meets many development dependency needs, and clearly associates dependency
|
||
groups with relevant tasks which can be run.
|
||
These mechanisms are <em>good</em> but they are not <em>sufficient</em>.</p>
|
||
<p>First, they do not address the needs of non-package projects.</p>
|
||
<p>Second, there is no standard for other tools to use to access these data. This
|
||
has impacts on high-level tools like IDEs and Dependabot, which cannot support
|
||
deep integration with these Dependency Groups. (For example, at time of writing
|
||
Dependabot will not flag dependencies which are pinned in <code class="docutils literal notranslate"><span class="pre">tox.ini</span></code> files.)</p>
|
||
</section>
|
||
</section>
|
||
<section id="deferred-ideas">
|
||
<h2><a class="toc-backref" href="#deferred-ideas" role="doc-backlink">Deferred Ideas</a></h2>
|
||
<section id="why-not-support-dependency-group-includes-in-project-dependencies-or-project-optional-dependencies">
|
||
<h3><a class="toc-backref" href="#why-not-support-dependency-group-includes-in-project-dependencies-or-project-optional-dependencies" role="doc-backlink">Why not support Dependency Group Includes in <code class="docutils literal notranslate"><span class="pre">[project.dependencies]</span></code> or <code class="docutils literal notranslate"><span class="pre">[project.optional-dependencies]</span></code>?</a></h3>
|
||
<p>Earlier drafts of this specification allowed Dependency Group Includes to be
|
||
used in the <code class="docutils literal notranslate"><span class="pre">[project]</span></code> table.
|
||
However, there were several issues raised during community feedback which led
|
||
to its removal.</p>
|
||
<p>Only a small number of additional use cases would be addressed by the inclusion
|
||
of Dependency Groups, and it increased the scope of the specification
|
||
significantly. In particular, this inclusion would increase the number of parties
|
||
impacted by the addition. Many readers of the <code class="docutils literal notranslate"><span class="pre">[project]</span></code> table, including build
|
||
backends, SBOM generators, and dependency analyzers are implicated by a change to
|
||
<code class="docutils literal notranslate"><span class="pre">[project]</span></code> but may continue to operate as-is in the presence of a new (but
|
||
unconnected) <code class="docutils literal notranslate"><span class="pre">[dependency-groups]</span></code> table.</p>
|
||
<p>Separately from the above concern, allowing inclusion of dependency groups from the
|
||
<code class="docutils literal notranslate"><span class="pre">[project]</span></code> table encourages package maintainers to move dependency metadata out
|
||
of the current standard location.
|
||
This complicates static <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> metadata and conflicts with the goal of
|
||
<a class="pep reference internal" href="../pep-0621/" title="PEP 621 – Storing project metadata in pyproject.toml">PEP 621</a> to store dependency metadata in a single location.</p>
|
||
<p>Finally, exclusion of <code class="docutils literal notranslate"><span class="pre">[project]</span></code> support from this PEP is not final. The
|
||
use of includes from that table, or an inclusion syntax from
|
||
<code class="docutils literal notranslate"><span class="pre">[dependency-groups]</span></code> into <code class="docutils literal notranslate"><span class="pre">[project]</span></code>, could be introduced by a future
|
||
PEP and considered on its own merits.</p>
|
||
<section id="use-cases-for-dependency-group-includes-from-project">
|
||
<h4><a class="toc-backref" href="#use-cases-for-dependency-group-includes-from-project" role="doc-backlink">Use Cases for Dependency Group Includes From <code class="docutils literal notranslate"><span class="pre">[project]</span></code></a></h4>
|
||
<p>Although deferred in this PEP, allowing includes from the <code class="docutils literal notranslate"><span class="pre">[project]</span></code>
|
||
table would address several use cases.</p>
|
||
<p>In particular, there are cases in which package developers would like to
|
||
install only the dependencies of a package, without the package itself.</p>
|
||
<p>For example:</p>
|
||
<ul class="simple">
|
||
<li>Specify different environment variables or options when building dependencies
|
||
vs when building the package itself</li>
|
||
<li>Creating layered container images in which the dependencies are isolated from
|
||
the package being installed</li>
|
||
<li>Providing the dependencies to analysis environments (e.g., type checking)
|
||
without having to build and install the package itself</li>
|
||
</ul>
|
||
<p>For an example of the last case, consider the following sample
|
||
<code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>:</p>
|
||
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="k">[project]</span>
|
||
<span class="n">dependencies</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[{</span><span class="n">include</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s2">"runtime"</span><span class="p">}]</span>
|
||
<span class="k">[optional-dependencies]</span>
|
||
<span class="n">foo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[{</span><span class="n">include</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s2">"foo"</span><span class="p">}]</span>
|
||
<span class="k">[dependency-groups]</span>
|
||
<span class="n">runtime</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="s2">"b"</span><span class="p">]</span>
|
||
<span class="n">foo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"c"</span><span class="p">,</span><span class="w"> </span><span class="s2">"d"</span><span class="p">]</span>
|
||
<span class="n">typing</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"mypy"</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="n">include</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s2">"runtime"</span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="n">include</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s2">"foo"</span><span class="p">}]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>In this case, a <code class="docutils literal notranslate"><span class="pre">typing</span></code> group can be defined, with all of the package’s
|
||
runtime dependencies, but without the package itself. This allows uses of the
|
||
<code class="docutils literal notranslate"><span class="pre">typing</span></code> Dependency Group to skip installation of the package – not only is
|
||
this more efficient, but it may reduce the requirements for testing systems.</p>
|
||
</section>
|
||
</section>
|
||
<section id="why-not-support-dependency-group-includes-in-build-system-requires">
|
||
<h3><a class="toc-backref" href="#why-not-support-dependency-group-includes-in-build-system-requires" role="doc-backlink">Why not support Dependency Group Includes in <code class="docutils literal notranslate"><span class="pre">[build-system.requires]</span></code>?</a></h3>
|
||
<p>Given that we will not allow for <code class="docutils literal notranslate"><span class="pre">[project]</span></code> usage of Dependency Groups,
|
||
<code class="docutils literal notranslate"><span class="pre">[build-system.requires]</span></code> can be considered in comparison with
|
||
<code class="docutils literal notranslate"><span class="pre">[project.dependencies]</span></code>.</p>
|
||
<p>There are fewer theoretical usages for build requirements specified in a group
|
||
than package requirements. Additionally, the impact of such a change implicates
|
||
<a class="pep reference internal" href="../pep-0517/" title="PEP 517 – A build-system independent format for source trees">PEP 517</a> frontend, which would need to support Dependency Groups in order to
|
||
prepare a build environment.</p>
|
||
<p>Compared with changes to <code class="docutils literal notranslate"><span class="pre">[project.dependencies]</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">[project.optional-dependencies]</span></code>, changing the behaviors of
|
||
<code class="docutils literal notranslate"><span class="pre">[build-system.requires]</span></code> is higher impact and has fewer potential uses.
|
||
Therefore, given that this PEP declines to make changes to the <code class="docutils literal notranslate"><span class="pre">[project]</span></code>
|
||
table, changing <code class="docutils literal notranslate"><span class="pre">[build-system]</span></code> is also deferred.</p>
|
||
</section>
|
||
<section id="why-not-support-a-dependency-group-which-includes-the-current-project">
|
||
<h3><a class="toc-backref" href="#why-not-support-a-dependency-group-which-includes-the-current-project" role="doc-backlink">Why not support a Dependency Group which includes the current project?</a></h3>
|
||
<p>Several usage scenarios for dependency groups revolve around installing a
|
||
dependency group alongside a package defined in the <code class="docutils literal notranslate"><span class="pre">[project]</span></code> table.
|
||
For example, testing a package involves installing testing dependencies and the
|
||
package itself. Additionally, the compatibility of a dependency group with the
|
||
main package is a valuable input to lockfile generators.</p>
|
||
<p>In such cases, it is desirable for a Dependency Group to declare that it
|
||
depends upon the project itself. Example syntaxes from discussions included
|
||
<code class="docutils literal notranslate"><span class="pre">{include-project</span> <span class="pre">=</span> <span class="pre">true}</span></code> and <code class="docutils literal notranslate"><span class="pre">{include-group</span> <span class="pre">=</span> <span class="pre">":project:"}</span></code>.</p>
|
||
<p>However, if a specification is established to extend <a class="pep reference internal" href="../pep-0508/" title="PEP 508 – Dependency specification for Python Software Packages">PEP 508</a> with Path
|
||
Dependencies, this would result in Dependency Groups having two ways of
|
||
specifying the main package. For example, if <code class="docutils literal notranslate"><span class="pre">.</span></code> becomes formally supported,
|
||
and <code class="docutils literal notranslate"><span class="pre">{include-project</span> <span class="pre">=</span> <span class="pre">true}</span></code> is included in this PEP, then dependency
|
||
groups may specify any of the following groups</p>
|
||
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="k">[dependency-groups]</span>
|
||
<span class="n">case1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[{</span><span class="n">include-project</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kc">true</span><span class="p">}]</span>
|
||
<span class="n">case2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"."</span><span class="p">]</span>
|
||
<span class="n">case3</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[{</span><span class="n">include-project</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kc">true</span><span class="p">},</span><span class="w"> </span><span class="s2">"."</span><span class="p">]</span>
|
||
<span class="n">case4</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[{</span><span class="n">include-project</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kc">false</span><span class="p">},</span><span class="w"> </span><span class="s2">"."</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>In order to avoid a confusing future in which multiple different options
|
||
specify the package defined in <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>, any syntax for declaring
|
||
this relationship is omitted from this PEP.</p>
|
||
</section>
|
||
</section>
|
||
<section id="appendix-a-prior-art-in-non-python-languages">
|
||
<span id="prior-art"></span><h2><a class="toc-backref" href="#appendix-a-prior-art-in-non-python-languages" role="doc-backlink">Appendix A: Prior Art in Non-Python Languages</a></h2>
|
||
<p>This section is primarily informational and serves to document how other
|
||
language ecosystems solve similar problems.</p>
|
||
<section id="javascript-and-package-json">
|
||
<span id="javascript-prior-art"></span><h3><a class="toc-backref" href="#javascript-and-package-json" role="doc-backlink">JavaScript and <code class="docutils literal notranslate"><span class="pre">package.json</span></code></a></h3>
|
||
<p>In the JavaScript community, packages contain a canonical configuration and
|
||
data file, similar in scope to <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>, at <code class="docutils literal notranslate"><span class="pre">package.json</span></code>.</p>
|
||
<p>Two keys in <code class="docutils literal notranslate"><span class="pre">package.json</span></code> control dependency data: <code class="docutils literal notranslate"><span class="pre">"dependencies"</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">"devDependencies"</span></code>. The role of <code class="docutils literal notranslate"><span class="pre">"dependencies"</span></code> is effectively the same
|
||
as that of <code class="docutils literal notranslate"><span class="pre">[project.dependencies]</span></code> in <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>, declaring the
|
||
direct dependencies of a package.</p>
|
||
<section id="dependencies-data">
|
||
<h4><a class="toc-backref" href="#dependencies-data" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">"dependencies"</span></code> data</a></h4>
|
||
<p>Dependency data is declared in <code class="docutils literal notranslate"><span class="pre">package.json</span></code> as a mapping from package names
|
||
to version specifiers.</p>
|
||
<p>Version specifiers support a small grammar of possible versions, ranges, and
|
||
other values, similar to Python’s <a class="pep reference internal" href="../pep-0440/" title="PEP 440 – Version Identification and Dependency Specification">PEP 440</a> version specifiers.</p>
|
||
<p>For example, here is a partial <code class="docutils literal notranslate"><span class="pre">package.json</span></code> file declaring a few
|
||
dependencies:</p>
|
||
<div class="highlight-json notranslate"><div class="highlight"><pre><span></span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"dependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"@angular/compiler"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^17.0.2"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"camelcase"</span><span class="p">:</span><span class="w"> </span><span class="s2">"8.0.0"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"diff"</span><span class="p">:</span><span class="w"> </span><span class="s2">">=5.1.0 <6.0.0"</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The use of the <code class="docutils literal notranslate"><span class="pre">@</span></code> symbol is a <a class="reference external" href="https://docs.npmjs.com/cli/v10/using-npm/scope">scope</a> which declares the package
|
||
owner, for organizationally owned packages.
|
||
<code class="docutils literal notranslate"><span class="pre">"@angular/compiler"</span></code> therefore declares a package named <code class="docutils literal notranslate"><span class="pre">compiler</span></code> grouped
|
||
under <code class="docutils literal notranslate"><span class="pre">angular</span></code> ownership.</p>
|
||
</section>
|
||
<section id="dependencies-referencing-urls-and-local-paths">
|
||
<h4><a class="toc-backref" href="#dependencies-referencing-urls-and-local-paths" role="doc-backlink">Dependencies Referencing URLs and Local Paths</a></h4>
|
||
<p>Dependency specifiers support a syntax for URLs and Git repositories, similar
|
||
to the provisions in Python packaging.</p>
|
||
<p>URLs may be used in lieu of version numbers.
|
||
When used, they implicitly refer to tarballs of package source code.</p>
|
||
<p>Git repositories may be similarly used, including support for committish
|
||
specifiers.</p>
|
||
<p>Unlike <a class="pep reference internal" href="../pep-0440/" title="PEP 440 – Version Identification and Dependency Specification">PEP 440</a>, NPM allows for the use of local paths to package source code
|
||
directories for dependencies. When these data are added to <code class="docutils literal notranslate"><span class="pre">package.json</span></code> via
|
||
the standard <code class="docutils literal notranslate"><span class="pre">npm</span> <span class="pre">install</span> <span class="pre">--save</span></code> command, the path is normalized to a
|
||
relative path, from the directory containing <code class="docutils literal notranslate"><span class="pre">package.json</span></code>, and prefixed
|
||
with <code class="docutils literal notranslate"><span class="pre">file:</span></code>. For example, the following partial <code class="docutils literal notranslate"><span class="pre">package.json</span></code> contains a
|
||
reference to a sibling of the current directory:</p>
|
||
<div class="highlight-json notranslate"><div class="highlight"><pre><span></span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"dependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"my-package"</span><span class="p">:</span><span class="w"> </span><span class="s2">"file:../foo"</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The <a class="reference external" href="https://docs.npmjs.com/cli/v8/configuring-npm/package-json#local-paths">official NPM documentation</a>
|
||
states that local path dependencies “should not” be published to public package
|
||
repositories, but makes no statement about the inherent validity or invalidity
|
||
of such dependency data in published packages.</p>
|
||
</section>
|
||
<section id="devdependencies-data">
|
||
<h4><a class="toc-backref" href="#devdependencies-data" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">"devDependencies"</span></code> data</a></h4>
|
||
<p><code class="docutils literal notranslate"><span class="pre">package.json</span></code> is permitted to contain a second section named
|
||
<code class="docutils literal notranslate"><span class="pre">"devDependencies"</span></code>, in the same format as <code class="docutils literal notranslate"><span class="pre">"dependencies"</span></code>.
|
||
The dependencies declared in <code class="docutils literal notranslate"><span class="pre">"devDependencies"</span></code> are not installed by default
|
||
when a package is installed from the package repository (e.g. as part of a
|
||
dependency being resolved) but are installed when <code class="docutils literal notranslate"><span class="pre">npm</span> <span class="pre">install</span></code> is run in the
|
||
source tree containing <code class="docutils literal notranslate"><span class="pre">package.json</span></code>.</p>
|
||
<p>Just as <code class="docutils literal notranslate"><span class="pre">"dependencies"</span></code> supports URLs and local paths, so does
|
||
<code class="docutils literal notranslate"><span class="pre">"devDependencies"</span></code>.</p>
|
||
</section>
|
||
<section id="peerdependencies-and-optionaldependencies">
|
||
<h4><a class="toc-backref" href="#peerdependencies-and-optionaldependencies" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">"peerDependencies"</span></code> and <code class="docutils literal notranslate"><span class="pre">"optionalDependencies"</span></code></a></h4>
|
||
<p>There are two additional, related sections in <code class="docutils literal notranslate"><span class="pre">package.json</span></code> which have
|
||
relevance.</p>
|
||
<p><code class="docutils literal notranslate"><span class="pre">"peerDependencies"</span></code> declares a list of dependencies in the same format as
|
||
<code class="docutils literal notranslate"><span class="pre">"dependencies"</span></code>, but with the meaning that these are a compatibility
|
||
declaration.
|
||
For example, the following data declares compatibility with package <code class="docutils literal notranslate"><span class="pre">foo</span></code>
|
||
version 2:</p>
|
||
<div class="highlight-json notranslate"><div class="highlight"><pre><span></span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"peerDependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"foo"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.x"</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">"optionalDependencies"</span></code> declares a list of dependencies which should be
|
||
installed if possible, but which should not be treated as failures if they are
|
||
unavailable. It also uses the same mapping format as <code class="docutils literal notranslate"><span class="pre">"dependencies"</span></code>.</p>
|
||
<section id="peerdependenciesmeta">
|
||
<h5><a class="toc-backref" href="#peerdependenciesmeta" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">"peerDependenciesMeta"</span></code></a></h5>
|
||
<p><code class="docutils literal notranslate"><span class="pre">"peerDependenciesMeta"</span></code> is a section which allows for additional control
|
||
over how <code class="docutils literal notranslate"><span class="pre">"peerDependencies"</span></code> are treated.</p>
|
||
<p>Warnings about missing dependencies can be disabled by setting packages to
|
||
<code class="docutils literal notranslate"><span class="pre">optional</span></code> in this section, as in the following sample:</p>
|
||
<div class="highlight-json notranslate"><div class="highlight"><pre><span></span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"peerDependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"foo"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.x"</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="nt">"peerDependenciesMeta"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"foo"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"optional"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
<section id="omit-and-include">
|
||
<h4><a class="toc-backref" href="#omit-and-include" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">--omit</span></code> and <code class="docutils literal notranslate"><span class="pre">--include</span></code></a></h4>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">npm</span> <span class="pre">install</span></code> command supports two options, <code class="docutils literal notranslate"><span class="pre">--omit</span></code> and <code class="docutils literal notranslate"><span class="pre">--include</span></code>,
|
||
which can control whether “prod”, “dev”, “optional”, or “peer” dependencies are installed.</p>
|
||
<p>The “prod” name refers to dependencies listed under <code class="docutils literal notranslate"><span class="pre">"dependencies"</span></code>.</p>
|
||
<p>By default, all four groups are installed when <code class="docutils literal notranslate"><span class="pre">npm</span> <span class="pre">install</span></code> is executed
|
||
against a source tree, but these options can be used to control installation
|
||
behavior more precisely.
|
||
Furthermore, these values can be declared in <code class="docutils literal notranslate"><span class="pre">.npmrc</span></code> files, allowing
|
||
per-user and per-project configurations to control installation behaviors.</p>
|
||
</section>
|
||
</section>
|
||
<section id="ruby-ruby-gems">
|
||
<span id="ruby-prior-art"></span><h3><a class="toc-backref" href="#ruby-ruby-gems" role="doc-backlink">Ruby & Ruby Gems</a></h3>
|
||
<p>Ruby projects may or may not be intended to produce packages (“gems”) in the
|
||
Ruby ecosystem. In fact, the expectation is that most users of the language do
|
||
not want to produce gems and have no interest in producing their own packages.
|
||
Many tutorials do not touch on how to produce packages, and the toolchain never
|
||
requires user code to be packaged for supported use-cases.</p>
|
||
<p>Ruby splits requirement specification into two separate files.</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">Gemfile</span></code>: a dedicated file which only supports requirement data in the form
|
||
of dependency groups</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre"><package>.gemspec</span></code>: a dedicated file for declaring package (gem) metadata</li>
|
||
</ul>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">bundler</span></code> tool, providing the <code class="docutils literal notranslate"><span class="pre">bundle</span></code> command, is the primary interface
|
||
for using <code class="docutils literal notranslate"><span class="pre">Gemfile</span></code> data.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">gem</span></code> tool is responsible for building gems from <code class="docutils literal notranslate"><span class="pre">.gemspec</span></code> data, via the
|
||
<code class="docutils literal notranslate"><span class="pre">gem</span> <span class="pre">build</span></code> command.</p>
|
||
<section id="gemfiles-bundle">
|
||
<h4><a class="toc-backref" href="#gemfiles-bundle" role="doc-backlink">Gemfiles & bundle</a></h4>
|
||
<p>A <a class="reference external" href="https://bundler.io/v1.12/man/gemfile.5.html">Gemfile</a> is a Ruby file
|
||
containing <code class="docutils literal notranslate"><span class="pre">gem</span></code> directives enclosed in any number of <code class="docutils literal notranslate"><span class="pre">group</span></code> declarations.
|
||
<code class="docutils literal notranslate"><span class="pre">gem</span></code> directives may also be used outside of the <code class="docutils literal notranslate"><span class="pre">group</span></code> declaration, in which
|
||
case they form an implicitly unnamed group of dependencies.</p>
|
||
<p>For example, the following <code class="docutils literal notranslate"><span class="pre">Gemfile</span></code> lists <code class="docutils literal notranslate"><span class="pre">rails</span></code> as a project dependency.
|
||
All other dependencies are listed under groups:</p>
|
||
<div class="highlight-ruby notranslate"><div class="highlight"><pre><span></span><span class="n">source</span><span class="w"> </span><span class="s1">'https://rubygems.org'</span>
|
||
|
||
<span class="n">gem</span><span class="w"> </span><span class="s1">'rails'</span>
|
||
|
||
<span class="n">group</span><span class="w"> </span><span class="ss">:test</span><span class="w"> </span><span class="k">do</span>
|
||
<span class="w"> </span><span class="n">gem</span><span class="w"> </span><span class="s1">'rspec'</span>
|
||
<span class="k">end</span>
|
||
|
||
<span class="n">group</span><span class="w"> </span><span class="ss">:lint</span><span class="w"> </span><span class="k">do</span>
|
||
<span class="w"> </span><span class="n">gem</span><span class="w"> </span><span class="s1">'rubocop'</span>
|
||
<span class="k">end</span>
|
||
|
||
<span class="n">group</span><span class="w"> </span><span class="ss">:docs</span><span class="w"> </span><span class="k">do</span>
|
||
<span class="w"> </span><span class="n">gem</span><span class="w"> </span><span class="s1">'kramdown'</span>
|
||
<span class="w"> </span><span class="n">gem</span><span class="w"> </span><span class="s1">'nokogiri'</span>
|
||
<span class="k">end</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>If a user executes <code class="docutils literal notranslate"><span class="pre">bundle</span> <span class="pre">install</span></code> with these data, all groups are
|
||
installed. Users can deselect groups by creating or modifying a bundler config
|
||
in <code class="docutils literal notranslate"><span class="pre">.bundle/config</span></code>, either manually or via the CLI. For example, <code class="docutils literal notranslate"><span class="pre">bundle</span>
|
||
<span class="pre">config</span> <span class="pre">set</span> <span class="pre">--local</span> <span class="pre">without</span> <span class="pre">'lint:docs'</span></code>.</p>
|
||
<p>It is not possible, with the above data, to exclude the top-level use of the
|
||
<code class="docutils literal notranslate"><span class="pre">'rails'</span></code> gem or to refer to that implicit grouping by name.</p>
|
||
</section>
|
||
<section id="gemspec-and-packaged-dependency-data">
|
||
<h4><a class="toc-backref" href="#gemspec-and-packaged-dependency-data" role="doc-backlink">gemspec and packaged dependency data</a></h4>
|
||
<p>A <a class="reference external" href="https://guides.rubygems.org/specification-reference/">gemspec file</a> is a
|
||
ruby file containing a <a class="reference external" href="https://ruby-doc.org/stdlib-3.0.1/libdoc/rubygems/rdoc/Gem/Specification.html">Gem::Specification</a>
|
||
instance declaration.</p>
|
||
<p>Only two fields in a <code class="docutils literal notranslate"><span class="pre">Gem::Specification</span></code> pertain to package dependency data.
|
||
These are <code class="docutils literal notranslate"><span class="pre">add_development_dependency</span></code> and <code class="docutils literal notranslate"><span class="pre">add_runtime_dependency</span></code>.
|
||
A <code class="docutils literal notranslate"><span class="pre">Gem::Specification</span></code> object also provides methods for adding dependencies
|
||
dynamically, including <code class="docutils literal notranslate"><span class="pre">add_dependency</span></code> (which adds a runtime dependency).</p>
|
||
<p>Here is a variant of the <code class="docutils literal notranslate"><span class="pre">rails.gemspec</span></code> file, with many fields removed or
|
||
shortened to simplify:</p>
|
||
<div class="highlight-ruby 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">'7.1.2'</span>
|
||
|
||
<span class="no">Gem</span><span class="o">::</span><span class="no">Specification</span><span class="o">.</span><span class="n">new</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="o">|</span><span class="n">s</span><span class="o">|</span>
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">platform</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">Gem</span><span class="o">::</span><span class="no">Platform</span><span class="o">::</span><span class="no">RUBY</span>
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"rails"</span>
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">version</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">version</span>
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">summary</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Full-stack web application framework."</span>
|
||
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">license</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"MIT"</span>
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">author</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"David Heinemeier Hansson"</span>
|
||
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">files</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">[</span><span class="s2">"README.md"</span><span class="p">,</span><span class="w"> </span><span class="s2">"MIT-LICENSE"</span><span class="o">]</span>
|
||
|
||
<span class="w"> </span><span class="c1"># shortened from the real 'rails' project</span>
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">add_dependency</span><span class="w"> </span><span class="s2">"activesupport"</span><span class="p">,</span><span class="w"> </span><span class="n">version</span>
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">add_dependency</span><span class="w"> </span><span class="s2">"activerecord"</span><span class="p">,</span><span class="w"> </span><span class="n">version</span>
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">add_dependency</span><span class="w"> </span><span class="s2">"actionmailer"</span><span class="p">,</span><span class="w"> </span><span class="n">version</span>
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">add_dependency</span><span class="w"> </span><span class="s2">"activestorage"</span><span class="p">,</span><span class="w"> </span><span class="n">version</span>
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">add_dependency</span><span class="w"> </span><span class="s2">"railties"</span><span class="p">,</span><span class="w"> </span><span class="n">version</span>
|
||
<span class="k">end</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that there is no use of <code class="docutils literal notranslate"><span class="pre">add_development_dependency</span></code>.
|
||
Some other mainstream, major packages (e.g. <code class="docutils literal notranslate"><span class="pre">rubocop</span></code>) do not use development
|
||
dependencies in their gems.</p>
|
||
<p>Other projects <em>do</em> use this feature. For example, <code class="docutils literal notranslate"><span class="pre">kramdown</span></code> makes use of
|
||
development dependencies, containing the following specification in its
|
||
<code class="docutils literal notranslate"><span class="pre">Rakefile</span></code>:</p>
|
||
<div class="highlight-ruby notranslate"><div class="highlight"><pre><span></span><span class="n">s</span><span class="o">.</span><span class="n">add_dependency</span><span class="w"> </span><span class="s2">"rexml"</span>
|
||
<span class="n">s</span><span class="o">.</span><span class="n">add_development_dependency</span><span class="w"> </span><span class="s1">'minitest'</span><span class="p">,</span><span class="w"> </span><span class="s1">'~> 5.0'</span>
|
||
<span class="n">s</span><span class="o">.</span><span class="n">add_development_dependency</span><span class="w"> </span><span class="s1">'rouge'</span><span class="p">,</span><span class="w"> </span><span class="s1">'~> 3.0'</span><span class="p">,</span><span class="w"> </span><span class="s1">'>= 3.26.0'</span>
|
||
<span class="n">s</span><span class="o">.</span><span class="n">add_development_dependency</span><span class="w"> </span><span class="s1">'stringex'</span><span class="p">,</span><span class="w"> </span><span class="s1">'~> 1.5.1'</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The purpose of development dependencies is only to declare an implicit group,
|
||
as part of the <code class="docutils literal notranslate"><span class="pre">.gemspec</span></code>, which can then be used by <code class="docutils literal notranslate"><span class="pre">bundler</span></code>.</p>
|
||
<p>For full details, see the <code class="docutils literal notranslate"><span class="pre">gemspec</span></code> directive in <code class="docutils literal notranslate"><span class="pre">bundler</span></code>'s
|
||
<a class="reference external" href="https://bundler.io/v1.12/man/gemfile.5.html#GEMSPEC-gemspec-">documentation on Gemfiles</a>.
|
||
However, the integration between <code class="docutils literal notranslate"><span class="pre">.gemspec</span></code> development dependencies and
|
||
<code class="docutils literal notranslate"><span class="pre">Gemfile</span></code>/<code class="docutils literal notranslate"><span class="pre">bundle</span></code> usage is best understood via an example.</p>
|
||
<section id="gemspec-development-dependency-example">
|
||
<h5><a class="toc-backref" href="#gemspec-development-dependency-example" role="doc-backlink">gemspec development dependency example</a></h5>
|
||
<p>Consider the following simple project in the form of a <code class="docutils literal notranslate"><span class="pre">Gemfile</span></code> and <code class="docutils literal notranslate"><span class="pre">.gemspec</span></code>.
|
||
The <code class="docutils literal notranslate"><span class="pre">cool-gem.gemspec</span></code> file:</p>
|
||
<div class="highlight-ruby notranslate"><div class="highlight"><pre><span></span><span class="no">Gem</span><span class="o">::</span><span class="no">Specification</span><span class="o">.</span><span class="n">new</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="o">|</span><span class="n">s</span><span class="o">|</span>
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">author</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'Stephen Rosen'</span>
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'cool-gem'</span>
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">version</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'0.0.1'</span>
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">summary</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'A very cool gem that does cool stuff'</span>
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">license</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'MIT'</span>
|
||
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">files</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">[]</span>
|
||
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">add_dependency</span><span class="w"> </span><span class="s1">'rails'</span>
|
||
<span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">add_development_dependency</span><span class="w"> </span><span class="s1">'kramdown'</span>
|
||
<span class="k">end</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>and the <code class="docutils literal notranslate"><span class="pre">Gemfile</span></code>:</p>
|
||
<div class="highlight-ruby notranslate"><div class="highlight"><pre><span></span><span class="n">source</span><span class="w"> </span><span class="s1">'https://rubygems.org'</span>
|
||
|
||
<span class="n">gemspec</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">gemspec</span></code> directive in <code class="docutils literal notranslate"><span class="pre">Gemfile</span></code> declares a dependency on the local
|
||
package, <code class="docutils literal notranslate"><span class="pre">cool-gem</span></code>, defined in the locally available <code class="docutils literal notranslate"><span class="pre">cool-gem.gemspec</span></code>
|
||
file. It <em>also</em> implicitly adds all development dependencies to a dependency
|
||
group named <code class="docutils literal notranslate"><span class="pre">development</span></code>.</p>
|
||
<p>Therefore, in this case, the <code class="docutils literal notranslate"><span class="pre">gemspec</span></code> directive is equivalent to the
|
||
following <code class="docutils literal notranslate"><span class="pre">Gemfile</span></code> content:</p>
|
||
<div class="highlight-ruby notranslate"><div class="highlight"><pre><span></span><span class="n">gem</span><span class="w"> </span><span class="s1">'cool-gem'</span><span class="p">,</span><span class="w"> </span><span class="ss">:path</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="s1">'.'</span>
|
||
|
||
<span class="n">group</span><span class="w"> </span><span class="ss">:development</span><span class="w"> </span><span class="k">do</span>
|
||
<span class="w"> </span><span class="n">gem</span><span class="w"> </span><span class="s1">'kramdown'</span>
|
||
<span class="k">end</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
</section>
|
||
</section>
|
||
<section id="appendix-b-prior-art-in-python">
|
||
<span id="python-prior-art"></span><h2><a class="toc-backref" href="#appendix-b-prior-art-in-python" role="doc-backlink">Appendix B: Prior Art in Python</a></h2>
|
||
<p>In the absence of any prior standard for Dependency Groups, two known workflow
|
||
tools, PDM and Poetry, have defined their own solutions.</p>
|
||
<p>This section will primarily focus on these two tools as cases of prior art
|
||
regarding the definition and use of Dependency Groups in Python.</p>
|
||
<section id="projects-are-packages">
|
||
<h3><a class="toc-backref" href="#projects-are-packages" role="doc-backlink">Projects are Packages</a></h3>
|
||
<p>Both PDM and Poetry treat the projects they support as packages.
|
||
This allows them to use and interact with standard <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> metadata
|
||
for some of their needs, and allows them to support installation of the
|
||
“current project” by doing a build and install using their build backends.</p>
|
||
<p>Effectively, this means that neither Poetry nor PDM supports non-package projects.</p>
|
||
</section>
|
||
<section id="non-standard-dependency-specifiers">
|
||
<h3><a class="toc-backref" href="#non-standard-dependency-specifiers" role="doc-backlink">Non-Standard Dependency Specifiers</a></h3>
|
||
<p>PDM and Poetry extend <a class="pep reference internal" href="../pep-0508/" title="PEP 508 – Dependency specification for Python Software Packages">PEP 508</a> dependency specifiers with additional features
|
||
which are not part of any shared standard.
|
||
The two tools use slightly different approaches to these problems, however.</p>
|
||
<p>PDM supports specifying local paths, and editable installs, via a syntax which
|
||
looks like a set of arguments to <code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span></code>. For example, the following
|
||
dependency group includes a local package in editable mode:</p>
|
||
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="k">[tool.pdm.dev-dependencies]</span>
|
||
<span class="n">mygroup</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"-e file:///${PROJECT_ROOT}/foo"</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This declares a dependency group <code class="docutils literal notranslate"><span class="pre">mygroup</span></code> which includes a local editable
|
||
install from the <code class="docutils literal notranslate"><span class="pre">foo</span></code> directory.</p>
|
||
<p>Poetry describes dependency groups as tables, mapping package names to
|
||
specifiers. For example, the same configuration as the above <code class="docutils literal notranslate"><span class="pre">mygroup</span></code>
|
||
example might appear as follows under Poetry:</p>
|
||
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="k">[tool.poetry.group.mygroup]</span>
|
||
<span class="n">foo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s2">"foo"</span><span class="p">,</span><span class="w"> </span><span class="n">editable</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span><span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>PDM restricts itself to a string syntax, and Poetry introduces tables which
|
||
describe dependencies.</p>
|
||
</section>
|
||
<section id="installing-and-referring-to-dependency-groups">
|
||
<h3><a class="toc-backref" href="#installing-and-referring-to-dependency-groups" role="doc-backlink">Installing and Referring to Dependency Groups</a></h3>
|
||
<p>Both PDM and Poetry have tool-specific support for installing dependency
|
||
groups. Because both projects support their own lockfile formats, they also
|
||
both have the capability to transparently use a dependency group name to refer
|
||
to the <em>locked</em> dependency data for that group.</p>
|
||
<p>However, neither tool’s dependency groups can be referenced natively from other
|
||
tools like <code class="docutils literal notranslate"><span class="pre">tox</span></code>, <code class="docutils literal notranslate"><span class="pre">nox</span></code>, or <code class="docutils literal notranslate"><span class="pre">pip</span></code>.
|
||
Attempting to install a dependency group under <code class="docutils literal notranslate"><span class="pre">tox</span></code>, for example, requires
|
||
an explicit call to PDM or Poetry to parse their dependency data and do the
|
||
relevant installation step.</p>
|
||
</section>
|
||
</section>
|
||
<section id="appendix-c-use-cases">
|
||
<span id="id1"></span><h2><a class="toc-backref" href="#appendix-c-use-cases" role="doc-backlink">Appendix C: Use Cases</a></h2>
|
||
<section id="web-applications">
|
||
<h3><a class="toc-backref" href="#web-applications" role="doc-backlink">Web Applications</a></h3>
|
||
<p>A web application (e.g. a Django or Flask app) often does not need to build a
|
||
distribution, but bundles and ships its source to a deployment toolchain.</p>
|
||
<p>For example, a source code repository may define Python packaging metadata as
|
||
well as containerization or other build pipeline metadata (<code class="docutils literal notranslate"><span class="pre">Dockerfile</span></code>,
|
||
etc).
|
||
The Python application is built by copying the entire repository into a
|
||
build context, installing dependencies, and bundling the result as a machine
|
||
image or container.</p>
|
||
<p>Such applications have dependency groups for the build, but also for linting,
|
||
testing, etc. In practice, today, these applications often define themselves as
|
||
packages to be able to use packaging tools and mechanisms like <code class="docutils literal notranslate"><span class="pre">extras</span></code> to
|
||
manage their dependency groups. However, they are not conceptually packages,
|
||
meant for distribution in sdist or wheel format.</p>
|
||
<p>Dependency Groups allow these applications to define their various dependencies
|
||
without relying on packaging metadata, and without trying to express their
|
||
needs in packaging terms.</p>
|
||
</section>
|
||
<section id="libraries">
|
||
<h3><a class="toc-backref" href="#libraries" role="doc-backlink">Libraries</a></h3>
|
||
<p>Libraries are Python packages which build distributions (sdist and wheel) and
|
||
publish them to PyPI.</p>
|
||
<p>For libraries, Dependency Groups represent an alternative to <code class="docutils literal notranslate"><span class="pre">extras</span></code> for
|
||
defining groups of development dependencies, with the important advantages
|
||
noted above.</p>
|
||
<p>A library may define groups for <code class="docutils literal notranslate"><span class="pre">test</span></code> and <code class="docutils literal notranslate"><span class="pre">typing</span></code> which allow testing and
|
||
type-checking, and therefore rely on the library’s own dependencies (as
|
||
specified in <code class="docutils literal notranslate"><span class="pre">[project.dependencies]</span></code>).</p>
|
||
<p>Other development needs may not require installation of the package at all. For
|
||
example, a <code class="docutils literal notranslate"><span class="pre">lint</span></code> Dependency Group may be valid and faster to install without
|
||
the library, as it only installs tools like <code class="docutils literal notranslate"><span class="pre">black</span></code>, <code class="docutils literal notranslate"><span class="pre">ruff</span></code>, or <code class="docutils literal notranslate"><span class="pre">flake8</span></code>.</p>
|
||
<p><code class="docutils literal notranslate"><span class="pre">lint</span></code> and <code class="docutils literal notranslate"><span class="pre">test</span></code> environments may also be valuable locations to hook in
|
||
IDE or editor support. See the case below for a fuller description of such
|
||
usage.</p>
|
||
<p>Here’s an example Dependency Groups table which might be suitable for a
|
||
library:</p>
|
||
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="k">[dependency-groups]</span>
|
||
<span class="n">test</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"pytest<8"</span><span class="p">,</span><span class="w"> </span><span class="s2">"coverage"</span><span class="p">]</span>
|
||
<span class="n">typing</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"mypy==1.7.1"</span><span class="p">,</span><span class="w"> </span><span class="s2">"types-requests"</span><span class="p">]</span>
|
||
<span class="n">lint</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"black"</span><span class="p">,</span><span class="w"> </span><span class="s2">"flake8"</span><span class="p">]</span>
|
||
<span class="n">typing-test</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[{</span><span class="n">include-group</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s2">"typing"</span><span class="p">},</span><span class="w"> </span><span class="s2">"pytest<8"</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that none of these implicitly install the library itself.
|
||
It is therefore the responsibility of any environment management toolchain to
|
||
install the appropriate Dependency Groups along with the library when needed,
|
||
as in the case of <code class="docutils literal notranslate"><span class="pre">test</span></code>.</p>
|
||
</section>
|
||
<section id="data-science-projects">
|
||
<h3><a class="toc-backref" href="#data-science-projects" role="doc-backlink">Data Science Projects</a></h3>
|
||
<p>Data Science Projects typically take the form of a logical collection of
|
||
scripts and utilities for processing and analyzing data, using a common
|
||
toolchain. Components may be defined in the Jupyter Notebook format (ipynb),
|
||
but rely on the same common core set of utilities.</p>
|
||
<p>In such a project, there is no package to build or install. Therefore,
|
||
<code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> currently does not offer any solution for dependency
|
||
management or declaration.</p>
|
||
<p>It is valuable for such a project to be able to define at least one major
|
||
grouping of dependencies. For example:</p>
|
||
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="k">[dependency-groups]</span>
|
||
<span class="n">main</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"numpy"</span><span class="p">,</span><span class="w"> </span><span class="s2">"pandas"</span><span class="p">,</span><span class="w"> </span><span class="s2">"matplotlib"</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>However, it may also be necessary for various scripts to have additional
|
||
supporting tools. Projects may even have conflicting or incompatible tools or
|
||
tool versions for different components, as they evolve over time.</p>
|
||
<p>Consider the following more elaborate configuration:</p>
|
||
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="k">[dependency-groups]</span>
|
||
<span class="n">main</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"numpy"</span><span class="p">,</span><span class="w"> </span><span class="s2">"pandas"</span><span class="p">,</span><span class="w"> </span><span class="s2">"matplotlib"</span><span class="p">]</span>
|
||
<span class="n">scikit</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[{</span><span class="n">include-group</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s2">"main"</span><span class="p">},</span><span class="w"> </span><span class="s2">"scikit-learn==1.3.2"</span><span class="p">]</span>
|
||
<span class="n">scikit-old</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[{</span><span class="n">include-group</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s2">"main"</span><span class="p">},</span><span class="w"> </span><span class="s2">"scikit-learn==0.24.2"</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This defines <code class="docutils literal notranslate"><span class="pre">scikit</span></code> and <code class="docutils literal notranslate"><span class="pre">scikit-old</span></code> as two similar variants of the
|
||
common suite of dependencies, pulling in different versions of <code class="docutils literal notranslate"><span class="pre">scikit-learn</span></code>
|
||
to suit different scripts.</p>
|
||
<p>This PEP only defines these data. It does not formalize any mechanism for a
|
||
Data Science Project (or any other type of project) to install the dependencies
|
||
into known environments or associate those environments with the various
|
||
scripts. Such combinations of data are left as a problem for tool authors to
|
||
solve, and perhaps eventually standardize.</p>
|
||
</section>
|
||
<section id="lockfile-generation">
|
||
<h3><a class="toc-backref" href="#lockfile-generation" role="doc-backlink">Lockfile Generation</a></h3>
|
||
<p>There are a number of tools which generate lockfiles in the Python ecosystem
|
||
today. PDM and Poetry each use their own lockfile formats, and pip-tools
|
||
generates <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files with version pins and hashes.</p>
|
||
<p>Dependency Groups are not an appropriate place to store lockfiles, as they lack
|
||
many of the necessary features. Most notably, they cannot store hashes, which
|
||
most lockfile users consider essential.</p>
|
||
<p>However, Dependency Groups are a valid input to tools which generate lockfiles.
|
||
Furthermore, PDM and Poetry both allow a Dependency Group name (under their
|
||
notions of Dependency Groups) to be used to refer to its locked variant.</p>
|
||
<p>Therefore, consider a tool which produces lockfiles, here called <code class="docutils literal notranslate"><span class="pre">$TOOL</span></code>.
|
||
It might be used as follows:</p>
|
||
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span><span class="nv">$TOOL</span><span class="w"> </span>lock<span class="w"> </span>--dependency-group<span class="o">=</span><span class="nb">test</span>
|
||
<span class="nv">$TOOL</span><span class="w"> </span>install<span class="w"> </span>--dependency-group<span class="o">=</span><span class="nb">test</span><span class="w"> </span>--use-locked
|
||
</pre></div>
|
||
</div>
|
||
<p>All that such a tool needs to do is to ensure that its lockfile data records
|
||
the name <code class="docutils literal notranslate"><span class="pre">test</span></code> in order to support such usage.</p>
|
||
<p>The mutual compatibility of Dependency Groups is not guaranteed. For example,
|
||
the Data Science example above shows conflicting versions of <code class="docutils literal notranslate"><span class="pre">scikit-learn</span></code>.
|
||
Therefore, installing multiple locked dependency groups in tandem may require
|
||
that tools apply additional constraints or generate additional lockfile data.
|
||
These problems are considered out of scope for this PEP.</p>
|
||
<p>As two examples of how combinations might be locked:</p>
|
||
<ul class="simple">
|
||
<li>A tool might require that lockfile data be explicitly generated for any
|
||
combination to be considered valid</li>
|
||
<li>Poetry implements the requirement that all Dependency Groups be mutually
|
||
compatible, and generates only one locked version. (Meaning it finds a single
|
||
solution, rather than a set or matrix of solutions.)</li>
|
||
</ul>
|
||
</section>
|
||
<section id="environment-manager-inputs">
|
||
<h3><a class="toc-backref" href="#environment-manager-inputs" role="doc-backlink">Environment Manager Inputs</a></h3>
|
||
<p>A common usage in tox, Nox, and Hatch is to install a set of dependencies into
|
||
a testing environment.</p>
|
||
<p>For example, under <code class="docutils literal notranslate"><span class="pre">tox.ini</span></code>, type checking dependencies may be defined
|
||
inline:</p>
|
||
<div class="highlight-ini notranslate"><div class="highlight"><pre><span></span><span class="k">[testenv:typing]</span>
|
||
<span class="na">deps</span><span class="w"> </span><span class="o">=</span>
|
||
<span class="w"> </span><span class="na">pyright</span>
|
||
<span class="w"> </span><span class="na">useful-types</span>
|
||
<span class="na">commands</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">pyright src/</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This combination provides a desirable developer experience within a limited
|
||
context. Under the relevant environment manager, the dependencies which are
|
||
needed for the test environment are declared alongside the commands which need
|
||
those dependencies. They are not published in package metadata, as <code class="docutils literal notranslate"><span class="pre">extras</span></code>
|
||
would be, and they are discoverable for the tool which needs them to build the
|
||
relevant environment.</p>
|
||
<p>Dependency Groups apply to such usages by effectively “lifting” these
|
||
requirements data from a tool-specific location into a more broadly available
|
||
one. In the example above, only <code class="docutils literal notranslate"><span class="pre">tox</span></code> has access to the declared list of
|
||
dependencies. Under an implementation supporting dependency groups, the same
|
||
data might be available in a Dependency Group:</p>
|
||
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="k">[dependency-groups]</span>
|
||
<span class="n">typing</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"pyright"</span><span class="p">,</span><span class="w"> </span><span class="s2">"useful-types"</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The data can then be used under multiple tools. For example, <code class="docutils literal notranslate"><span class="pre">tox</span></code> might
|
||
implement support as <code class="docutils literal notranslate"><span class="pre">dependency_groups</span> <span class="pre">=</span> <span class="pre">typing</span></code>, replacing the <code class="docutils literal notranslate"><span class="pre">deps</span></code>
|
||
usage above.</p>
|
||
<p>In order for Dependency Groups to be a viable alternative for users of
|
||
environment managers, the environment managers will need to support processing
|
||
Dependency Groups similarly to how they support inline dependency declaration.</p>
|
||
</section>
|
||
<section id="ide-and-editor-use-of-requirements-data">
|
||
<h3><a class="toc-backref" href="#ide-and-editor-use-of-requirements-data" role="doc-backlink">IDE and Editor Use of Requirements Data</a></h3>
|
||
<p>IDE and editor integrations may benefit from conventional or configurable name
|
||
definitions for Dependency Groups which are used for integrations.</p>
|
||
<p>There are at least two known scenarios in which it is valuable for an editor or
|
||
IDE to be capable of discovering the non-published dependencies of a project:</p>
|
||
<ul class="simple">
|
||
<li>testing: IDEs such as VS Code support GUI interfaces for running particular
|
||
tests</li>
|
||
<li>linting: editors and IDEs often support linting and autoformatting
|
||
integrations which highlight or autocorrect errors</li>
|
||
</ul>
|
||
<p>These cases could be handled by defining conventional group names like
|
||
<code class="docutils literal notranslate"><span class="pre">test</span></code>, <code class="docutils literal notranslate"><span class="pre">lint</span></code>, and <code class="docutils literal notranslate"><span class="pre">fix</span></code>, or by defining configuration mechanisms which
|
||
allow the selection of Dependency Groups.</p>
|
||
<p>For example, the following <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> declares the three aforementioned
|
||
groups:</p>
|
||
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="k">[dependency-groups]</span>
|
||
<span class="n">test</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"pytest"</span><span class="p">,</span><span class="w"> </span><span class="s2">"pytest-timeout"</span><span class="p">]</span>
|
||
<span class="n">lint</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"flake8"</span><span class="p">,</span><span class="w"> </span><span class="s2">"mypy"</span><span class="p">]</span>
|
||
<span class="n">fix</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"black"</span><span class="p">,</span><span class="w"> </span><span class="s2">"isort"</span><span class="p">,</span><span class="w"> </span><span class="s2">"pyupgrade"</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This PEP makes no attempt to standardize such names or reserve them for such
|
||
uses. IDEs may standardize or may allow users to configure the group names used
|
||
for various purposes.</p>
|
||
<p>This declaration allows the project author’s knowledge of the appropriate tools
|
||
for the project to be shared with all editors of that project.</p>
|
||
</section>
|
||
</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-0735.rst">https://github.com/python/peps/blob/main/peps/pep-0735.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0735.rst">2024-10-16 16:05:18 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><ul>
|
||
<li><a class="reference internal" href="#limitations-of-requirements-txt-files">Limitations of <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> files</a></li>
|
||
<li><a class="reference internal" href="#limitations-of-extras">Limitations of <code class="docutils literal notranslate"><span class="pre">extras</span></code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rationale">Rationale</a><ul>
|
||
<li><a class="reference internal" href="#use-cases">Use Cases</a></li>
|
||
<li><a class="reference internal" href="#regarding-poetry-and-pdm-dependency-groups">Regarding Poetry and PDM Dependency Groups</a></li>
|
||
<li><a class="reference internal" href="#dependency-groups-are-not-hidden-extras">Dependency Groups are not Hidden Extras</a></li>
|
||
<li><a class="reference internal" href="#future-compatibility-invalid-data">Future Compatibility & Invalid Data</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#specification">Specification</a><ul>
|
||
<li><a class="reference internal" href="#dependency-object-specifiers">Dependency Object Specifiers</a><ul>
|
||
<li><a class="reference internal" href="#dependency-group-include">Dependency Group Include</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#example-dependency-groups-table">Example Dependency Groups Table</a></li>
|
||
<li><a class="reference internal" href="#package-building">Package Building</a></li>
|
||
<li><a class="reference internal" href="#installing-dependency-groups">Installing Dependency Groups</a><ul>
|
||
<li><a class="reference internal" href="#overlapping-install-ux-with-extras">Overlapping Install UX with Extras</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#validation-and-compatibility">Validation and Compatibility</a><ul>
|
||
<li><a class="reference internal" href="#linters-and-validators-may-be-stricter">Linters and Validators may be stricter</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
|
||
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a><ul>
|
||
<li><a class="reference internal" href="#audit-and-update-tools">Audit and Update Tools</a></li>
|
||
</ul>
|
||
</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><ul>
|
||
<li><a class="reference internal" href="#interfaces-for-use-of-dependency-groups">Interfaces for Use of Dependency Groups</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
|
||
<li><a class="reference internal" href="#why-not-define-each-dependency-group-as-a-table">Why not define each Dependency Group as a table?</a></li>
|
||
<li><a class="reference internal" href="#why-not-define-a-special-string-syntax-to-extend-dependency-specifiers">Why not define a special string syntax to extend Dependency Specifiers?</a></li>
|
||
<li><a class="reference internal" href="#why-not-allow-for-more-non-pep-508-dependency-specifiers">Why not allow for more non-PEP 508 dependency specifiers?</a></li>
|
||
<li><a class="reference internal" href="#why-is-the-table-not-named-run-project-dependency-groups">Why is the table not named <code class="docutils literal notranslate"><span class="pre">[run]</span></code>, <code class="docutils literal notranslate"><span class="pre">[project.dependency-groups]</span></code>, …?</a></li>
|
||
<li><a class="reference internal" href="#why-is-pip-s-planned-implementation-of-only-deps-not-sufficient">Why is pip’s planned implementation of <code class="docutils literal notranslate"><span class="pre">--only-deps</span></code> not sufficient?</a></li>
|
||
<li><a class="reference internal" href="#why-isn-t-environment-manager-a-solution">Why isn’t <environment manager> a solution?</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#deferred-ideas">Deferred Ideas</a><ul>
|
||
<li><a class="reference internal" href="#why-not-support-dependency-group-includes-in-project-dependencies-or-project-optional-dependencies">Why not support Dependency Group Includes in <code class="docutils literal notranslate"><span class="pre">[project.dependencies]</span></code> or <code class="docutils literal notranslate"><span class="pre">[project.optional-dependencies]</span></code>?</a><ul>
|
||
<li><a class="reference internal" href="#use-cases-for-dependency-group-includes-from-project">Use Cases for Dependency Group Includes From <code class="docutils literal notranslate"><span class="pre">[project]</span></code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#why-not-support-dependency-group-includes-in-build-system-requires">Why not support Dependency Group Includes in <code class="docutils literal notranslate"><span class="pre">[build-system.requires]</span></code>?</a></li>
|
||
<li><a class="reference internal" href="#why-not-support-a-dependency-group-which-includes-the-current-project">Why not support a Dependency Group which includes the current project?</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#appendix-a-prior-art-in-non-python-languages">Appendix A: Prior Art in Non-Python Languages</a><ul>
|
||
<li><a class="reference internal" href="#javascript-and-package-json">JavaScript and <code class="docutils literal notranslate"><span class="pre">package.json</span></code></a><ul>
|
||
<li><a class="reference internal" href="#dependencies-data"><code class="docutils literal notranslate"><span class="pre">"dependencies"</span></code> data</a></li>
|
||
<li><a class="reference internal" href="#dependencies-referencing-urls-and-local-paths">Dependencies Referencing URLs and Local Paths</a></li>
|
||
<li><a class="reference internal" href="#devdependencies-data"><code class="docutils literal notranslate"><span class="pre">"devDependencies"</span></code> data</a></li>
|
||
<li><a class="reference internal" href="#peerdependencies-and-optionaldependencies"><code class="docutils literal notranslate"><span class="pre">"peerDependencies"</span></code> and <code class="docutils literal notranslate"><span class="pre">"optionalDependencies"</span></code></a><ul>
|
||
<li><a class="reference internal" href="#peerdependenciesmeta"><code class="docutils literal notranslate"><span class="pre">"peerDependenciesMeta"</span></code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#omit-and-include"><code class="docutils literal notranslate"><span class="pre">--omit</span></code> and <code class="docutils literal notranslate"><span class="pre">--include</span></code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#ruby-ruby-gems">Ruby & Ruby Gems</a><ul>
|
||
<li><a class="reference internal" href="#gemfiles-bundle">Gemfiles & bundle</a></li>
|
||
<li><a class="reference internal" href="#gemspec-and-packaged-dependency-data">gemspec and packaged dependency data</a><ul>
|
||
<li><a class="reference internal" href="#gemspec-development-dependency-example">gemspec development dependency example</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#appendix-b-prior-art-in-python">Appendix B: Prior Art in Python</a><ul>
|
||
<li><a class="reference internal" href="#projects-are-packages">Projects are Packages</a></li>
|
||
<li><a class="reference internal" href="#non-standard-dependency-specifiers">Non-Standard Dependency Specifiers</a></li>
|
||
<li><a class="reference internal" href="#installing-and-referring-to-dependency-groups">Installing and Referring to Dependency Groups</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#appendix-c-use-cases">Appendix C: Use Cases</a><ul>
|
||
<li><a class="reference internal" href="#web-applications">Web Applications</a></li>
|
||
<li><a class="reference internal" href="#libraries">Libraries</a></li>
|
||
<li><a class="reference internal" href="#data-science-projects">Data Science Projects</a></li>
|
||
<li><a class="reference internal" href="#lockfile-generation">Lockfile Generation</a></li>
|
||
<li><a class="reference internal" href="#environment-manager-inputs">Environment Manager Inputs</a></li>
|
||
<li><a class="reference internal" href="#ide-and-editor-use-of-requirements-data">IDE and Editor Use of Requirements Data</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
|
||
<br>
|
||
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0735.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> |