-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
PEP 796: Relative Virtual Environments #4476
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 35 commits
ceeaa3b
d0a9289
41d2ea8
65a415c
4175b20
bbc85b7
a0c2529
8e6e21e
37202ca
b81c50f
55a5e28
e09b00d
c7a6a9b
bc1ffbd
2301d2a
e14b01f
175f378
31cb756
cd46f4d
b532f5c
99434fc
862142a
1c8fb2d
c542890
3240227
557cb71
0579623
c9388b3
b959f24
5b259cc
560dadb
f41ed51
5cbc9f5
0af0eea
68dd5e0
b50a80c
af8f880
ae5a67f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,272 @@ | ||||||||
| PEP: 796 | ||||||||
| Title: Relative Home Path in Virtual Environments | ||||||||
| Author: Richard Levasseur <[email protected]> | ||||||||
| Sponsor: Alyssa Coghlan <[email protected]> | ||||||||
| Discussions-To: Pending | ||||||||
| Status: Draft | ||||||||
| Type: Standards Track | ||||||||
| Created: 02-Jul-2025 | ||||||||
| Python-Version: 3.15 | ||||||||
|
|
||||||||
|
|
||||||||
| Abstract | ||||||||
| ======== | ||||||||
|
|
||||||||
| This PEP formally specifies the use of a relative path for ``home`` | ||||||||
| in a Python virtual environment's :file:`pyvenv.cfg` file. | ||||||||
|
|
||||||||
| Specifically, we will discuss how such relative paths are understood | ||||||||
| by the Python startup process, including their conversion to absolute | ||||||||
| paths for use by the runtime. | ||||||||
| This is a fundamental building block for virtual environments to | ||||||||
| become more portable. | ||||||||
|
|
||||||||
| Motivation | ||||||||
| ========== | ||||||||
|
|
||||||||
| There are two main motivations for allowing relative paths in ``pyvenv.cfg``. | ||||||||
|
|
||||||||
| First, it is currently prescribed that the ``home`` value in ``pyvenv.cfg`` be | ||||||||
| an absolute path (`gh-135773`). The behavior of relative paths is unspecified. While | ||||||||
| techniques exist to work around this for every other sub-part of a virtual | ||||||||
| environment, the one remaining part without a tenable solution is how the | ||||||||
| Python runtime itself finds ``PYTHONHOME``. This is because, currently, the | ||||||||
| startup process requires absolute paths be used for the ``home`` key in | ||||||||
| ``pyvenv.cfg``. If a relative path is used, behavior is unspecified (the | ||||||||
| current implementation ends up making it relative to the process's current | ||||||||
| working directory, making it untenable to use). | ||||||||
|
|
||||||||
| This requirement is overly proscriptive and restrictive because, given a known | ||||||||
| anchor point, it's easy to transform a relative path to an absolute path and | ||||||||
| still retain predictable and reliable behavior. Thus, the absolute path | ||||||||
| requirement should be relaxed and relative path behavior allowed and defined. | ||||||||
rickeylev marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
|
|
||||||||
| Second, such relative paths are a building block to enable portable virtual | ||||||||
rickeylev marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
| environments, i.e. copying a virtual environment as-is between hosts of | ||||||||
| compatible platforms. For example, by pointing to a parent directory, the | ||||||||
| virtual environment becomes independent of path prefix differences between | ||||||||
| hosts (e.g. ``/usr/local/`` in a container vs | ||||||||
| ``/home/user/.pyenv/versions/3.12.0/bin`` in a user's dev environment). | ||||||||
rickeylev marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
|
|
||||||||
| Portable virtual environments are appealing because virtual environments are a | ||||||||
| popular mechanism for running Python applications. This provides several | ||||||||
| benefits: | ||||||||
|
|
||||||||
| * The closer the development environment is to the non-development environment, | ||||||||
rickeylev marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
| environment-specific issues are less likely, and the easier it is to | ||||||||
| reproduce issues. | ||||||||
StanFromIreland marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
| * The simpler the process of re-creating the environment, environment-specific | ||||||||
| issues are less likely, and the faster the process can be. | ||||||||
|
|
||||||||
| Making it simpler to copy a virtual environment from one host to another | ||||||||
| mitigates these categories of problems. Additionally, the development tools to | ||||||||
| create a virtual environment and install its dependencies aren't needed on the | ||||||||
| host that intends to run the program. | ||||||||
|
|
||||||||
| When the virtual environment doesn't require modifications to be usable, it | ||||||||
| also allows more advanced deployment mechanisms, e.g. remote mounting and | ||||||||
| caching of artifacts. While this PEP on its own isn't sufficient to enable | ||||||||
| that, it allows tools like Bazel or venvstacks to more easily prepare | ||||||||
| constrained environments that allow for such use cases. | ||||||||
ncoghlan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
|
|
||||||||
| Rationale | ||||||||
| ========= | ||||||||
|
|
||||||||
| The reason support for relative virtual environments needs to be | ||||||||
| in the interpreter itself is because locating ``PYTHONHOME`` happens | ||||||||
| very early in the interpreter startup process, which limits the options for | ||||||||
| customizing how it's computed. Without the ability to specify where the | ||||||||
| supporting Python runtime files are, the interpreter can't finish startup, | ||||||||
| so other hook points (e.g. ``site`` initialization) never trigger. | ||||||||
rickeylev marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
|
|
||||||||
rickeylev marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
| Tools that currently look to enable virtual environment portability across | ||||||||
| machines do so either by relying on undocumented interpreter behaviour | ||||||||
| (Bazel, omitting the ``home`` key entirely to trigger an implementation | ||||||||
| dependent fallback to resolving via a symlinked interpreter binary on | ||||||||
| non-Windows systems, see `gh-135773`) or by requiring a post-installation script to be executed | ||||||||
ncoghlan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
| after the environment is placed in its target location (venvstacks). | ||||||||
rickeylev marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
|
|
||||||||
| Specification | ||||||||
| ============= | ||||||||
|
|
||||||||
| The ``home`` value in ``pyvenv.cfg`` is permitted to use a relative path value. | ||||||||
| These may contain parent-directory references outside of the virtual environment root | ||||||||
| directory. | ||||||||
| For example: | ||||||||
|
|
||||||||
| * ``subdir/whatever/bin`` (a directory within the virtual environment) | ||||||||
| * ``./subdir/whatever/bin`` (same as above) | ||||||||
| * ``../../../../../elsewhere/runtime/bin`` (a directory outside the virtual | ||||||||
| environment) | ||||||||
|
|
||||||||
| Relative paths are relative to the directory containing :file:`pyvenv.cfg`. | ||||||||
| During interpreter startup (i.e. :file:`getpath.py`), the relative path is joined to the | ||||||||
| directory containing ``pyvenv.cfg`` to form an absolute path. | ||||||||
| Parent-directory references (``../``) and current | ||||||||
| directory references (``./``) are resolved syntactically (i.e. not resolving | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
(I don't mind if this isn't accepted, I've just always referred to this style of resolution as lexical rather than as syntactic: python/cpython#124825 |
||||||||
| symlinks). Symlinks are *not* resolved prior to construction of the absolute | ||||||||
| path to ensure semantics between a relative path and absolute path remain the | ||||||||
| same. | ||||||||
|
|
||||||||
| For example, given | ||||||||
| ``/home/user/venv/bin/pyvenv.cfg`` with | ||||||||
| ``home = ../../runtime/./bin``, the result is ``home = /home/user/runtime/bin``, | ||||||||
| i.e. it's equivalent to using that value verbatim in ``pyvenv.cfg``. | ||||||||
|
|
||||||||
|
|
||||||||
| CPython Runtime Changes | ||||||||
| ----------------------- | ||||||||
|
|
||||||||
| The CPython runtime itself *almost* already supports relative paths. The | ||||||||
| primitives are there, so the only change needed is to define how it resolves | ||||||||
| relative paths for ``home`` in ``pyvenv.cfg``. | ||||||||
|
|
||||||||
| Currently, relative paths resolve relative to the process's current working | ||||||||
| directory. Because the current working directory isn't knowable in advance, it | ||||||||
| makes relative paths today effectively impossible. | ||||||||
ncoghlan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
|
|
||||||||
| Instead, the paths should be relative to the location of the ``pyvenv.cfg`` | ||||||||
| file. This file is chosen as the anchor point because the tool that creates the | ||||||||
| file also has to know where the Python runtime is, so can easily calculate the | ||||||||
| correct relative path. For tools that read the ``pyvenv.cfg``, it is also easy | ||||||||
| to simply join the directory name of where ``pyvenv.cfg`` was found with the | ||||||||
| path in the config file. When a person reads the config file, they can do | ||||||||
| something similar, which results in a lower cognitive burden and helps avoid | ||||||||
| the question of "relative to what?" | ||||||||
|
|
||||||||
| This change is only a couple of lines in the startup code. Specifically, when | ||||||||
| parsing the ``pyvenv.cfg`` file and finding the ``home`` value, it just needs | ||||||||
| to be checked if it's already absolute. If not, then join it to the directory | ||||||||
| name of the ``pyvenv.cfg`` file. The code already knows the directory and has | ||||||||
| helpers already exist for checking if a path is absolute and joining two | ||||||||
| paths. | ||||||||
|
|
||||||||
| A proof-of-concept of this is implemented in the author's branch, | ||||||||
| `rickeylev/feat.relative.pyvenv.home <https://github.com/python/cpython/compare/main...rickeylev:cpython:feat.relative.pyvenv.home>`__. | ||||||||
|
||||||||
| `rickeylev/feat.relative.pyvenv.home <https://github.com/python/cpython/compare/main...rickeylev:cpython:feat.relative.pyvenv.home>`__. | |
| `rickeylev/feat.relative.pyvenv.home | |
| <https://github.com/python/cpython/compare/main...rickeylev:cpython:feat.relative.pyvenv.home>`__. |
Please could you also wrap some of the other long lines to 79 chars?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be useful to say where you will teach this. What docs will you update?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't an open issue, but rather than intentional omission. Perhaps just include a variant of this paragraph in the rationale subsection about not fully specifying portable virtual environments?
Uh oh!
There was an error while loading. Please reload this page.