Skip to content

Commit b50a80c

Browse files
committed
address review comments: better split and organize rationale/motivation
1 parent 68dd5e0 commit b50a80c

File tree

1 file changed

+100
-50
lines changed

1 file changed

+100
-50
lines changed

peps/pep-0796.rst

Lines changed: 100 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -24,55 +24,24 @@ become more portable.
2424
Motivation
2525
==========
2626

27-
There are two main motivations for allowing relative paths in ``pyvenv.cfg``.
28-
29-
First, it is currently prescribed that the ``home`` value in ``pyvenv.cfg`` be
30-
an absolute path (`gh-135773`). The behavior of relative paths is unspecified. While
31-
techniques exist to work around this for every other sub-part of a virtual
32-
environment, the one remaining part without a tenable solution is how the
33-
Python runtime itself finds ``PYTHONHOME``. This is because, currently, the
34-
startup process requires absolute paths be used for the ``home`` key in
35-
``pyvenv.cfg``. If a relative path is used, behavior is unspecified (the
36-
current implementation ends up making it relative to the process's current
37-
working directory, making it untenable to use).
38-
39-
This requirement is overly proscriptive and restrictive because, given a known
40-
anchor point, it's easy to transform a relative path to an absolute path and
41-
still retain predictable and reliable behavior. Thus, the absolute path
42-
requirement should be relaxed and relative path behavior allowed and defined.
43-
44-
Second, such relative paths are a building block to enable portable virtual
45-
environments, i.e. copying a virtual environment as-is between hosts of
46-
compatible platforms. For example, by pointing to a parent directory, the
47-
virtual environment becomes independent of path prefix differences between
48-
hosts (e.g. ``/usr/local/`` in a container vs
49-
``/home/user/.pyenv/versions/3.12.0/bin`` in a user's dev environment).
50-
51-
Portable virtual environments are appealing because virtual environments are a
52-
popular mechanism for running Python applications. This provides several
53-
benefits:
54-
55-
* The closer the development environment is to the non-development environment,
56-
environment-specific issues are less likely, and the easier it is to
57-
reproduce issues.
58-
* The simpler the process of re-creating the environment, environment-specific
59-
issues are less likely, and the faster the process can be.
60-
61-
Making it simpler to copy a virtual environment from one host to another
62-
mitigates these categories of problems. Additionally, the development tools to
63-
create a virtual environment and install its dependencies aren't needed on the
64-
host that intends to run the program.
65-
66-
When the virtual environment doesn't require modifications to be usable, it
67-
also allows more advanced deployment mechanisms, e.g. remote mounting and
68-
caching of artifacts. While this PEP on its own isn't sufficient to enable
69-
that, it allows tools like Bazel or venvstacks to more easily prepare
70-
constrained environments that allow for such use cases.
71-
72-
Rationale
73-
=========
74-
75-
The reason support for relative virtual environments needs to be
27+
The ``home`` field in :file:`pyvenv.cfg` is used on interpreter startup to
28+
determine the actual Python interpreter installation that is used to execute
29+
code in that virtual environment. Currently, this path is required to be
30+
absolute for correct virtual environment operation because the original
31+
`PEP 405 <https://peps.python.org/pep-0405/>`__
32+
specifying virtual environments didn't cover any specific way of processing
33+
relative paths, their behaviour is implementation dependent. CPython releases
34+
up to and including CPython 3.14 resolve them relative to the current process
35+
working directory, making them too unreliable to use in practice.
36+
37+
The reason to support a relative path is to support portable virtual
38+
environments, which rely on using a host-agnostic relative path to point to
39+
``PYTHONHOME``.
40+
A portable virtual environment is one that can be moved between
41+
platform-compatible hosts, which is an important feature for some projects (see
42+
"Why portable environments matter").
43+
44+
The reason support for a relative ``home`` path needs to be
7645
in the interpreter itself is because locating ``PYTHONHOME`` happens
7746
very early in the interpreter startup process, which limits the options for
7847
customizing how it's computed. Without the ability to specify where the
@@ -84,7 +53,66 @@ machines do so either by relying on undocumented interpreter behaviour
8453
(Bazel, omitting the ``home`` key entirely to trigger an implementation
8554
dependent fallback to resolving via a symlinked interpreter binary on
8655
non-Windows systems, see `gh-135773`) or by requiring a post-installation script to be executed
87-
after the environment is placed in its target location (venvstacks).
56+
after the environment is placed in its target location (
57+
`venvstacks <https://lmstudio.ai/blog/venvstacks#publishing-environment-layer-archives>`__
58+
).
59+
60+
While this PEP on its own isn't sufficient to enable portable virtual
61+
environments, it allows tools like Bazel or venvstacks to more easily prepare
62+
constrained environments that allow for such use cases.
63+
64+
Why portable virtual environments matter
65+
----------------------------------------
66+
67+
Portable virtual environments are important for the efficiency and
68+
reproducibility benefits they bring from being created once and reused multiple
69+
times later in different locations. For example, a build farm can build a
70+
virtual environment once, cache it, and then re-use it as-is to CI jobs.
71+
72+
73+
Rationale
74+
=========
75+
76+
Defining semantics for a relative ``home`` path is the chosen design for the
77+
following reasons.
78+
79+
First, it is a small change to interpreter startup, in particular of an
80+
unreliable behavior that isn't specified. Currently, relative paths are
81+
resolved to the process's current working directory, which makes them
82+
unreliable for use in practice.
83+
84+
Second, for portable virtual environments, relative paths allow more
85+
efficient, simple, and correct reproduction of virtual environments between
86+
hosts. This is because they can be copied as-is to different locations. Some
87+
example capabilities this allows are:
88+
89+
* A build farm creating (and caching) a virtual environment, which is then
90+
served to developers (e.g. Bazel).
91+
* Composing virtual environments together (e.g. venvstacks).
92+
* Installing multiple arbitrary virtual environments into a container to
93+
save disk space.
94+
* Layering a virtual environment atop a container image for faster image
95+
building.
96+
* Not needing virtual environment creation tools on the host that uses a
97+
virtual environment.
98+
* Exact reproduction of an application's virtual environment between a
99+
developer's host and a production host.
100+
101+
Third, requiring an absolute path is inherently overly proscriptive. The
102+
interpreter itself doesn't care whether paths are relative or absolute, merely
103+
that they point to valid locations, so users should be given the ability to use
104+
a path of their choosing. Given a known anchor point, it's easy to transform a
105+
relative path to an absolute path and still retain predictable and reliable
106+
behavior that produces correct values.
107+
108+
Fullying designing portable virtual environments
109+
------------------------------------------------
110+
111+
This PEP purposely only focuses on the interpreter startup behavior to limit
112+
its scope. There are multiple implementations and many design questions for how
113+
to implement portable virtual environments work (e.g. what installers should
114+
do), but they are separate from the Python runtime initialization phase.
115+
88116

89117
Specification
90118
=============
@@ -162,6 +190,22 @@ relative virtual environment paths will typically be aware of the underlying
162190
base runtime Python version, and hence able to update the emitted relative path
163191
accordingly.
164192

193+
Security Implications
194+
=====================
195+
196+
A relative path in :file:`pyvenv.cfg` may resolve differently depending on the
197+
location of the virtual environment. This *could* point to a surprising,
198+
potentially malicious, location.
199+
200+
However, this risk already exists today because a relative path isn't
201+
_rejected_, but resolved relative to the current working directory. This PEP
202+
just changes the anchor point to ``pyvenv.cfg`` itself.
203+
204+
Similarly, the same concern exists for absolute paths. The two are
205+
fundamentally the same because they both rely on trusting whoever created
206+
the ``pyvenv.cfg`` file, which requires having run another tool or downloaded
207+
something from elsewhere.
208+
165209

166210
How to Teach This
167211
=================
@@ -193,6 +237,11 @@ them. These questions are best addressed separately by tool owners.
193237
References
194238
==========
195239

240+
portable virtual environment
241+
A portable virtual environment is one that can be copied from
242+
one host to another that is platform compatible (e.g. same OS, CPU
243+
architecture, etc), with little or no modification or post processing.
244+
196245
* `rules_python <https://github.com/bazel-contrib/rules_python>`__: implements
197246
host-relocatable virtual environments.
198247
* `rules_py <https://github.com/aspect-build/rules_py>`__: implements
@@ -265,6 +314,7 @@ Code generally assumes that any virtual environment will be
265314
automatically detected and activated by the presence of ``pyvenv.cfg``, so
266315
things work better when alterations to the environment aren't a concern.
267316

317+
268318
Copyright
269319
=========
270320

0 commit comments

Comments
 (0)