445 lines
38 KiB
HTML
445 lines
38 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 582 – Python local packages directory | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0582/">
|
||
<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 582 – Python local packages directory | peps.python.org'>
|
||
<meta property="og:description" content="This PEP proposes extending the existing mechanism for setting up sys.path to include a new __pypackages__ directory, in addition to the existing locations. The new directory will be added at the start of sys.path, after the current working directory an...">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0582/">
|
||
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
|
||
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
|
||
<meta property="og:image:alt" content="Python PEPs">
|
||
<meta property="og:image:width" content="200">
|
||
<meta property="og:image:height" content="200">
|
||
<meta name="description" content="This PEP proposes extending the existing mechanism for setting up sys.path to include a new __pypackages__ directory, in addition to the existing locations. The new directory will be added at the start of sys.path, after the current working directory an...">
|
||
<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 582</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 582 – Python local packages directory</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Kushal Das <mail at kushaldas.in>, Steve Dower <steve.dower at python.org>,
|
||
Donald Stufft <donald at stufft.io>, Alyssa Coghlan <ncoghlan at gmail.com></dd>
|
||
<dt class="field-even">Discussions-To<span class="colon">:</span></dt>
|
||
<dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/pep-582-python-local-packages-directory/963/">Discourse thread</a></dd>
|
||
<dt class="field-odd">Status<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><abbr title="Formally declined and will not be accepted">Rejected</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-May-2018</dd>
|
||
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
|
||
<dd class="field-odd">3.12</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/pep-582-python-local-packages-directory/963" title="Discourse thread">01-Mar-2019</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-582-python-local-packages-directory/963/430">Discourse message</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="#relationship-to-virtual-environments">Relationship to virtual environments</a></li>
|
||
<li><a class="reference internal" href="#security-considerations">Security Considerations</a></li>
|
||
<li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li>
|
||
<li><a class="reference internal" href="#impact-on-tools">Impact on Tools</a></li>
|
||
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a><ul>
|
||
<li><a class="reference internal" href="#impact-on-other-python-implementations">Impact on other Python implementations</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
|
||
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
</details></section>
|
||
<section id="abstract">
|
||
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
|
||
<p>This PEP proposes extending the existing mechanism for setting up <code class="docutils literal notranslate"><span class="pre">sys.path</span></code>
|
||
to include a new <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> directory, in addition to the existing
|
||
locations. The new directory will be added at the start of <code class="docutils literal notranslate"><span class="pre">sys.path</span></code>, after
|
||
the current working directory and just before the system site-packages, to give
|
||
packages installed there priority over other locations.</p>
|
||
<p>This is similar to the existing mechanism of adding the current directory (or
|
||
the directory the script is located in), but by using a subdirectory,
|
||
additional libraries are kept separate from the user’s work.</p>
|
||
</section>
|
||
<section id="motivation">
|
||
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
|
||
<p>New Python programmers can benefit from being taught the value of isolating an
|
||
individual project’s dependencies from their system environment. However, the
|
||
existing mechanism for doing this, virtual environments, is known to be complex
|
||
and error-prone for beginners to understand. Explaining virtual environments is
|
||
often a distraction when trying to get a group of beginners set up - differences
|
||
in platform and shell environments require individual assistance, and the need
|
||
for activation in every new shell session makes it easy for students to make
|
||
mistakes when coming back to work after a break. This proposal offers a lightweight
|
||
solution that gives isolation without the user needing to understand more
|
||
advanced concepts.</p>
|
||
<p>Furthermore, standalone Python applications usually need 3rd party libraries to
|
||
function. Typically, they are either designed to be run from a virtual environment,
|
||
where the dependencies are installed into the environment alongside the application,
|
||
or they bundle their dependencies in a subdirectory, and modify <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> at
|
||
application startup. Virtual environments, while a common and effective solution
|
||
(used, for example, by the <code class="docutils literal notranslate"><span class="pre">pipx</span></code> tool), are somewhat awkward to set up and manage,
|
||
and are not relocatable. On the other hand, manual manipulation of <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> is
|
||
boilerplate that developers need to get right, and (being a runtime behaviour)
|
||
it is not understood by tools like linters and type checkers. The <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code>
|
||
proposal formalises the idea of a “bundled dependencies” location, avoiding the
|
||
boilerplate and providing a standard location that development tools can be taught
|
||
to recognise.</p>
|
||
<p>It should be noted that in general, Python libraries cannot be simply copied
|
||
between machines, platforms, or even necessarily between Python versions. This
|
||
proposal does nothing to change that fact, and while it is tempting to assume
|
||
that bundling a script and its <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> is a mechanism for
|
||
distributing applications, this is explicitly <em>not</em> a goal of this proposal.
|
||
Developers remain responsible for the portability of their code.</p>
|
||
</section>
|
||
<section id="rationale">
|
||
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
|
||
<p>While <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> can be manipulated at runtime, the default value is important, as
|
||
it establishes a common baseline that users and tools can agree on. The current default
|
||
does not include a location that could be viewed as “private to the current project”,
|
||
and yet that is a useful concept.</p>
|
||
<p>This is similar to the npm <code class="docutils literal notranslate"><span class="pre">node_modules</span></code> directory, which is popular in the
|
||
Javascript community, and something that developers familiar with that
|
||
ecosystem often ask for from Python.</p>
|
||
</section>
|
||
<section id="specification">
|
||
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
|
||
<p>This PEP proposes to add a new step in the process of calculating <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> at
|
||
startup.</p>
|
||
<p>When the interactive interpreter starts, if a <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> directory is
|
||
found in the current working directory, then it will be included in
|
||
<code class="docutils literal notranslate"><span class="pre">sys.path</span></code> after the entry for current working directory and just before the
|
||
system site-packages.</p>
|
||
<p>When the interpreter runs a script, Python will try to find <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code>
|
||
in the same directory as the script. If found (along with the current Python
|
||
version directory inside), then it will be used, otherwise Python will behave
|
||
as it does currently.</p>
|
||
<p>The behaviour should work exactly the same as the way the existing mechanism
|
||
for adding the current working directory or script directory to <code class="docutils literal notranslate"><span class="pre">sys.path</span></code>
|
||
works. For example, <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> will be ignored if the <code class="docutils literal notranslate"><span class="pre">-P</span></code> option or
|
||
the <code class="docutils literal notranslate"><span class="pre">PYTHONSAFEPATH</span></code> environment variable is set.</p>
|
||
<p>In order to be recognised, the <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> directory must be laid out
|
||
according to a new <code class="docutils literal notranslate"><span class="pre">localpackages</span></code> scheme in the sysconfig module.
|
||
Specifically, both of the <code class="docutils literal notranslate"><span class="pre">purelib</span></code> and <code class="docutils literal notranslate"><span class="pre">platlib</span></code> directories must be
|
||
present, using the following code to determine the locations of those
|
||
directories:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">scheme</span> <span class="o">=</span> <span class="s2">"localpackages"</span>
|
||
<span class="n">purelib</span> <span class="o">=</span> <span class="n">sysconfig</span><span class="o">.</span><span class="n">get_path</span><span class="p">(</span><span class="s2">"purelib"</span><span class="p">,</span> <span class="n">scheme</span><span class="p">,</span> <span class="nb">vars</span><span class="o">=</span><span class="p">{</span><span class="s2">"base"</span><span class="p">:</span> <span class="s2">"__pypackages__"</span><span class="p">,</span> <span class="s2">"platbase"</span><span class="p">:</span> <span class="s2">"__pypackages__"</span><span class="p">})</span>
|
||
<span class="n">platlib</span> <span class="o">=</span> <span class="n">sysconfig</span><span class="o">.</span><span class="n">get_path</span><span class="p">(</span><span class="s2">"platlib"</span><span class="p">,</span> <span class="n">scheme</span><span class="p">,</span> <span class="nb">vars</span><span class="o">=</span><span class="p">{</span><span class="s2">"base"</span><span class="p">:</span> <span class="s2">"__pypackages__"</span><span class="p">,</span> <span class="s2">"platbase"</span><span class="p">:</span> <span class="s2">"__pypackages__"</span><span class="p">})</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>These two locations will be added to <code class="docutils literal notranslate"><span class="pre">sys.path</span></code>, other directories or
|
||
files in the <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> directory will be silently ignored. The
|
||
paths will be based on Python versions.</p>
|
||
<div class="admonition note">
|
||
<p class="admonition-title">Note</p>
|
||
<p>There is a possible option of having a separate new API, it is documented at <a class="reference external" href="https://github.com/python/peps/issues/3013">issue #3013</a>.</p>
|
||
</div>
|
||
<section id="example">
|
||
<h3><a class="toc-backref" href="#example" role="doc-backlink">Example</a></h3>
|
||
<p>The following shows an example project directory structure, and different ways
|
||
the Python executable and any script will behave. The example is for Unix-like
|
||
systems - on Windows the subdirectories will be different.</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">foo</span>
|
||
<span class="n">__pypackages__</span>
|
||
<span class="n">lib</span>
|
||
<span class="n">python3</span><span class="mf">.10</span>
|
||
<span class="n">site</span><span class="o">-</span><span class="n">packages</span>
|
||
<span class="n">bottle</span>
|
||
<span class="n">myscript</span><span class="o">.</span><span class="n">py</span>
|
||
|
||
<span class="o">/></span> <span class="n">python</span> <span class="n">foo</span><span class="o">/</span><span class="n">myscript</span><span class="o">.</span><span class="n">py</span>
|
||
<span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s1">'foo'</span>
|
||
<span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s1">'foo/__pypackages__/lib/python3.10/site-packages/'</span>
|
||
|
||
|
||
<span class="n">cd</span> <span class="n">foo</span>
|
||
|
||
<span class="n">foo</span><span class="o">></span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="nb">bin</span><span class="o">/</span><span class="n">ansible</span>
|
||
<span class="c1">#! /usr/bin/env python3</span>
|
||
<span class="n">foo</span><span class="o">></span> <span class="n">python</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="nb">bin</span><span class="o">/</span><span class="n">ansible</span>
|
||
|
||
<span class="n">foo</span><span class="o">></span> <span class="n">python</span> <span class="n">myscript</span><span class="o">.</span><span class="n">py</span>
|
||
|
||
<span class="n">foo</span><span class="o">></span> <span class="n">python</span>
|
||
<span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s1">'.'</span>
|
||
<span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s1">'./__pypackages__/lib/python3.10/site-packages'</span>
|
||
|
||
<span class="n">foo</span><span class="o">></span> <span class="n">python</span> <span class="o">-</span><span class="n">m</span> <span class="n">bottle</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We have a project directory called <code class="docutils literal notranslate"><span class="pre">foo</span></code> and it has a <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code>
|
||
inside of it. We have <code class="docutils literal notranslate"><span class="pre">bottle</span></code> installed in that
|
||
<code class="docutils literal notranslate"><span class="pre">__pypackages__/lib/python3.10/site-packages/</span></code>, and have a <code class="docutils literal notranslate"><span class="pre">myscript.py</span></code>
|
||
file inside of the project directory. We have used whatever tool we generally
|
||
use to install <code class="docutils literal notranslate"><span class="pre">bottle</span></code> in that location.</p>
|
||
<p>For invoking a script, Python will try to find a <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> inside of
|
||
the directory that the script resides <a class="footnote-reference brackets" href="#id2" id="id1">[1]</a>, <code class="docutils literal notranslate"><span class="pre">/usr/bin</span></code>. The same will happen
|
||
in case of the last example, where we are executing <code class="docutils literal notranslate"><span class="pre">/usr/bin/ansible</span></code> from
|
||
inside of the <code class="docutils literal notranslate"><span class="pre">foo</span></code> directory. In both cases, it will <strong>not</strong> use the
|
||
<code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> in the current working directory.</p>
|
||
<p>Similarly, if we invoke <code class="docutils literal notranslate"><span class="pre">myscript.py</span></code> from the first example, it will use the
|
||
<code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> directory that was in the <code class="docutils literal notranslate"><span class="pre">foo</span></code> directory.</p>
|
||
<p>If we go inside of the <code class="docutils literal notranslate"><span class="pre">foo</span></code> directory and start the Python executable (the
|
||
interpreter), it will find the <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> directory inside of the
|
||
current working directory and use it in the <code class="docutils literal notranslate"><span class="pre">sys.path</span></code>. The same happens if we
|
||
try to use the <code class="docutils literal notranslate"><span class="pre">-m</span></code> and use a module. In our example, <code class="docutils literal notranslate"><span class="pre">bottle</span></code> module will
|
||
be found inside of the <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> directory.</p>
|
||
<p>The above two examples are only cases where <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> from current
|
||
working directory is used.</p>
|
||
<p>In another example scenario, a trainer of a Python class can say “Today we are
|
||
going to learn how to use Twisted! To start, please checkout our example
|
||
project, go to that directory, and then run a given command to install Twisted.”</p>
|
||
<p>That will install Twisted into a directory separate from <code class="docutils literal notranslate"><span class="pre">python3</span></code>. There’s no
|
||
need to discuss virtual environments, global versus user installs, etc. as the
|
||
install will be local by default. The trainer can then just keep telling them to
|
||
use <code class="docutils literal notranslate"><span class="pre">python3</span></code> without any activation step, etc.</p>
|
||
<aside class="footnote-list brackets">
|
||
<aside class="footnote brackets" id="id2" role="doc-footnote">
|
||
<dt class="label" id="id2">[<a href="#id1">1</a>]</dt>
|
||
<dd>In the case of symlinks, it is the directory where the actual script
|
||
resides, not the symlink pointing to the script</aside>
|
||
</aside>
|
||
</section>
|
||
</section>
|
||
<section id="relationship-to-virtual-environments">
|
||
<h2><a class="toc-backref" href="#relationship-to-virtual-environments" role="doc-backlink">Relationship to virtual environments</a></h2>
|
||
<p>At its heart, this proposal is simply to modify the calculation of the default
|
||
value of <code class="docutils literal notranslate"><span class="pre">sys.path</span></code>, and does not relate at all to the virtual environment
|
||
mechanism. However, <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> can be viewed as providing an isolation
|
||
capability, and in that sense, it “competes” with virtual environments.</p>
|
||
<p>However, there are significant differences:</p>
|
||
<blockquote>
|
||
<div><ul class="simple">
|
||
<li>Virtual environments are isolated from the system environment, whereas
|
||
<code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> simply adds to the system environment.</li>
|
||
<li>Virtual environments include a full “installation scheme”, with directories
|
||
for binaries, C header files, etc., whereas <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> is solely
|
||
for Python library code.</li>
|
||
<li>Virtual environments work most smoothly when “activated”. This proposal
|
||
needs no activation.</li>
|
||
</ul>
|
||
</div></blockquote>
|
||
<p>This proposal should be seen as independent of virtual environments, not competing
|
||
with them. At best, some use cases currently only served by virtual environments
|
||
can also be served (possibly better) by <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code>.</p>
|
||
<p>It should be noted that libraries installed in <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> will be visible
|
||
in a virtual environment. This arguably breaks the isolation of virtual environments,
|
||
but it is no different in principle to the presence of the current directory on
|
||
<code class="docutils literal notranslate"><span class="pre">sys.path</span></code> (or mechanisms like the <code class="docutils literal notranslate"><span class="pre">PYTHONPATH</span></code> environment variable). The only
|
||
difference is in degree, as the expectation is that people will more commonly install
|
||
packages in <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code>. The alternative would be to explicitly detect virtual
|
||
environments and disable <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> in that case - however that would break
|
||
scripts with bundled dependencies. The PEP authors believe that developers using
|
||
virtual environments should be experienced enough to understand the issue and
|
||
anticipate and avoid any problems.</p>
|
||
</section>
|
||
<section id="security-considerations">
|
||
<h2><a class="toc-backref" href="#security-considerations" role="doc-backlink">Security Considerations</a></h2>
|
||
<p>In theory, it is possible to add a library to the <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> directory
|
||
that overrides a stdlib module or an installed 3rd party library. For the
|
||
<code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> associated with a script, this is assumed not to be a
|
||
significant issue, as it is unlikely that anyone would be able to write to
|
||
<code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> unless they also had the ability to write to the script itself.</p>
|
||
<p>For a <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> directory in the current working directory, the
|
||
interactive interpreter could be affected. However, this is not significantly
|
||
different than the existing issue of someone having a <code class="docutils literal notranslate"><span class="pre">math.py</span></code> module in their
|
||
current directory, and while (just like that case) it can cause user confusion,
|
||
it does not introduce any new security implications.</p>
|
||
<p>When running a script, any <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> directory in the current working
|
||
directory is ignored. This is the same approach Python uses for adding the
|
||
current working directory to <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> and ensures that it is not possible
|
||
to change the behaviour of a script by modifying files in the current
|
||
directory.</p>
|
||
<p>Also, a <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> directory is only recognised in the current (or
|
||
script) directory. The interpreter will <em>not</em> scan for <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> in
|
||
parent directories. Doing so would open up the risk of security issues if
|
||
directory permissions on parents differ. In particular, scripts in the <code class="docutils literal notranslate"><span class="pre">bin</span></code>
|
||
directory or <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> (the <code class="docutils literal notranslate"><span class="pre">scripts</span></code> location in <code class="docutils literal notranslate"><span class="pre">sysconfig</span></code>
|
||
terms) have no special access to the libraries installed in <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code>.
|
||
Putting executable scripts in a <code class="docutils literal notranslate"><span class="pre">bin</span></code> directory is not supported by this
|
||
proposal.</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 original motivation for this proposal was to make it easier to teach Python
|
||
to beginners. To that end, it needs to be easy to explain, and simple to use.</p>
|
||
<p>At the most basic level, this is similar to the existing mechanism where the
|
||
script directory is added to <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> and can be taught in a similar manner.
|
||
However, for its intended use of “lightweight isolation”, it would likely be taught
|
||
in terms of “things you put in a <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> directory are private to your
|
||
script”. The experience of the PEP authors suggests that this would be significantly
|
||
easier to teach than the current alternative of introducing virtual environments.</p>
|
||
</section>
|
||
<section id="impact-on-tools">
|
||
<h2><a class="toc-backref" href="#impact-on-tools" role="doc-backlink">Impact on Tools</a></h2>
|
||
<p>As the intended use of the feature is to install 3rd party libraries in the new
|
||
directory, it is important that tools, particularly installers, understand how to
|
||
manage <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code>.</p>
|
||
<p>It is hoped that tools will introduce a dedicated “pypackages” installation
|
||
mode that <em>is</em> guaranteed to match the expected layout in all cases. However,
|
||
the question of how best to support the <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> layout is ultimately
|
||
left to individual tool maintainers to consider and decide on.</p>
|
||
<p>Tools that locate packages without actually running Python code (IDEs, linters,
|
||
type checkers, etc.) would need updating to recognise <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code>. In the
|
||
absence of such updates, the <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> directory would work similarly
|
||
to directories currently added to <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> at runtime (i.e., the tool would
|
||
probably ignore it).</p>
|
||
</section>
|
||
<section id="backwards-compatibility">
|
||
<h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2>
|
||
<p>The directory name <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> was chosen because it is unlikely to be in
|
||
common use. It is true that users who have chosen to use that name for their own
|
||
purposes will be impacted, but at the time this PEP was written, this was viewed
|
||
as a relatively low risk.</p>
|
||
<p>Unfortunately, in the time this PEP has been under discussion, a number of tools
|
||
have chosen to implement variations on what is being proposed here, which are not
|
||
all compatible with the final form of the PEP. As a result, the risk of clashes is
|
||
now higher than originally anticipated.</p>
|
||
<p>It would be possible to mitigate this by choosing a <em>different</em> name, hopefully as
|
||
uncommon as <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> originally was. But realistically, any compatibility
|
||
issues can be viewed as simply the consequences of people trying to implement
|
||
draft proposals, without making the effort to track changes in the proposal. As such,
|
||
it seems reasonable to retain the <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> name, and put the burden of
|
||
addressing the compatibility issue on the tools that implemented the draft version.</p>
|
||
<section id="impact-on-other-python-implementations">
|
||
<h3><a class="toc-backref" href="#impact-on-other-python-implementations" role="doc-backlink">Impact on other Python implementations</a></h3>
|
||
<p>Other Python implementations will need to replicate the new behavior of the
|
||
interpreter bootstrap, including locating the <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> directory and
|
||
adding it the <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> just before site packages, if it is present. This is
|
||
no different to any other Python change.</p>
|
||
</section>
|
||
</section>
|
||
<section id="reference-implementation">
|
||
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
|
||
<p><a class="reference external" href="https://github.com/kushaldas/pep582">Here</a> is a small script which will
|
||
enable the implementation for <code class="docutils literal notranslate"><span class="pre">Cpython</span></code> & in <code class="docutils literal notranslate"><span class="pre">PyPy</span></code>.</p>
|
||
</section>
|
||
<section id="rejected-ideas">
|
||
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2>
|
||
<ul class="simple">
|
||
<li>Alternative names, such as <code class="docutils literal notranslate"><span class="pre">__pylocal__</span></code> and <code class="docutils literal notranslate"><span class="pre">python_modules</span></code>. Ultimately, the name is arbitrary and the chosen name is good enough.</li>
|
||
<li>Additional features of virtual environments. This proposal is not a replacement for virtual environments, and such features are therefore out of scope.</li>
|
||
<li>We will not scan any parent directory to find <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code>. If we want to execute scripts inside of the <code class="docutils literal notranslate"><span class="pre">~/bin/</span></code> directory, then the <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> directory must be inside of the <code class="docutils literal notranslate"><span class="pre">~/bin/</span></code> directory. Doing any such scan for <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> (for the interpreter or a script) will have security implications and also increase startup time.</li>
|
||
<li>Raise an error if unexpected files or directories are present in <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code>. This is considered too strict, particularly as transitional approaches like <code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">--prefix</span></code> can create additional files in <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code>.</li>
|
||
<li>Using a different <code class="docutils literal notranslate"><span class="pre">sysconfig</span></code> scheme, or a dedicated <code class="docutils literal notranslate"><span class="pre">pypackages</span></code> scheme. While this is attractive in theory, it makes transition harder, as there will be no readily-available way of installing to <code class="docutils literal notranslate"><span class="pre">__pypackages__</span></code> until tools implement explicit support. And while the PEP authors hope and assume that such support would be added, having the proposal dependent on such support in order to be usable seems like an unacceptable risk.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="copyright">
|
||
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
|
||
<p>This document has been placed in the public domain.</p>
|
||
</section>
|
||
</section>
|
||
<hr class="docutils" />
|
||
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0582.rst">https://github.com/python/peps/blob/main/peps/pep-0582.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0582.rst">2024-08-20 10:29:32 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="#relationship-to-virtual-environments">Relationship to virtual environments</a></li>
|
||
<li><a class="reference internal" href="#security-considerations">Security Considerations</a></li>
|
||
<li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li>
|
||
<li><a class="reference internal" href="#impact-on-tools">Impact on Tools</a></li>
|
||
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a><ul>
|
||
<li><a class="reference internal" href="#impact-on-other-python-implementations">Impact on other Python implementations</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
|
||
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</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-0582.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> |