python-peps/pep-0722/index.html

780 lines
62 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<title>PEP 722 Dependency specification for single-file scripts | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0722/">
<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 722 Dependency specification for single-file scripts | peps.python.org'>
<meta property="og:description" content="This PEP specifies a format for including 3rd-party dependencies in a single-file Python script.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0722/">
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
<meta property="og:image:alt" content="Python PEPs">
<meta property="og:image:width" content="200">
<meta property="og:image:height" content="200">
<meta name="description" content="This PEP specifies a format for including 3rd-party dependencies in a single-file Python script.">
<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 722</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 722 Dependency specification for single-file scripts</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Paul Moore &lt;p.f.moore&#32;&#97;t&#32;gmail.com&gt;</dd>
<dt class="field-even">PEP-Delegate<span class="colon">:</span></dt>
<dd class="field-even">Brett Cannon &lt;brett&#32;&#97;t&#32;python.org&gt;</dd>
<dt class="field-odd">Discussions-To<span class="colon">:</span></dt>
<dd class="field-odd"><a class="reference external" href="https://discuss.python.org/t/29905">Discourse thread</a></dd>
<dt class="field-even">Status<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Formally declined and will not be accepted">Rejected</abbr></dd>
<dt class="field-odd">Type<span class="colon">:</span></dt>
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
<dt class="field-even">Topic<span class="colon">:</span></dt>
<dd class="field-even"><a class="reference external" href="../topic/packaging/">Packaging</a></dd>
<dt class="field-odd">Created<span class="colon">:</span></dt>
<dd class="field-odd">19-Jul-2023</dd>
<dt class="field-even">Post-History<span class="colon">:</span></dt>
<dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/29905" title="Discourse thread">19-Jul-2023</a></dd>
<dt class="field-odd">Resolution<span class="colon">:</span></dt>
<dd class="field-odd"><a class="reference external" href="https://discuss.python.org/t/pep-722-723-decision/36763/">Discourse thread</a></dd>
</dl>
<hr class="docutils" />
<section id="contents">
<details><summary>Table of Contents</summary><ul class="simple">
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#motivation">Motivation</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#example">Example</a></li>
</ul>
</li>
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li>
<li><a class="reference internal" href="#security-implications">Security Implications</a></li>
<li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li>
<li><a class="reference internal" href="#recommendations">Recommendations</a></li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
<li><a class="reference internal" href="#why-not-include-other-metadata">Why not include other metadata?</a></li>
<li><a class="reference internal" href="#why-not-use-a-marker-per-line">Why not use a marker per line?</a></li>
<li><a class="reference internal" href="#why-not-use-a-distinct-form-of-comment-for-the-dependency-block">Why not use a distinct form of comment for the dependency block?</a></li>
<li><a class="reference internal" href="#why-not-allow-multiple-dependency-blocks-and-merge-them">Why not allow multiple dependency blocks and merge them?</a></li>
<li><a class="reference internal" href="#why-not-use-a-more-standard-data-format-e-g-toml">Why not use a more standard data format (e.g., TOML)?</a></li>
<li><a class="reference internal" href="#why-not-use-possibly-restricted-python-syntax">Why not use (possibly restricted) Python syntax?</a></li>
<li><a class="reference internal" href="#why-not-embed-a-pyproject-toml-file-in-the-script">Why not embed a <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> file in the script?</a></li>
<li><a class="reference internal" href="#why-not-infer-the-requirements-from-import-statements">Why not infer the requirements from import statements?</a></li>
<li><a class="reference internal" href="#why-not-simply-manage-the-environment-at-runtime">Why not simply manage the environment at runtime?</a></li>
<li><a class="reference internal" href="#why-not-just-set-up-a-python-project-with-a-pyproject-toml">Why not just set up a Python project with a <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>?</a></li>
<li><a class="reference internal" href="#why-not-use-a-requirements-file-for-dependencies">Why not use a requirements file for dependencies?</a></li>
<li><a class="reference internal" href="#should-scripts-be-able-to-specify-a-package-index">Should scripts be able to specify a package index?</a></li>
<li><a class="reference internal" href="#what-about-local-dependencies">What about local dependencies?</a></li>
</ul>
</li>
<li><a class="reference internal" href="#open-issues">Open Issues</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
</details></section>
<section id="abstract">
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
<p>This PEP specifies a format for including 3rd-party dependencies in a
single-file Python script.</p>
</section>
<section id="motivation">
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
<p>Not all Python code is structured as a “project”, in the sense of having its own
directory complete with a <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> file, and being built into an
installable distribution package. Python is also routinely used as a scripting
language, with Python scripts as a (better) alternative to shell scripts, batch
files, etc. When used to create scripts, Python code is typically stored as a
single file, often in a directory dedicated to such “utility scripts”, which
might be in a mix of languages with Python being only one possibility among
many. Such scripts may be shared, often by something as simple as email, or a
link to a URL such as a Github gist. But they are typically <em>not</em> “distributed”
or “installed” as part of a normal workflow.</p>
<p>One problem when using Python as a scripting language in this way is how to run
the script in an environment that contains whatever third party dependencies are
required by the script. There is currently no standard tool that addresses this
issue, and this PEP does <em>not</em> attempt to define one. However, any tool that
<em>does</em> address this issue will need to know what 3rd party dependencies a script
requires. By defining a standard format for storing such data, existing tools,
as well as any future tools, will be able to obtain that information without
requiring users to include tool-specific metadata in their scripts.</p>
</section>
<section id="rationale">
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
<p>Because a key requirement is writing single-file scripts, and simple sharing by
giving someone a copy of the script, the PEP defines a mechanism for embedding
dependency data <em>within the script itself</em>, and not in an external file.</p>
<p>We define the concept of a <em>dependency block</em> that contains information about
what 3rd party packages a script depends on.</p>
<p>In order to identify dependency blocks, the script can simply be read as a text
file. This is deliberate, as Python syntax changes over time, so attempting to
parse the script as Python code would require choosing a specific version of
Python syntax. Also, it is likely that at least some tools will not be written
in Python, and expecting them to implement a Python parser is too much of a
burden.</p>
<p>However, to avoid needing changes to core Python, the format is designed to
appear as comments to the Python parser. It is possible to write code where a
dependency block is <em>not</em> interpreted as a comment (for example, by embedding it
in a Python multi-line string), but such uses are discouraged and can easily be
avoided assuming you are not deliberately trying to create a pathological
example.</p>
<p>A <a class="reference external" href="https://dbohdan.com/scripts-with-dependencies">review</a> of how other languages allow scripts to specify
their dependencies shows that a “structured comment” like this is a
commonly-used approach.</p>
</section>
<section id="specification">
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
<p>The content of this section will be published in the Python Packaging user
guide, PyPA Specifications section, as a document with the title “Embedding
Metadata in Script Files”.</p>
<p>Any Python script may contain a <em>dependency block</em>. The dependency block is
identified by reading the script <em>as a text file</em> (i.e., the file is not parsed
as Python source code), looking for the first line of the form:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Script Dependencies:</span>
</pre></div>
</div>
<p>The hash character must be at the start of the line with no preceding whitespace.
The text “Script Dependencies” is recognised regardless of case, and the spaces
represent arbitrary whitespace (although at least one space must be present). The
following regular expression recognises the dependency block header line:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>(?i)^#\s+script\s+dependencies:\s*$
</pre></div>
</div>
<p>Tools reading the dependency block MAY respect the standard Python encoding
declaration. If they choose not to do so, they MUST process the file as UTF-8.</p>
<p>After the header line, all lines in the file up to the first line that doesnt
start with a <code class="docutils literal notranslate"><span class="pre">#</span></code> sign are considered <em>dependency lines</em> and are treated as
follows:</p>
<ol class="arabic simple">
<li>The initial <code class="docutils literal notranslate"><span class="pre">#</span></code> sign is stripped.</li>
<li>If the line contains the character sequence “ # “ (SPACE HASH SPACE), then
those characters and any subsequent characters are discarded. This allows
dependency blocks to contain inline comments.</li>
<li>Whitespace at the start and end of the remaining text is discarded.</li>
<li>If the line is now empty, it is ignored.</li>
<li>The content of the line MUST now be a valid <a class="pep reference internal" href="../pep-0508/" title="PEP 508 Dependency specification for Python Software Packages">PEP 508</a> dependency specifier.</li>
</ol>
<p>The requirement for spaces before and after the <code class="docutils literal notranslate"><span class="pre">#</span></code> in an inline comment is
necessary to distinguish them from part of a <a class="pep reference internal" href="../pep-0508/" title="PEP 508 Dependency specification for Python Software Packages">PEP 508</a> URL specifier (which
can contain a hash, but without surrounding whitespace).</p>
<p>Consumers MUST validate that at a minimum, all dependencies start with a
<code class="docutils literal notranslate"><span class="pre">name</span></code> as defined in <a class="pep reference internal" href="../pep-0508/" title="PEP 508 Dependency specification for Python Software Packages">PEP 508</a>, and they MAY validate that all dependencies
conform fully to <a class="pep reference internal" href="../pep-0508/" title="PEP 508 Dependency specification for Python Software Packages">PEP 508</a>. They MUST fail with an error if they find an
invalid specifier.</p>
<section id="example">
<h3><a class="toc-backref" href="#example" role="doc-backlink">Example</a></h3>
<p>The following is an example of a script with an embedded dependency block:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># In order to run, this script needs the following 3rd party libraries</span>
<span class="c1">#</span>
<span class="c1"># Script Dependencies:</span>
<span class="c1"># requests</span>
<span class="c1"># rich # Needed for the output</span>
<span class="c1">#</span>
<span class="c1"># # Not needed - just to show that fragments in URLs do not</span>
<span class="c1"># # get treated as comments</span>
<span class="c1"># pip @ https://github.com/pypa/pip/archive/1.3.1.zip#sha1=da9234ee9982d4bbb3c72346a6de940a148ea686</span>
<span class="kn">import</span> <span class="nn">requests</span>
<span class="kn">from</span> <span class="nn">rich.pretty</span> <span class="kn">import</span> <span class="n">pprint</span>
<span class="n">resp</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;https://peps.python.org/api/peps.json&quot;</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">resp</span><span class="o">.</span><span class="n">json</span><span class="p">()</span>
<span class="n">pprint</span><span class="p">([(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">[</span><span class="s2">&quot;title&quot;</span><span class="p">])</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">data</span><span class="o">.</span><span class="n">items</span><span class="p">()][:</span><span class="mi">10</span><span class="p">])</span>
</pre></div>
</div>
</section>
</section>
<section id="backwards-compatibility">
<h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2>
<p>As dependency blocks take the form of a structured comment, they can be added
without altering the meaning of existing code.</p>
<p>It is possible that a comment may already exist which matches the form of a
dependency block. While the identifying header text, “Script Dependencies” is
chosen to minimise this risk, it is still possible.</p>
<p>In the rare case where an existing comment would be interpreted incorrectly as a
dependency block, this can be addressed by adding an actual dependency block
(which can be empty if the script has no dependencies) earlier in the code.</p>
</section>
<section id="security-implications">
<h2><a class="toc-backref" href="#security-implications" role="doc-backlink">Security Implications</a></h2>
<p>If a script containing a dependency block is run using a tool that automatically
installs dependencies, this could cause arbitrary code to be downloaded and
installed in the users environment.</p>
<p>The risk here is part of the functionality of the tool being used to run the
script, and as such should already be addressed by the tool itself. The only
additional risk introduced by this PEP is if an untrusted script with a
dependency block is run, when a potentially malicious dependency might be
installed. This risk is addressed by the normal good practice of reviewing code
before running it.</p>
</section>
<section id="how-to-teach-this">
<h2><a class="toc-backref" href="#how-to-teach-this" role="doc-backlink">How to Teach This</a></h2>
<p>The format is intended to be close to how a developer might already specify
script dependencies in an explanatory comment. The required structure is
deliberately minimal, so that formatting rules are easy to learn.</p>
<p>Users will need to know how to write Python dependency specifiers. This is
covered by <a class="pep reference internal" href="../pep-0508/" title="PEP 508 Dependency specification for Python Software Packages">PEP 508</a>, but for simple examples (which is expected to be the norm
for inexperienced users) the syntax is either just a package name, or a name and
a version restriction, which is fairly well-understood syntax.</p>
<p>Users will also know how to <em>run</em> a script using a tool that interprets
dependency data. This is not covered by this PEP, as it is the responsibility of
such a tool to document how it should be used.</p>
<p>Note that the core Python interpreter does <em>not</em> interpret dependency blocks.
This may be a point of confusion for beginners, who try to run <code class="docutils literal notranslate"><span class="pre">python</span>
<span class="pre">some_script.py</span></code> and do not understand why it fails. This is no different than
the current status quo, though, where running a script without its dependencies
present will give an error.</p>
<p>In general, it is assumed that if a beginner is given a script with dependencies
(regardless of whether they are specified in a dependency block), the person
supplying the script should explain how to run that script, and if that involves
using a script runner tool, that should be noted.</p>
</section>
<section id="recommendations">
<h2><a class="toc-backref" href="#recommendations" role="doc-backlink">Recommendations</a></h2>
<p>This section is non-normative and simply describes “good practices” when using
dependency blocks.</p>
<p>While it is permitted for tools to do minimal validation of requirements, in
practice they should do as much “sanity check” validation as possible, even if
they cannot do a full check for <a class="pep reference internal" href="../pep-0508/" title="PEP 508 Dependency specification for Python Software Packages">PEP 508</a> syntax. This helps to ensure that
dependency blocks that are not correctly terminated are reported early. A good
compromise between the minimal approach of checking just that the requirement
starts with a name, and full <a class="pep reference internal" href="../pep-0508/" title="PEP 508 Dependency specification for Python Software Packages">PEP 508</a> validation, is to check for a bare name,
or a name followed by optional whitespace, and then one of <code class="docutils literal notranslate"><span class="pre">[</span></code> (extra), <code class="docutils literal notranslate"><span class="pre">&#64;</span></code>
(urlspec), <code class="docutils literal notranslate"><span class="pre">;</span></code> (marker) or one of <code class="docutils literal notranslate"><span class="pre">(&lt;!=&gt;~</span></code> (version).</p>
<p>Scripts should, in general, place the dependency block at the top of the file,
either immediately after any shebang line, or straight after the script
docstring. In particular, the dependency block should always be placed before
any executable code in the file. This makes it easy for the human reader to
locate it.</p>
</section>
<section id="reference-implementation">
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
<p>Code to implement this proposal in Python is fairly straightforward, so the
reference implementation can be included here.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">tokenize</span>
<span class="kn">from</span> <span class="nn">packaging.requirements</span> <span class="kn">import</span> <span class="n">Requirement</span>
<span class="n">DEPENDENCY_BLOCK_MARKER</span> <span class="o">=</span> <span class="sa">r</span><span class="s2">&quot;(?i)^#\s+script\s+dependencies:\s*$&quot;</span>
<span class="k">def</span> <span class="nf">read_dependency_block</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
<span class="c1"># Use the tokenize module to handle any encoding declaration.</span>
<span class="k">with</span> <span class="n">tokenize</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="c1"># Skip lines until we reach a dependency block (OR EOF).</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">f</span><span class="p">:</span>
<span class="k">if</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">DEPENDENCY_BLOCK_MARKER</span><span class="p">,</span> <span class="n">line</span><span class="p">):</span>
<span class="k">break</span>
<span class="c1"># Read dependency lines until we hit a line that doesn&#39;t</span>
<span class="c1"># start with #, or we are at EOF.</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">f</span><span class="p">:</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">line</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;#&quot;</span><span class="p">):</span>
<span class="k">break</span>
<span class="c1"># Remove comments. An inline comment is introduced by</span>
<span class="c1"># a hash, which must be preceded and followed by a</span>
<span class="c1"># space.</span>
<span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot; # &quot;</span><span class="p">,</span> <span class="n">maxsplit</span><span class="o">=</span><span class="mi">1</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="c1"># Ignore empty lines</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">line</span><span class="p">:</span>
<span class="k">continue</span>
<span class="c1"># Try to convert to a requirement. This will raise</span>
<span class="c1"># an error if the line is not a PEP 508 requirement</span>
<span class="k">yield</span> <span class="n">Requirement</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
</pre></div>
</div>
<p>A format similar to the one proposed here is already supported <a class="reference external" href="https://github.com/pypa/pipx/pull/916">in pipx</a> and in <a class="reference external" href="https://pypi.org/project/pip-run/">pip-run</a>.</p>
</section>
<section id="rejected-ideas">
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2>
<section id="why-not-include-other-metadata">
<h3><a class="toc-backref" href="#why-not-include-other-metadata" role="doc-backlink">Why not include other metadata?</a></h3>
<p>The core use case addressed by this proposal is that of identifying what
dependencies a standalone script needs in order to run successfully. This is a
common real-world issue that is currently solved by script runner tools, using
implementation-specific ways of storing the data. Standardising the storage
format improves interoperability by not typing the script to a particular
runner.</p>
<p>While it is arguable that other forms of metadata could be useful in a
standalone script, the need is largely theoretical at this point. In practical
terms, scripts either dont use other metadata, or they store it in existing,
widely used (and therefore de facto standard) formats. For example, scripts
needing README style text typically use the standard Python module docstring,
and scripts wanting to declare a version use the common convention of having a
<code class="docutils literal notranslate"><span class="pre">__version__</span></code> variable.</p>
<p>One case which was raised during the discussion on this PEP, was the ability to
declare a minimum Python version that a script needed to run, by analogy with
the <code class="docutils literal notranslate"><span class="pre">Requires-Python</span></code> core metadata item for packages. Unlike packages,
scripts are normally only run by one user or in one environment, in contexts
where multiple versions of Python are uncommon. The need for this metadata is
therefore much less critical in the case of scripts. As further evidence of
this, the two key script runners currently available, <code class="docutils literal notranslate"><span class="pre">pipx</span></code> and <code class="docutils literal notranslate"><span class="pre">pip-run</span></code>
do not offer a means of including this data in a script.</p>
<p>Creating a standard “metadata container” format would unify the various
approaches, but in practical terms there is no real need for unification, and
the disruption would either delay adoption, or more likely simply mean script
authors would ignore the standard.</p>
<p>This proposal therefore chooses to focus just on the one use case where there is
a clear need for something, and no existing standard or common practice.</p>
</section>
<section id="why-not-use-a-marker-per-line">
<h3><a class="toc-backref" href="#why-not-use-a-marker-per-line" role="doc-backlink">Why not use a marker per line?</a></h3>
<p>Rather than using a comment block with a header, another possibility would be to
use a marker on each line, something like:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Script-Dependency: requests</span>
<span class="c1"># Script-Dependency: click</span>
</pre></div>
</div>
<p>While this makes it easier to parse lines individually, it has a number of
issues. The first is simply that its rather verbose, and less readable. This is
clearly affected by the chosen keyword, but all of the suggested options were
(in the authors opinion) less readable than the block comment form.</p>
<p>More importantly, this form <em>by design</em> makes it impossible to require that the
dependency specifiers are all together in a single block. As a result, its not
possible for a human reader, without a careful check of the whole file, to be
sure that they have identified all of the dependencies. See the question below,
“Why not allow multiple dependency blocks and merge them?”, for further
discussion of this problem.</p>
<p>Finally, as the reference implementation demonstrates, parsing the “comment
block” form isnt, in practice, significantly more difficult than parsing this
form.</p>
</section>
<section id="why-not-use-a-distinct-form-of-comment-for-the-dependency-block">
<h3><a class="toc-backref" href="#why-not-use-a-distinct-form-of-comment-for-the-dependency-block" role="doc-backlink">Why not use a distinct form of comment for the dependency block?</a></h3>
<p>A previous version of this proposal used <code class="docutils literal notranslate"><span class="pre">##</span></code> to identify dependency blocks.
Unfortunately, however, the flake8 linter implements a rule requiring that
comments must have a space after the initial <code class="docutils literal notranslate"><span class="pre">#</span></code> sign. While the PEP author
considers that rule misguided, it is on by default and as a result would cause
checks to fail when faced with a dependency block.</p>
<p>Furthermore, the <code class="docutils literal notranslate"><span class="pre">black</span></code> formatter, although it allows the <code class="docutils literal notranslate"><span class="pre">##</span></code> form, does
add a space after the <code class="docutils literal notranslate"><span class="pre">#</span></code> for most other forms of comment. This means that if
we chose an alternative like <code class="docutils literal notranslate"><span class="pre">#%</span></code>, automatic reformatting would corrupt the
dependency block. Forms including a space, like <code class="docutils literal notranslate"><span class="pre">#</span> <span class="pre">#</span></code> are possible, but less
natural for the average user (omitting the space is an obvious mistake to make).</p>
<p>While it is possible that linters and formatters could be changed to recognise
the new standard, the benefit of having a dedicated prefix did not seem
sufficient to justify the transition cost, or the risk that users might be using
older tools.</p>
</section>
<section id="why-not-allow-multiple-dependency-blocks-and-merge-them">
<h3><a class="toc-backref" href="#why-not-allow-multiple-dependency-blocks-and-merge-them" role="doc-backlink">Why not allow multiple dependency blocks and merge them?</a></h3>
<p>Because its too easy for the human reader to miss the fact that theres a
second dependency block. This could simply result in the script runner
unexpectedly downloading extra packages, or it could even be a way to smuggle
malicious packages onto a users machine (by “hiding” a second dependency block
in the body of the script).</p>
<p>While the principle of “dont run untrusted code” applies here, the benefits
arent sufficient to be worth the risk.</p>
</section>
<section id="why-not-use-a-more-standard-data-format-e-g-toml">
<h3><a class="toc-backref" href="#why-not-use-a-more-standard-data-format-e-g-toml" role="doc-backlink">Why not use a more standard data format (e.g., TOML)?</a></h3>
<p>First of all, the only practical choice for an alternative format is TOML.
Python packaging has standardised on TOML for structured data, and using a
different format, such as YAML or JSON, would add complexity and confusion for
no real benefit.</p>
<p>So the question is essentially, “why not use TOML?”</p>
<p>The key idea behind the “dependency block” format is to define something that
reads naturally as a comment in the script. Dependency data is useful both for
tools and for the human reader, so having a human readable format is beneficial.
On the other hand, TOML of necessity has a syntax of its own, which distracts
from the underlying data.</p>
<p>It is important to remember that developers who <em>write</em> scripts in Python are
often <em>not</em> experienced in Python, or Python packaging. They are often systems
administrators, or data analysts, who may simply be using Python as a “better
batch file”. For such users, the TOML format is extremely likely to be
unfamiliar, and the syntax will be obscure to them, and not particularly
intuitive. Such developers may well be copying dependency specifiers from
sources such as Stack Overflow, without really understanding them. Having to
embed such a requirement into a TOML structure is an additional complexity
and it is important to remember that the goal here is to make using 3rd party
libraries <em>easy</em> for such users.</p>
<p>Furthermore, TOML, by its nature, is a flexible format intended to support very
general data structures. There are <em>many</em> ways of writing a simple list of
strings in it, and it will not be clear to inexperienced users which form to use.</p>
<p>Another potential issue is that using a generalised TOML parser can <a class="reference external" href="https://discuss.python.org/t/pep-722-dependency-specification-for-single-file-scripts/29905/275">in some cases</a>
result in a measurable performance overhead. Startup time is often quoted as an
issue when running small scripts, so this may be a problem for script runners that
are aiming for high performance.</p>
<p>And finally, there will be tools that expect to <em>write</em> dependency data into
scripts for example, an IDE with a feature that automatically adds an import
and a dependency specifier when you reference a library function. While
libraries exist that allow editing TOML data, they are not always good at
preserving the users layout. Even if libraries exist which do an effective job
at this, expecting all tools to use such a library is a significant imposition
on code supporting this PEP.</p>
<p>By choosing a simple, line-based format with no quoting rules, dependency data
is easy to read (for humans and tools) and easy to write. The format doesnt
have the flexibility of something like TOML, but the use case simply doesnt
demand that sort of flexibility.</p>
</section>
<section id="why-not-use-possibly-restricted-python-syntax">
<h3><a class="toc-backref" href="#why-not-use-possibly-restricted-python-syntax" role="doc-backlink">Why not use (possibly restricted) Python syntax?</a></h3>
<p>This would typically involve storing the dependencies as a (runtime) list
variable with a conventional name, such as:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">__requires__</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">&quot;requests&quot;</span><span class="p">,</span>
<span class="s2">&quot;click&quot;</span><span class="p">,</span>
<span class="p">]</span>
</pre></div>
</div>
<p>Other suggestions include a static multi-line string, or including the
dependencies in the scripts docstring.</p>
<p>The most significant problem with this proposal is that it requires all
consumers of the dependency data to implement a Python parser. Even if the
syntax is restricted, the <em>rest</em> of the script will use the full Python syntax,
and trying to define a syntax which can be successfully parsed in isolation from
the surrounding code is likely to be extremely difficult and error-prone.</p>
<p>Furthermore, Pythons syntax changes in every release. If extracting dependency
data needs a Python parser, the parser will need to know which version of Python
the script is written for, and the overhead for a generic tool of having a
parser that can handle <em>multiple</em> versions of Python is unsustainable.</p>
<p>Even if the above issues could be addressed, the format would give the
impression that the data could be altered at runtime. However, this is not the
case in general, and code that tries to do so will encounter unexpected and
confusing behaviour.</p>
<p>And finally, there is no evidence that having dependency data available at
runtime is of any practical use. Should such a use be found, it is simple enough
to get the data by parsing the source - <code class="docutils literal notranslate"><span class="pre">read_dependency_block(__file__)</span></code>.</p>
<p>It is worth noting, though, that the <code class="docutils literal notranslate"><span class="pre">pip-run</span></code> utility does implement (an
extended form of) this approach. <a class="reference external" href="https://github.com/jaraco/pip-run/issues/44">Further discussion</a> of
the <code class="docutils literal notranslate"><span class="pre">pip-run</span></code> design is available on the projects issue tracker.</p>
</section>
<section id="why-not-embed-a-pyproject-toml-file-in-the-script">
<h3><a class="toc-backref" href="#why-not-embed-a-pyproject-toml-file-in-the-script" role="doc-backlink">Why not embed a <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> file in the script?</a></h3>
<p>First of all, <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> is a TOML based format, so all of the previous
concerns around TOML as a format apply. However, <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> is a
standard used by Python packaging, and re-using an existing standard is a
reasonable suggestion that deserves to be addressed on its own merits.</p>
<p>The first issue is that the suggestion rarely implies that <em>all</em> of
<code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> is to be supported for scripts. A script is not intended to
be “built” into any sort of distributable artifact like a wheel (see below for
more on this point), so the <code class="docutils literal notranslate"><span class="pre">[build-system]</span></code> section of <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>
makes little sense, for example. And while the tool-specific sections of
<code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> might be useful for scripts, its not at all clear that a
tool like <a class="reference external" href="https://beta.ruff.rs/docs/">ruff</a> would want to support per-file
configuration in this way, leading to confusion when users <em>expect</em> it to work,
but it doesnt. Furthermore, this sort of tool-specific configuration is just as
useful for individual files in a larger project, so we have to consider what it
would mean to embed a <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> into a single file in a larger project
that has its own <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>.</p>
<p>In addition, <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> is currently focused on projects that are to be
built into wheels. There is <a class="reference external" href="https://discuss.python.org/t/projects-that-arent-meant-to-generate-a-wheel-and-pyproject-toml/29684">an ongoing discussion</a>
about how to use <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> for projects that are not intended to be
built as wheels, and until that question is resolved (which will likely require
some PEPs of its own) it seems premature to be discussing embedding
<code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> into scripts, which are <em>definitely</em> not intended to be built
and distributed in that manner.</p>
<p>The conclusion, therefore (which has been stated explicitly in some, but not
all, cases) is that this proposal is intended to mean that we would embed <em>part
of</em> <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>. Typically this is the <code class="docutils literal notranslate"><span class="pre">[project]</span></code> section from
<a class="pep reference internal" href="../pep-0621/" title="PEP 621 Storing project metadata in pyproject.toml">PEP 621</a>, or even just the <code class="docutils literal notranslate"><span class="pre">dependencies</span></code> item from that section.</p>
<p>At this point, the first issue is that by framing the proposal as “embedding
<code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>”, we would be encouraging the sort of confusion discussed in
the previous paragraphs - developers will expect the full capabilities of
<code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>, and be confused when there are differences and limitations.
It would be better, therefore, to consider this suggestion as simply being a
proposal to use an embedded TOML format, but specifically re-using the
<em>structure</em> of a particular part of <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>. The problem then becomes
how we describe that structure, <em>without</em> causing confusion for people familiar
with <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>. If we describe it with reference to <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>,
the link is still there. But if we describe it in isolation, people will be
confused by the “similar but different” nature of the structure.</p>
<p>It is also important to remember that a key part of the target audience for this
proposal is developers who are simply using Python as a “better batch file”
solution. These developers will generally not be familiar with Python packaging
and its conventions, and are often the people most critical of the “complexity”
and “difficulty” of packaging solutions. As a result, proposals based on those
existing solutions are likely to be unwelcome to that audience, and could easily
result in people simply continuing to use existing adhoc solutions, and ignoring
the standard that was intended to make their lives easier.</p>
</section>
<section id="why-not-infer-the-requirements-from-import-statements">
<h3><a class="toc-backref" href="#why-not-infer-the-requirements-from-import-statements" role="doc-backlink">Why not infer the requirements from import statements?</a></h3>
<p>The idea would be to automatically recognize <code class="docutils literal notranslate"><span class="pre">import</span></code> statements in the source
file and turn them into a list of requirements.</p>
<p>However, this is infeasible for several reasons. First, the points above about
the necessity to keep the syntax easily parsable, for all Python versions, also
by tools written in other languages, apply equally here.</p>
<p>Second, PyPI and other package repositories conforming to the Simple Repository
API do not provide a mechanism to resolve package names from the module names
that are imported (see also <a class="reference external" href="https://discuss.python.org/t/record-the-top-level-names-of-a-wheel-in-metadata/29494">this related discussion</a>).</p>
<p>Third, even if repositories did offer this information, the same import name may
correspond to several packages on PyPI. One might object that disambiguating
which package is wanted would only be needed if there are several projects
providing the same import name. However, this would make it easy for anyone to
unintentionally or malevolently break working scripts, by uploading a package to
PyPI providing an import name that is the same as an existing project. The
alternative where, among the candidates, the first package to have been
registered on the index is chosen, would be confusing in case a popular package
is developed with the same import name as an existing obscure package, and even
harmful if the existing package is malware intentionally uploaded with a
sufficiently generic import name that has a high probability of being reused.</p>
<p>A related idea would be to attach the requirements as comments to the import
statements instead of gathering them in a block, with a syntax such as:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span> <span class="c1"># requires: numpy</span>
<span class="kn">import</span> <span class="nn">rich</span> <span class="c1"># requires: rich</span>
</pre></div>
</div>
<p>This still suffers from parsing difficulties. Also, where to place the comment
in the case of multiline imports is ambiguous and may look ugly:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">PyQt5.QtWidgets</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">QCheckBox</span><span class="p">,</span> <span class="n">QComboBox</span><span class="p">,</span> <span class="n">QDialog</span><span class="p">,</span> <span class="n">QDialogButtonBox</span><span class="p">,</span>
<span class="n">QGridLayout</span><span class="p">,</span> <span class="n">QLabel</span><span class="p">,</span> <span class="n">QSpinBox</span><span class="p">,</span> <span class="n">QTextEdit</span>
<span class="p">)</span> <span class="c1"># requires: PyQt5</span>
</pre></div>
</div>
<p>Furthermore, this syntax cannot behave as might be intuitively expected
in all situations. Consider:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">platform</span>
<span class="k">if</span> <span class="n">platform</span><span class="o">.</span><span class="n">system</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&quot;Windows&quot;</span><span class="p">:</span>
<span class="kn">import</span> <span class="nn">pywin32</span> <span class="c1"># requires: pywin32</span>
</pre></div>
</div>
<p>Here, the users intent is that the package is only required on Windows, but
this cannot be understood by the script runner (the correct way to write
it would be <code class="docutils literal notranslate"><span class="pre">requires:</span> <span class="pre">pywin32</span> <span class="pre">;</span> <span class="pre">sys_platform</span> <span class="pre">==</span> <span class="pre">'win32'</span></code>).</p>
<p>(Thanks to Jean Abou-Samra for the clear discussion of this point)</p>
</section>
<section id="why-not-simply-manage-the-environment-at-runtime">
<h3><a class="toc-backref" href="#why-not-simply-manage-the-environment-at-runtime" role="doc-backlink">Why not simply manage the environment at runtime?</a></h3>
<p>Another approach to running scripts with dependencies is simply to manage those
dependencies at runtime. This can be done by using a library that makes packages
available. There are many options for implementing such a library, for example
by installing them directly into the users environment or by manipulating
<code class="docutils literal notranslate"><span class="pre">sys.path</span></code> to make them available from a local cache.</p>
<p>These approaches are not incompatible with this PEP. An API such as</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">env_mgr</span><span class="o">.</span><span class="n">install</span><span class="p">(</span><span class="s2">&quot;rich&quot;</span><span class="p">)</span>
<span class="n">env_mgr</span><span class="o">.</span><span class="n">install</span><span class="p">(</span><span class="s2">&quot;click&quot;</span><span class="p">)</span>
<span class="kn">import</span> <span class="nn">rich</span>
<span class="kn">import</span> <span class="nn">click</span>
<span class="o">...</span>
</pre></div>
</div>
<p>is certainly feasible. However, such a library could be written without the need
for any new standards, and as far as the PEP author is aware, this has not
happened. This suggests that an approach like this is not as attractive as it
first seems. There is also the bootstrapping issue of making the <code class="docutils literal notranslate"><span class="pre">env_mgr</span></code>
library available in the first place. And finally, this approach doesnt
actually offer any interoperability benefits, as it does not use a standard form
for the dependency list, and so other tools cannot access the data.</p>
<p>In any case, such a library could still benefit from this proposal, as it could
include an API to read the packages to install from the script dependency block.
This would give the same functionality while allowing interoperability with
other tools that support this specification.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># Script Dependencies:</span>
<span class="c1"># rich</span>
<span class="c1"># click</span>
<span class="n">env_mgr</span><span class="o">.</span><span class="n">install_dependencies</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span>
<span class="kn">import</span> <span class="nn">rich</span>
<span class="kn">import</span> <span class="nn">click</span>
<span class="o">...</span>
</pre></div>
</div>
</section>
<section id="why-not-just-set-up-a-python-project-with-a-pyproject-toml">
<h3><a class="toc-backref" href="#why-not-just-set-up-a-python-project-with-a-pyproject-toml" role="doc-backlink">Why not just set up a Python project with a <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>?</a></h3>
<p>Again, a key issue here is that the target audience for this proposal is people
writing scripts which arent intended for distribution. Sometimes scripts will
be “shared”, but this is far more informal than “distribution” - it typically
involves sending a script via an email with some written instructions on how to
run it, or passing someone a link to a gist.</p>
<p>Expecting such users to learn the complexities of Python packaging is a
significant step up in complexity, and would almost certainly give the
impression that “Python is too hard for scripts”.</p>
<p>In addition, if the expectation here is that the <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> will somehow
be designed for running scripts in place, thats a new feature of the standard
that doesnt currently exist. At a minimum, this isnt a reasonable suggestion
until the <a class="reference external" href="https://discuss.python.org/t/projects-that-arent-meant-to-generate-a-wheel-and-pyproject-toml/29684">current discussion on Discourse</a> about
using <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> for projects that wont be distributed as wheels is
resolved. And even then, it doesnt address the “sending someone a script in a
gist or email” use case.</p>
</section>
<section id="why-not-use-a-requirements-file-for-dependencies">
<h3><a class="toc-backref" href="#why-not-use-a-requirements-file-for-dependencies" role="doc-backlink">Why not use a requirements file for dependencies?</a></h3>
<p>Putting your requirements in a requirements file, doesnt require a PEP. You can
do that right now, and in fact its quite likely that many adhoc solutions do
this. However, without a standard, theres no way of knowing how to locate a
scripts dependency data. And furthermore, the requirements file format is
pip-specific, so tools relying on it are depending on a pip implementation
detail.</p>
<p>So in order to make a standard, two things would be required:</p>
<ol class="arabic simple">
<li>A standardised replacement for the requirements file format.</li>
<li>A standard for how to locate the requirements file for a given script.</li>
</ol>
<p>The first item is a significant undertaking. It has been discussed on a number
of occasions, but so far no-one has attempted to actually do it. The most likely
approach would be for standards to be developed for individual use cases
currently addressed with requirements files. One option here would be for this
PEP to simply define a new file format which is simply a text file containing
<a class="pep reference internal" href="../pep-0508/" title="PEP 508 Dependency specification for Python Software Packages">PEP 508</a> requirements, one per line. That would just leave the question of how
to locate that file.</p>
<p>The “obvious” solution here would be to do something like name the file the same
as the script, but with a <code class="docutils literal notranslate"><span class="pre">.reqs</span></code> extension (or something similar). However,
this still requires <em>two</em> files, where currently only a single file is needed,
and as such, does not match the “better batch file” model (shell scripts and
batch files are typically self-contained). It requires the developer to remember
to keep the two files together, and this may not always be possible. For
example, system administration policies may require that <em>all</em> files in a
certain directory are executable (the Linux filesystem standards require this of
<code class="docutils literal notranslate"><span class="pre">/usr/bin</span></code>, for example). And some methods of sharing a script (for example,
publishing it on a text file sharing service like Githubs gist, or a corporate
intranet) may not allow for deriving the location of an associated requirements
file from the scripts location (tools like <code class="docutils literal notranslate"><span class="pre">pipx</span></code> support running a script
directly from a URL, so “download and unpack a zip of the script and its
dependencies” may not be an appropriate requirement).</p>
<p>Essentially, though, the issue here is that there is an explicitly stated
requirement that the format supports storing dependency data <em>in the script file
itself</em>. Solutions that dont do that are simply ignoring that requirement.</p>
</section>
<section id="should-scripts-be-able-to-specify-a-package-index">
<h3><a class="toc-backref" href="#should-scripts-be-able-to-specify-a-package-index" role="doc-backlink">Should scripts be able to specify a package index?</a></h3>
<p>Dependency metadata is about <em>what</em> package the code depends on, and not <em>where</em>
that package comes from. There is no difference here between metadata for
scripts, and metadata for distribution packages (as defined in
<code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>). In both cases, dependencies are given in “abstract” form,
without specifying how they are obtained.</p>
<p>Some tools that use the dependency information may, of course, need to locate
concrete dependency artifacts - for example if they expect to create an
environment containing those dependencies. But the way they choose to do that
will be closely linked to the tools UI in general, and this PEP does not try to
dictate the UI for tools.</p>
<p>There is more discussion of this point, and in particular of the UI choices made
by the <code class="docutils literal notranslate"><span class="pre">pip-run</span></code> tool, in <a class="reference external" href="https://github.com/jaraco/pip-run/issues/44">the previously mentioned pip-run issue</a>.</p>
</section>
<section id="what-about-local-dependencies">
<h3><a class="toc-backref" href="#what-about-local-dependencies" role="doc-backlink">What about local dependencies?</a></h3>
<p>These can be handled without needing special metadata and tooling, simply by
adding the location of the dependencies to <code class="docutils literal notranslate"><span class="pre">sys.path</span></code>. This PEP simply isnt
needed for this case. If, on the other hand, the “local dependencies” are actual
distributions which are published locally, they can be specified as usual with a
<a class="pep reference internal" href="../pep-0508/" title="PEP 508 Dependency specification for Python Software Packages">PEP 508</a> requirement, and the local package index specified when running a
tool by using the tools UI for that.</p>
</section>
</section>
<section id="open-issues">
<h2><a class="toc-backref" href="#open-issues" role="doc-backlink">Open Issues</a></h2>
<p>None at this point.</p>
</section>
<section id="copyright">
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
<p>This document is placed in the public domain or under the
CC0-1.0-Universal license, whichever is more permissive.</p>
</section>
</section>
<hr class="docutils" />
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0722.rst">https://github.com/python/peps/blob/main/peps/pep-0722.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0722.rst">2023-10-21 10:30:17 GMT</a></p>
</article>
<nav id="pep-sidebar">
<h2>Contents</h2>
<ul>
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#motivation">Motivation</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#example">Example</a></li>
</ul>
</li>
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li>
<li><a class="reference internal" href="#security-implications">Security Implications</a></li>
<li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li>
<li><a class="reference internal" href="#recommendations">Recommendations</a></li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
<li><a class="reference internal" href="#why-not-include-other-metadata">Why not include other metadata?</a></li>
<li><a class="reference internal" href="#why-not-use-a-marker-per-line">Why not use a marker per line?</a></li>
<li><a class="reference internal" href="#why-not-use-a-distinct-form-of-comment-for-the-dependency-block">Why not use a distinct form of comment for the dependency block?</a></li>
<li><a class="reference internal" href="#why-not-allow-multiple-dependency-blocks-and-merge-them">Why not allow multiple dependency blocks and merge them?</a></li>
<li><a class="reference internal" href="#why-not-use-a-more-standard-data-format-e-g-toml">Why not use a more standard data format (e.g., TOML)?</a></li>
<li><a class="reference internal" href="#why-not-use-possibly-restricted-python-syntax">Why not use (possibly restricted) Python syntax?</a></li>
<li><a class="reference internal" href="#why-not-embed-a-pyproject-toml-file-in-the-script">Why not embed a <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code> file in the script?</a></li>
<li><a class="reference internal" href="#why-not-infer-the-requirements-from-import-statements">Why not infer the requirements from import statements?</a></li>
<li><a class="reference internal" href="#why-not-simply-manage-the-environment-at-runtime">Why not simply manage the environment at runtime?</a></li>
<li><a class="reference internal" href="#why-not-just-set-up-a-python-project-with-a-pyproject-toml">Why not just set up a Python project with a <code class="docutils literal notranslate"><span class="pre">pyproject.toml</span></code>?</a></li>
<li><a class="reference internal" href="#why-not-use-a-requirements-file-for-dependencies">Why not use a requirements file for dependencies?</a></li>
<li><a class="reference internal" href="#should-scripts-be-able-to-specify-a-package-index">Should scripts be able to specify a package index?</a></li>
<li><a class="reference internal" href="#what-about-local-dependencies">What about local dependencies?</a></li>
</ul>
</li>
<li><a class="reference internal" href="#open-issues">Open Issues</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
<br>
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0722.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>