807 lines
57 KiB
HTML
807 lines
57 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 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:description" content="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 Poetry or Pipenv are popular as well due to offering unique features which are opti...">
|
|||
|
<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 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 Poetry or Pipenv are popular as well due to offering unique features which are opti...">
|
|||
|
<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 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 <vikramjayanthi at google.com>,
|
|||
|
Dustin Ingram <di at python.org>,
|
|||
|
Brett Cannon <brett at python.org></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 & 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 & 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 developer’s
|
|||
|
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 provider’s 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 & 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 & 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 Heroku’s 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 IDE’s 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 collaborator’s preference.</p>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
<section id="upgraders-package-infrastructure-providers">
|
|||
|
<h3><a class="toc-backref" href="#upgraders-package-infrastructure-providers" role="doc-backlink">Upgraders & 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 people’s 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 backend’s 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">"pipenv"</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">"pipenv.api:main"</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">"dev"</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">"deploy"</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">-></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 doesn’t 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">-></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">-></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 doesn’t 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">-></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">-></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">["requests==2.8.1",</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>Let’s 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">"-m"</span><span class="p">,</span>
|
|||
|
<span class="s2">"pip"</span><span class="p">,</span>
|
|||
|
<span class="s2">"install"</span><span class="p">,</span>
|
|||
|
<span class="s2">"-r"</span><span class="p">,</span>
|
|||
|
<span class="n">dependency_group</span> <span class="ow">or</span> <span class="s2">"requirements.txt"</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">"pep650pip"</span><span class="p">,</span> <span class="s2">"pip"</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">"pep650pip:main"</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> doesn’t 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 doesn’t. 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. There’s 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 & 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 & 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>
|