peps/pep-0650/index.html

806 lines
57 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!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 650 Specifying Installer Requirements for Python Projects | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0650/">
<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 650 Specifying Installer Requirements for Python Projects | peps.python.org'>
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0650/">
<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="Python Enhancement Proposals (PEPs)">
<meta name="theme-color" content="#3776ab">
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
<title>Following system colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"></circle>
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
<title>Selected dark colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
<title>Selected light colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
</svg>
<script>
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
</script>
<section id="pep-page-section">
<header>
<h1>Python Enhancement Proposals</h1>
<ul class="breadcrumbs">
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li>
<li><a href="../pep-0000/">PEP Index</a> &raquo; </li>
<li>PEP 650</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 650 Specifying Installer Requirements for Python Projects</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Vikram Jayanthi &lt;vikramjayanthi&#32;&#97;t&#32;google.com&gt;,
Dustin Ingram &lt;di&#32;&#97;t&#32;python.org&gt;,
Brett Cannon &lt;brett&#32;&#97;t&#32;python.org&gt;</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/pep-650-specifying-installer-requirements-for-python-projects/6657">Discourse thread</a></dd>
<dt class="field-odd">Status<span class="colon">:</span></dt>
<dd class="field-odd"><abbr title="Removed from consideration by sponsor or authors">Withdrawn</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">16-Jul-2020</dd>
<dt class="field-odd">Post-History<span class="colon">:</span></dt>
<dd class="field-odd">14-Jan-2021</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="#terminology">Terminology</a></li>
<li><a class="reference internal" href="#motivation">Motivation</a><ul>
<li><a class="reference internal" href="#providers">Providers</a><ul>
<li><a class="reference internal" href="#platform-infrastructure-providers">Platform/Infrastructure Providers</a></li>
<li><a class="reference internal" href="#ide-providers">IDE Providers</a></li>
</ul>
</li>
<li><a class="reference internal" href="#developers">Developers</a><ul>
<li><a class="reference internal" href="#developers-using-paas-iaas-providers">Developers using PaaS &amp; IaaS providers</a></li>
<li><a class="reference internal" href="#developers-using-ides">Developers using IDEs</a></li>
<li><a class="reference internal" href="#developers-working-with-other-developers">Developers working with other developers</a></li>
</ul>
</li>
<li><a class="reference internal" href="#upgraders-package-infrastructure-providers">Upgraders &amp; Package Infrastructure Providers</a></li>
<li><a class="reference internal" href="#open-source-community">Open Source Community</a></li>
</ul>
</li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#install-system">[install-system]</a><ul>
<li><a class="reference internal" href="#installer-requirements">Installer Requirements:</a></li>
<li><a class="reference internal" href="#additional-parameters-or-tool-specific-data">Additional parameters or tool specific data</a></li>
</ul>
</li>
<li><a class="reference internal" href="#installer-interface">Installer interface:</a></li>
<li><a class="reference internal" href="#mandatory-hooks">Mandatory hooks:</a><ul>
<li><a class="reference internal" href="#invoke-install">invoke_install</a></li>
</ul>
</li>
<li><a class="reference internal" href="#optional-hooks">Optional hooks:</a><ul>
<li><a class="reference internal" href="#invoke-uninstall">invoke_uninstall</a></li>
<li><a class="reference internal" href="#get-dependencies-to-install">get_dependencies_to_install</a></li>
<li><a class="reference internal" href="#get-dependency-groups">get_dependency_groups</a></li>
<li><a class="reference internal" href="#update-dependencies">update_dependencies</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#example">Example</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li>
<li><a class="reference internal" href="#security-implications">Security Implications</a></li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
<li><a class="reference internal" href="#a-standardized-lock-file">A standardized lock file</a></li>
<li><a class="reference internal" href="#have-installer-backends-support-creating-virtual-environments">Have installer backends support creating virtual environments</a></li>
</ul>
</li>
<li><a class="reference internal" href="#open-issues">Open Issues</a><ul>
<li><a class="reference internal" href="#should-the-dependency-group-argument-take-an-iterable">Should the <code class="docutils literal notranslate"><span class="pre">dependency_group</span></code> argument take an iterable?</a></li>
<li><a class="reference internal" href="#is-the-installer-backend-executed-in-process">Is the installer backend executed in-process?</a></li>
<li><a class="reference internal" href="#enforce-that-results-from-the-proposed-interface-feed-into-other-parts">Enforce that results from the proposed interface feed into other parts?</a></li>
<li><a class="reference internal" href="#raising-exceptions-instead-of-exit-codes-for-failure-conditions">Raising exceptions instead of exit codes for failure conditions</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>Python package installers are not completely interoperable with each
other. While pip is the most widely used installer and a de facto
standard, other installers such as <a class="reference external" href="https://python-poetry.org/">Poetry</a> or <a class="reference external" href="https://pipenv-fork.readthedocs.io/en/latest/">Pipenv</a> are popular as
well due to offering unique features which are optimal for certain
workflows and not directly in line with how pip operates.</p>
<p>While the abundance of installer options is good for end-users with
specific needs, the lack of interoperability between them makes it
hard to support all potential installers. Specifically, the lack of a
standard requirements file for declaring dependencies means that each
tool must be explicitly used in order to install dependencies
specified with their respective format. Otherwise tools must emit a
requirements file which leads to potential information loss for the
installer as well as an added export step as part of a developers
workflow.</p>
<p>By providing a standardized API that can be used to invoke a
compatible installer, we can solve this problem without needing to
resolve individual concerns, unique requirements, and
incompatibilities between different installers and their lock files.</p>
<p>Installers that implement the specification can be invoked in a
uniform way, allowing users to use their installer of choice as if
they were invoking it directly.</p>
</section>
<section id="terminology">
<h2><a class="toc-backref" href="#terminology" role="doc-backlink">Terminology</a></h2>
<dl class="simple">
<dt>Installer interface</dt><dd>The interface by which an <em>installer backend</em> and a
<em>universal installer</em> interact.</dd>
<dt>Universal installer</dt><dd>An installer that can invoke an <em>installer backend</em> by calling the
optional invocation methods of the <em>installer interface</em>. This can
also be thought of as the installer frontend, à la the <a class="reference external" href="https://github.com/pypa/build">build</a>
project for <a class="pep reference internal" href="../pep-0517/" title="PEP 517 A build-system independent format for source trees">PEP 517</a>.</dd>
<dt>Installer backend</dt><dd>An installer that implements the <em>installer interface</em>, allowing
it to be invoked by a <em>universal installer</em>. An
<em>installer backend</em> may also be a <em>universal installer</em> as well,
but it is not required. In comparison to <a class="pep reference internal" href="../pep-0517/" title="PEP 517 A build-system independent format for source trees">PEP 517</a>, this would
be <a class="reference external" href="https://flit.readthedocs.io">Flit</a>. <em>Installer backends</em> may be wrapper packages around
a backing installer, e.g. Poetry could choose to not support this
API, but a package could act as a wrapper to invoke Poetry as
appropriate to use Poetry to perform an installation.</dd>
<dt>Dependency group</dt><dd>A set of dependencies that are related and required to be
installed simultaneously for some purpose. For example, a
“test” dependency group could include the dependencies required to
run the test suite. How dependency groups are specified is up to
the <em>installer backend</em>.</dd>
</dl>
</section>
<section id="motivation">
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
<p>This specification allows anyone to invoke and interact with
<em>installer backends</em> that implement the specified interface, allowing
for a universally supported layer on top of existing tool-specific
installation processes.</p>
<p>This in turn would enable the use of all installers that implement the
specified interface to be used in environments that support a single
<em>universal installer</em>, as long as that installer implements this
specification as well.</p>
<p>Below, we identify various use-cases applicable to stakeholders in the
Python community and anyone who interacts with Python package
installers. For developers or companies, this PEP would allow for
increased functionality and flexibility with Python package
installers.</p>
<section id="providers">
<h3><a class="toc-backref" href="#providers" role="doc-backlink">Providers</a></h3>
<p>Providers are the parties (organization, person, community, etc.) that
supply a service or software tool which interacts with Python
packaging and consequently Python package installers. Two different
types of providers are considered:</p>
<section id="platform-infrastructure-providers">
<h4><a class="toc-backref" href="#platform-infrastructure-providers" role="doc-backlink">Platform/Infrastructure Providers</a></h4>
<p>Platform providers (cloud environments, application hosting, etc.) and
infrastructure service providers need to support package installers
for their users to install Python dependencies. Most only support pip,
however there is user demand for other Python installers. Most
providers do not want to maintain support for more than one installer
because of the complexity it adds to their software or service and the
resources it takes to do so.</p>
<p>Via this specification, we can enable a provider-supported
<em>universal installer</em> to invoke the user-desired <em>installer backend</em>
without the providers platform needing to have specific knowledge of
said backend. What this means is if Poetry implemented the installer
backend API proposed by this PEP (or some other package wrapped Poetry
to provide the API), then platform providers would support Poetry
implicitly.</p>
</section>
<section id="ide-providers">
<h4><a class="toc-backref" href="#ide-providers" role="doc-backlink">IDE Providers</a></h4>
<p>Integrated development environments may interact with Python package
installation and management. Most only support pip as a Python package
installer, and users are required to find work arounds to install
their dependencies using other package installers. Similar to the
situation with PaaS &amp; IaaS providers, IDE providers do not want to
maintain support for N different Python installers. Instead,
implementers of the installer interface (<em>installer backends</em>) could
be invoked by the IDE by it acting as a <em>universal installer</em>.</p>
</section>
</section>
<section id="developers">
<h3><a class="toc-backref" href="#developers" role="doc-backlink">Developers</a></h3>
<p>Developers are teams, people, or communities that code and use Python
package installers and Python packages. Three different types of
developers are considered:</p>
<section id="developers-using-paas-iaas-providers">
<h4><a class="toc-backref" href="#developers-using-paas-iaas-providers" role="doc-backlink">Developers using PaaS &amp; IaaS providers</a></h4>
<p>Most PaaS and IaaS providers only support one Python package
installer: <a class="reference external" href="https://pip.pypa.io">pip</a>. (Some exceptions include Herokus Python <a class="reference external" href="https://elements.heroku.com/buildpacks/heroku/heroku-buildpack-python">buildpack</a>,
which supports pip and <a class="reference external" href="https://pipenv-fork.readthedocs.io/en/latest/">Pipenv</a>). This dictates the installers that
developers can use while working with these providers, which might not
be optimal for their application or workflow.</p>
<p>Installers adopting this PEP to become <em>installer backends</em> would allow
users to use third party platforms/infrastructure without having to
worry about which Python package installer they are required to use as
long as the provider uses a <em>universal installer</em>.</p>
</section>
<section id="developers-using-ides">
<h4><a class="toc-backref" href="#developers-using-ides" role="doc-backlink">Developers using IDEs</a></h4>
<p>Most IDEs only support pip or a few Python package installers.
Consequently, developers must use workarounds or hacky methods to
install their dependencies if they use an unsupported package
installer.</p>
<p>If the IDE uses/provides a <em>universal installer</em> it would allow for
any <em>installer backend</em> that the developer wanted to be used to
install dependencies, freeing them of any extra work to install their
dependencies in order to integrate into the IDEs workflow more
closely.</p>
</section>
<section id="developers-working-with-other-developers">
<h4><a class="toc-backref" href="#developers-working-with-other-developers" role="doc-backlink">Developers working with other developers</a></h4>
<p>Developers want to be able to use the installer of their choice while
working with other developers, but currently have to synchronize their
installer choice for compatibility of dependency installation. If all
preferred installers instead implemented the specified interface, it
would allow for cross use of installers, allowing developers to choose
an installer regardless of their collaborators preference.</p>
</section>
</section>
<section id="upgraders-package-infrastructure-providers">
<h3><a class="toc-backref" href="#upgraders-package-infrastructure-providers" role="doc-backlink">Upgraders &amp; Package Infrastructure Providers</a></h3>
<p>Package upgraders and package infrastructure in CI/CD such as
<a class="reference external" href="https://dependabot.com/">Dependabot</a>, <a class="reference external" href="https://pyup.io/">PyUP</a>, etc. currently support a few installers. They work
by parsing and editing the installer-specific dependency files
directly (such as <code class="docutils literal notranslate"><span class="pre">requirements.txt</span></code> or <code class="docutils literal notranslate"><span class="pre">poetry.lock</span></code>) with
relevant package information such as upgrades, downgrades, or new
hashes. Similar to Platform and IDE providers, most of these providers
do not want to support N different Python package installers as that
would require supporting N different file types.</p>
<p>Currently, these services/bots have to implement support for each
package installer individually. Inevitably, the most popular
installers are supported first, and less popular tools are often never
supported. By implementing this specification, these services/bots can
support any (compliant) installer, allowing users to select the tool
of their choice. This will allow for more innovation in the space, as
platforms and IDEs are no longer forced to prematurely select a
“winner”.</p>
</section>
<section id="open-source-community">
<h3><a class="toc-backref" href="#open-source-community" role="doc-backlink">Open Source Community</a></h3>
<p>Specifying installer requirements and adopting this PEP will reduce
the friction between Python package installers and peoples workflows.
Consequently, it will reduce the friction between Python package
installers and 3rd party infrastructure/technologies such as PaaS or
IDEs. Overall, it will allow for easier development, deployment and
maintenance of Python projects as Python package installation becomes
simpler and more interoperable.</p>
<p>Specifying requirements and creating an interface for installers can
also increase the pace of innovation around installers. This would
allow for installers to experiment and add unique functionality
without requiring the rest of the ecosystem to do the same. Support
becomes easier and more likely for a new installer regardless of the
functionality it adds and the format in which it writes dependencies,
while reducing the developer time and resources needed to do so.</p>
</section>
</section>
<section id="specification">
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
<p>Similar to how <a class="pep reference internal" href="../pep-0517/" title="PEP 517 A build-system independent format for source trees">PEP 517</a> specifies build systems, the install system
information will live in the <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> file under the
<code class="docutils literal notranslate"><span class="pre">install-system</span></code> table.</p>
<section id="install-system">
<h3><a class="toc-backref" href="#install-system" role="doc-backlink">[install-system]</a></h3>
<p>The install-system table is used to store install-system relevant data
and information. There are multiple required keys for this table:
<code class="docutils literal notranslate"><span class="pre">requires</span></code> and <code class="docutils literal notranslate"><span class="pre">install-backend</span></code>. The <code class="docutils literal notranslate"><span class="pre">requires</span></code> key holds the
minimum requirements for the <em>installer backend</em> to execute and which
will be installed by the <em>universal installer</em>. The <code class="docutils literal notranslate"><span class="pre">install-backend</span></code>
key holds the name of the install backends entry point. This will
allow the <em>universal installer</em> to install the requirements for the
<em>installer backend</em> itself to execute (not the requirements that the
<em>installer backend</em> itself will install) as well as invoke the
<em>installer backend</em>.</p>
<p>If either of the required keys are missing or empty then the
<em>universal installer</em> SHOULD raise an error.</p>
<p>All package names interacting with this interface are assumed to
follow <a class="pep reference internal" href="../pep-0508/" title="PEP 508 Dependency specification for Python Software Packages">PEP 508</a>s “Dependency specification for Python Software
Packages” format.</p>
<p>An example <code class="docutils literal notranslate"><span class="pre">install-system</span></code> table:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1">#pyproject.toml</span>
<span class="p">[</span><span class="n">install</span><span class="o">-</span><span class="n">system</span><span class="p">]</span>
<span class="c1">#Eg : pipenv</span>
<span class="n">requires</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;pipenv&quot;</span><span class="p">]</span>
<span class="n">install</span><span class="o">-</span><span class="n">backend</span> <span class="o">=</span> <span class="s2">&quot;pipenv.api:main&quot;</span>
</pre></div>
</div>
<section id="installer-requirements">
<h4><a class="toc-backref" href="#installer-requirements" role="doc-backlink">Installer Requirements:</a></h4>
<p>The requirements specified by the <code class="docutils literal notranslate"><span class="pre">requires</span></code> key must be within the
constraints specified by <a class="pep reference internal" href="../pep-0517/" title="PEP 517 A build-system independent format for source trees">PEP 517</a>. Specifically, that dependency
cycles are not permitted and the <em>universal installer</em> SHOULD refuse
to install the dependencies if a cycle is detected.</p>
</section>
<section id="additional-parameters-or-tool-specific-data">
<h4><a class="toc-backref" href="#additional-parameters-or-tool-specific-data" role="doc-backlink">Additional parameters or tool specific data</a></h4>
<p>Additional parameters or tool (<em>installer backend</em>) data may also be
stored in the <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> file. This would be in the “tool.*”
table as specified by <a class="pep reference internal" href="../pep-0518/" title="PEP 518 Specifying Minimum Build System Requirements for Python Projects">PEP 518</a>. For example, if the
<em>installer backend</em> is Poetry and you wanted to specify multiple
dependency groups, the tool.poetry tables could look like this:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[</span><span class="n">tool</span><span class="o">.</span><span class="n">poetry</span><span class="o">.</span><span class="n">dev</span><span class="o">-</span><span class="n">dependencies</span><span class="p">]</span>
<span class="n">dependencies</span> <span class="o">=</span> <span class="s2">&quot;dev&quot;</span>
<span class="p">[</span><span class="n">tool</span><span class="o">.</span><span class="n">poetry</span><span class="o">.</span><span class="n">deploy</span><span class="p">]</span>
<span class="n">dependencies</span> <span class="o">=</span> <span class="s2">&quot;deploy&quot;</span>
</pre></div>
</div>
<p>Data may also be stored in other ways as the installer backend sees
fit (e.g. separate configuration file).</p>
</section>
</section>
<section id="installer-interface">
<h3><a class="toc-backref" href="#installer-interface" role="doc-backlink">Installer interface:</a></h3>
<p>The <em>installer interface</em> contains mandatory and optional hooks.
Compliant <em>installer backends</em> MUST implement the mandatory hooks and
MAY implement the optional hooks. A <em>universal installer</em> MAY
implement any of the <em>installer backend</em> hooks itself, to act as both
a <em>universal installer</em> and <em>installer backend</em>, but this is not
required.</p>
<p>All hooks take <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> arbitrary parameters that a
<em>installer backend</em> may require that are not already specified,
allowing for backwards compatibility. If unexpected parameters are
passed to the <em>installer backend</em>, it should ignore them.</p>
<p>The following information is akin to the corresponding section in
<a class="pep reference internal" href="../pep-0517/" title="PEP 517 A build-system independent format for source trees">PEP 517</a>. The hooks may be called with keyword arguments, so
<em>installer backends</em> implementing them should be careful to make sure
that their signatures match both the order and the names of the
arguments above.</p>
<p>All hooks MAY print arbitrary informational text to <code class="docutils literal notranslate"><span class="pre">stdout</span></code> and
<code class="docutils literal notranslate"><span class="pre">stderr</span></code>. They MUST NOT read from <code class="docutils literal notranslate"><span class="pre">stdin</span></code>, and the
<em>universal installer</em> MAY close <code class="docutils literal notranslate"><span class="pre">stdin</span></code> before invoking the hooks.</p>
<p>The <em>universal installer</em> may capture <code class="docutils literal notranslate"><span class="pre">stdout</span></code> and/or <code class="docutils literal notranslate"><span class="pre">stderr</span></code>
from the backend. If the backend detects that an output stream is not
a terminal/console (e.g. not <code class="docutils literal notranslate"><span class="pre">sys.stdout.isatty()</span></code>), it SHOULD
ensure that any output it writes to that stream is <code class="docutils literal notranslate"><span class="pre">UTF-8</span></code> encoded.
The <em>universal installer</em> MUST NOT fail if captured output is not
valid UTF-8, but it MAY not preserve all the information in that case
(e.g. it may decode using the replace error handler in Python). If the
output stream is a terminal, the <em>installer backend</em> is responsible
for presenting its output accurately, as for any program running in a
terminal.</p>
<p>If a hook raises an exception, or causes the process to terminate,
then this indicates an error.</p>
</section>
<section id="mandatory-hooks">
<h3><a class="toc-backref" href="#mandatory-hooks" role="doc-backlink">Mandatory hooks:</a></h3>
<section id="invoke-install">
<h4><a class="toc-backref" href="#invoke-install" role="doc-backlink">invoke_install</a></h4>
<p>Installs the dependencies:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">invoke_install</span><span class="p">(</span>
<span class="n">path</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">PathLike</span><span class="p">[</span><span class="nb">str</span><span class="p">]],</span>
<span class="o">*</span><span class="p">,</span>
<span class="n">dependency_group</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="o">**</span><span class="n">kwargs</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
<span class="o">...</span>
</pre></div>
</div>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">path</span></code> : An absolute path where the <em>installer backend</em> should be
invoked from (e.g. the directory where <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> is
located).</li>
<li><code class="docutils literal notranslate"><span class="pre">dependency_group</span></code> : An optional flag specifying a dependency
group that the <em>installer backend</em> should install. The install will
error if the dependency group doesnt exist. A user can find all
dependency groups by calling
<code class="docutils literal notranslate"><span class="pre">get_dependency_groups()</span></code> if dependency groups are
supported by the <em>installer backend</em>.</li>
<li><code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> : Arbitrary parameters that a <em>installer backend</em> may
require that are not already specified, allows for backwards
compatibility.</li>
<li>Returns : An exit code (int). 0 if successful, any positive integer
if unsuccessful.</li>
</ul>
<p>The <em>universal installer</em> will use the exit code to determine if the
installation is successful and SHOULD return the exit code itself.</p>
</section>
</section>
<section id="optional-hooks">
<h3><a class="toc-backref" href="#optional-hooks" role="doc-backlink">Optional hooks:</a></h3>
<section id="invoke-uninstall">
<h4><a class="toc-backref" href="#invoke-uninstall" role="doc-backlink">invoke_uninstall</a></h4>
<p>Uninstall the specified dependencies:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">invoke_uninstall</span><span class="p">(</span>
<span class="n">path</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">PathLike</span><span class="p">[</span><span class="nb">str</span><span class="p">]],</span>
<span class="o">*</span><span class="p">,</span>
<span class="n">dependency_group</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="o">**</span><span class="n">kwargs</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
<span class="o">...</span>
</pre></div>
</div>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">path</span></code> : An absolute path where the <em>installer backend</em> should be
invoked from (e.g. the directory where <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> is
located).</li>
<li><code class="docutils literal notranslate"><span class="pre">dependency_group</span></code> : An optional flag specifying a dependency
group that the <em>installer backend</em> should uninstall.</li>
<li><code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> : Arbitrary parameters that a <em>installer backend</em> may
require that are not already specified, allows for backwards
compatibility.</li>
<li>Returns : An exit code (int). 0 if successful, any positive integer
if unsuccessful.</li>
</ul>
<p>The <em>universal installer</em> MUST invoke the <em>installer backend</em> at the
same path that the <em>universal installer</em> itself was invoked.</p>
<p>The <em>universal installer</em> will use the exit code to determine if the
uninstall is successful and SHOULD return the exit code itself.</p>
</section>
<section id="get-dependencies-to-install">
<h4><a class="toc-backref" href="#get-dependencies-to-install" role="doc-backlink">get_dependencies_to_install</a></h4>
<p>Returns the dependencies that would be installed by
<code class="docutils literal notranslate"><span class="pre">invoke_install(...)</span></code>. This allows package upgraders
(e.g., Dependabot) to retrieve the dependencies attempting to be
installed without parsing the dependency file:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">get_dependencies_to_install</span><span class="p">(</span>
<span class="n">path</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">PathLike</span><span class="p">[</span><span class="nb">str</span><span class="p">]],</span>
<span class="o">*</span><span class="p">,</span>
<span class="n">dependency_group</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="o">**</span><span class="n">kwargs</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Sequence</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="o">...</span>
</pre></div>
</div>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">path</span></code> : An absolute path where the <em>installer backend</em> should be
invoked from (e.g. the directory where <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> is
located).</li>
<li><code class="docutils literal notranslate"><span class="pre">dependency_group</span></code> : Specify a dependency group to get the
dependencies <code class="docutils literal notranslate"><span class="pre">invoke_install(...)</span></code> would install for that
dependency group.</li>
<li><code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> : Arbitrary parameters that a <em>installer backend</em> may
require that are not already specified, allows for backwards
compatibility.</li>
<li>Returns: A list of dependencies (<a class="pep reference internal" href="../pep-0508/" title="PEP 508 Dependency specification for Python Software Packages">PEP 508</a> strings) to install.</li>
</ul>
<p>If the group is specified, the <em>installer backend</em> MUST return the
dependencies corresponding to the provided dependency group. If the
specified group doesnt exist, or dependency groups are not supported
by the <em>installer backend</em>, the <em>installer backend</em> MUST raise an
error.</p>
<p>If the group is not specified, and the <em>installer backend</em> provides
the concept of a default/unspecified group, the <em>installer backend</em>
MAY return the dependencies for the default/unspecified group, but
otherwise MUST raise an error.</p>
</section>
<section id="get-dependency-groups">
<h4><a class="toc-backref" href="#get-dependency-groups" role="doc-backlink">get_dependency_groups</a></h4>
<p>Returns the dependency groups available to be installed. This allows
<em>universal installers</em> to enumerate all dependency groups the
<em>installer backend</em> is aware of:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">get_dependency_groups</span><span class="p">(</span>
<span class="n">path</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">PathLike</span><span class="p">[</span><span class="nb">str</span><span class="p">]],</span>
<span class="o">**</span><span class="n">kwargs</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">AbstractSet</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="o">...</span>
</pre></div>
</div>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">path</span></code> : An absolute path where the <em>installer backend</em> should be
invoked from (e.g. the directory where <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> is
located).</li>
<li><code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> : Arbitrary parameters that a <em>installer backend</em> may
require that are not already specified, allows for backwards
compatibility.</li>
<li>Returns: A set of known dependency groups, as strings The empty set
represents no dependency groups.</li>
</ul>
</section>
<section id="update-dependencies">
<h4><a class="toc-backref" href="#update-dependencies" role="doc-backlink">update_dependencies</a></h4>
<p>Outputs a dependency file based on inputted package list:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">update_dependencies</span><span class="p">(</span>
<span class="n">path</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">PathLike</span><span class="p">[</span><span class="nb">str</span><span class="p">]],</span>
<span class="n">dependency_specifiers</span><span class="p">:</span> <span class="n">Iterable</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span>
<span class="o">*</span><span class="p">,</span>
<span class="n">dependency_group</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="o">**</span><span class="n">kwargs</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
<span class="o">...</span>
</pre></div>
</div>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">path</span></code> : An absolute path where the <em>installer backend</em> should be
invoked from (e.g. the directory where <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> is
located).</li>
<li><code class="docutils literal notranslate"><span class="pre">dependency_specifiers</span></code> : An iterable of dependencies as
<a class="pep reference internal" href="../pep-0508/" title="PEP 508 Dependency specification for Python Software Packages">PEP 508</a> strings that are being updated, for example :
<code class="docutils literal notranslate"><span class="pre">[&quot;requests==2.8.1&quot;,</span> <span class="pre">...]</span></code>. Optionally for a specific dependency
group.</li>
<li><code class="docutils literal notranslate"><span class="pre">dependency_group</span></code> : The dependency group that the list of
packages is for.</li>
<li><code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> : Arbitrary parameters that a <em>installer backend</em> may
require that are not already specified, allows for backwards
compatibility.</li>
<li>Returns : An exit code (int). 0 if successful, any positive integer
if unsuccessful.</li>
</ul>
</section>
</section>
</section>
<section id="example">
<h2><a class="toc-backref" href="#example" role="doc-backlink">Example</a></h2>
<p>Lets consider implementing an <em>installer backend</em> that uses pip and
its requirements files for <em>dependency groups</em>. An implementation may
(very roughly) look like the following:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">subprocess</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="k">def</span> <span class="nf">invoke_install</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">dependency_group</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">run</span><span class="p">(</span>
<span class="p">[</span>
<span class="n">sys</span><span class="o">.</span><span class="n">executable</span><span class="p">,</span>
<span class="s2">&quot;-m&quot;</span><span class="p">,</span>
<span class="s2">&quot;pip&quot;</span><span class="p">,</span>
<span class="s2">&quot;install&quot;</span><span class="p">,</span>
<span class="s2">&quot;-r&quot;</span><span class="p">,</span>
<span class="n">dependency_group</span> <span class="ow">or</span> <span class="s2">&quot;requirements.txt&quot;</span><span class="p">,</span>
<span class="p">],</span>
<span class="n">cwd</span><span class="o">=</span><span class="n">path</span><span class="p">,</span>
<span class="p">)</span><span class="o">.</span><span class="n">returncode</span>
<span class="k">except</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">CalledProcessError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">return</span> <span class="n">e</span><span class="o">.</span><span class="n">returncode</span>
</pre></div>
</div>
<p>If we named this package <code class="docutils literal notranslate"><span class="pre">pep650pip</span></code>, then we could specify in
<code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[</span><span class="n">install</span><span class="o">-</span><span class="n">system</span><span class="p">]</span>
<span class="c1">#Eg : pipenv</span>
<span class="n">requires</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;pep650pip&quot;</span><span class="p">,</span> <span class="s2">&quot;pip&quot;</span><span class="p">]</span>
<span class="n">install</span><span class="o">-</span><span class="n">backend</span> <span class="o">=</span> <span class="s2">&quot;pep650pip:main&quot;</span>
</pre></div>
</div>
</section>
<section id="rationale">
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
<p>All hooks take <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> to allow for backwards compatibility and
allow for tool specific <em>installer backend</em> functionality which
requires a user to provide additional information not required by the
hook.</p>
<p>While <em>installer backends</em> must be Python packages, what they do when
invoked is an implementation detail of that tool. For example, an
<em>installer backend</em> could act as a wrapper for a platform package
manager (e.g., <code class="docutils literal notranslate"><span class="pre">apt</span></code>).</p>
<p>The interface does not in any way try to specify <em>how</em>
<em>installer backends</em> should function. This is on purpose so that
<em>installer backends</em> can be allowed to innovate and solve problem in
their own way. This also means this PEP takes no stance on OS
packaging as that would be an <em>installer backend</em>s domain.</p>
<p>Defining the API in Python does mean that <em>some</em> Python code will
eventually need to be executed. That does not preclude non-Python
<em>installer backends</em> from being used, though (e.g. <a class="reference external" href="https://github.com/mamba-org/mamba">mamba</a>), as they
could be executed as a subprocess from Python code.</p>
</section>
<section id="backwards-compatibility">
<h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2>
<p>This PEP would have no impact on pre-existing code and functionality
as it only adds new functionality to a <em>universal installer</em>. Any
existing installer should maintain its existing functionality and use
cases, therefore having no backwards compatibility issues. Only code
aiming to take advantage of this new functionality will have
motivation to make changes to their pre existing code.</p>
</section>
<section id="security-implications">
<h2><a class="toc-backref" href="#security-implications" role="doc-backlink">Security Implications</a></h2>
<p>A malicious user has no increased ability or easier access to anything
with the addition of standardized installer specifications. The
installer that could be invoked by a <em>universal installer</em> via the
interface specified in this PEP would be explicitly declared by the
user. If the user has chosen a malicious installer, then invoking it
with a <em>universal installer</em> is no different than the user invoking
the installer directly. A malicious installer being an
<em>installer backend</em> doesnt give it additional permissions or
abilities.</p>
</section>
<section id="rejected-ideas">
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2>
<section id="a-standardized-lock-file">
<h3><a class="toc-backref" href="#a-standardized-lock-file" role="doc-backlink">A standardized lock file</a></h3>
<p>A standardized lock file would solve a lot of the same problems that
specifying installer requirements would. For example, it would allow
for PaaS/IaaS to just support one installer that could read the
standardized lock file regardless of the installer that created it.
The problem with a standardized lock file is the difference in needs
between Python package installers as well as a fundamental issue with
creating reproducible environments via the lockfile (one of the main
benefits).</p>
<p>Needs and information stored in dependency files between installers
differ significantly and are dependent on installer functionality. For
example, a Python package installer such as Poetry requires
information for all Python versions and platforms and calculates
appropriate hashes while pip doesnt. Additionally, pip would not be
able to guarantee recreating the same environment (install the exact
same dependencies) as it is outside the scope of its functionality.
This makes a standardized lock file harder to implement and makes it
seem more appropriate to make lock files tool specific.</p>
</section>
<section id="have-installer-backends-support-creating-virtual-environments">
<h3><a class="toc-backref" href="#have-installer-backends-support-creating-virtual-environments" role="doc-backlink">Have installer backends support creating virtual environments</a></h3>
<p>Because <em>installer backends</em> will very likely have a concept of virtual
environments and how to install into them, it was briefly considered
to have them also support creating virtual environments. In the end,
though, it was considered an orthogonal idea.</p>
</section>
</section>
<section id="open-issues">
<h2><a class="toc-backref" href="#open-issues" role="doc-backlink">Open Issues</a></h2>
<section id="should-the-dependency-group-argument-take-an-iterable">
<h3><a class="toc-backref" href="#should-the-dependency-group-argument-take-an-iterable" role="doc-backlink">Should the <code class="docutils literal notranslate"><span class="pre">dependency_group</span></code> argument take an iterable?</a></h3>
<p>This would allow for specifying non-overlapping dependency groups in
a single call, e.g. “docs” and “test” groups which have independent
dependencies but which a developer may want to install simultaneously
while doing development.</p>
</section>
<section id="is-the-installer-backend-executed-in-process">
<h3><a class="toc-backref" href="#is-the-installer-backend-executed-in-process" role="doc-backlink">Is the installer backend executed in-process?</a></h3>
<p>If the <em>installer backend</em> is executed in-process then it greatly
simplifies knowing what environment to install for/into, as the live
Python environment can be queried for appropriate information.</p>
<p>Executing out-of-process allows for minimizing potential issues of
clashes between the environment being installed into and the
<em>installer backend</em> (and potentially <em>universal installer</em>).</p>
</section>
<section id="enforce-that-results-from-the-proposed-interface-feed-into-other-parts">
<h3><a class="toc-backref" href="#enforce-that-results-from-the-proposed-interface-feed-into-other-parts" role="doc-backlink">Enforce that results from the proposed interface feed into other parts?</a></h3>
<p>E.g. the results from <code class="docutils literal notranslate"><span class="pre">get_dependencies_to_install()</span></code> and
<code class="docutils literal notranslate"><span class="pre">get_dependency_groups()</span></code> can be passed into <code class="docutils literal notranslate"><span class="pre">invoke_install()</span></code>.
This would prevent drift between the results of various parts of the
proposed interface, but it makes more of the interface required
instead of optional.</p>
</section>
<section id="raising-exceptions-instead-of-exit-codes-for-failure-conditions">
<h3><a class="toc-backref" href="#raising-exceptions-instead-of-exit-codes-for-failure-conditions" role="doc-backlink">Raising exceptions instead of exit codes for failure conditions</a></h3>
<p>It has been suggested that instead of returning an exit code the API
should raise exceptions. If you view this PEP as helping to translate
current installers into <em>installer backends</em>, then relying on exit
codes makes sense. Theres is also the point that the APIs have no
specific return value, so passing along an exit code does not
interfere with what the functions return.</p>
<p>Compare that to raising exceptions in case of an error. That could
potentially provide a more structured approach to error raising,
although to be able to capture errors it would require specifying
exception types as part of the interface.</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-0650.rst">https://github.com/python/peps/blob/main/peps/pep-0650.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0650.rst">2023-09-09 17:39:29 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="#terminology">Terminology</a></li>
<li><a class="reference internal" href="#motivation">Motivation</a><ul>
<li><a class="reference internal" href="#providers">Providers</a><ul>
<li><a class="reference internal" href="#platform-infrastructure-providers">Platform/Infrastructure Providers</a></li>
<li><a class="reference internal" href="#ide-providers">IDE Providers</a></li>
</ul>
</li>
<li><a class="reference internal" href="#developers">Developers</a><ul>
<li><a class="reference internal" href="#developers-using-paas-iaas-providers">Developers using PaaS &amp; IaaS providers</a></li>
<li><a class="reference internal" href="#developers-using-ides">Developers using IDEs</a></li>
<li><a class="reference internal" href="#developers-working-with-other-developers">Developers working with other developers</a></li>
</ul>
</li>
<li><a class="reference internal" href="#upgraders-package-infrastructure-providers">Upgraders &amp; Package Infrastructure Providers</a></li>
<li><a class="reference internal" href="#open-source-community">Open Source Community</a></li>
</ul>
</li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#install-system">[install-system]</a><ul>
<li><a class="reference internal" href="#installer-requirements">Installer Requirements:</a></li>
<li><a class="reference internal" href="#additional-parameters-or-tool-specific-data">Additional parameters or tool specific data</a></li>
</ul>
</li>
<li><a class="reference internal" href="#installer-interface">Installer interface:</a></li>
<li><a class="reference internal" href="#mandatory-hooks">Mandatory hooks:</a><ul>
<li><a class="reference internal" href="#invoke-install">invoke_install</a></li>
</ul>
</li>
<li><a class="reference internal" href="#optional-hooks">Optional hooks:</a><ul>
<li><a class="reference internal" href="#invoke-uninstall">invoke_uninstall</a></li>
<li><a class="reference internal" href="#get-dependencies-to-install">get_dependencies_to_install</a></li>
<li><a class="reference internal" href="#get-dependency-groups">get_dependency_groups</a></li>
<li><a class="reference internal" href="#update-dependencies">update_dependencies</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#example">Example</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li>
<li><a class="reference internal" href="#security-implications">Security Implications</a></li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
<li><a class="reference internal" href="#a-standardized-lock-file">A standardized lock file</a></li>
<li><a class="reference internal" href="#have-installer-backends-support-creating-virtual-environments">Have installer backends support creating virtual environments</a></li>
</ul>
</li>
<li><a class="reference internal" href="#open-issues">Open Issues</a><ul>
<li><a class="reference internal" href="#should-the-dependency-group-argument-take-an-iterable">Should the <code class="docutils literal notranslate"><span class="pre">dependency_group</span></code> argument take an iterable?</a></li>
<li><a class="reference internal" href="#is-the-installer-backend-executed-in-process">Is the installer backend executed in-process?</a></li>
<li><a class="reference internal" href="#enforce-that-results-from-the-proposed-interface-feed-into-other-parts">Enforce that results from the proposed interface feed into other parts?</a></li>
<li><a class="reference internal" href="#raising-exceptions-instead-of-exit-codes-for-failure-conditions">Raising exceptions instead of exit codes for failure conditions</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-0650.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>