feat(venv): add relocatable flag (#5515)

## Summary

Adds a `--relocatable` CLI arg to `uv venv`. This flag does two things:

* ensures that the associated activation scripts do not rely on a
hardcoded
absolute path to the virtual environment (to the extent possible; `.csh`
and
  `.nu` left as-is)
* persists a `relocatable` flag in `pyvenv.cfg`.

The flag in `pyvenv.cfg` in turn instructs the wheel `Installer` to
create script
entrypoints in a relocatable way (use `exec` trick + `dirname $0` on
POSIX;
use relative path to `python[w].exe` on Windows).

Fixes: #3863

## Test Plan

* Relocatable console scripts covered as additional scenarios in
existing test cases.
* Integration testing of boilerplate generation in `venv`.
* Manual testing of `uv venv` with and without `--relocatable`
This commit is contained in:
Pavel Dikov 2024-07-29 01:10:11 +01:00 committed by GitHub
parent 3626d08cca
commit cb47aed9de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 299 additions and 26 deletions

View file

@ -50,6 +50,7 @@ pub(crate) fn create(
prompt: Prompt,
system_site_packages: bool,
allow_existing: bool,
relocatable: bool,
) -> Result<VirtualEnvironment, Error> {
// Determine the base Python executable; that is, the Python executable that should be
// considered the "base" for the virtual environment. This is typically the Python executable
@ -294,12 +295,27 @@ pub(crate) fn create(
.map(|path| path.simplified().to_str().unwrap().replace('\\', "\\\\"))
.join(path_sep);
let activator = template
.replace(
"{{ VIRTUAL_ENV_DIR }}",
let virtual_env_dir = match (relocatable, name.to_owned()) {
(true, "activate") => {
// Extremely verbose, but should cover all major POSIX shells,
// as well as platforms where `readlink` does not implement `-f`.
r#"'"$(dirname -- "$(CDPATH= cd -- "$(dirname -- ${BASH_SOURCE[0]:-${(%):-%x}})" && echo "$PWD")")"'"#
}
(true, "activate.bat") => r"%~dp0..",
(true, "activate.fish") => {
r#"'"$(dirname -- "$(cd "$(dirname -- "$(status -f)")"; and pwd)")"'"#
}
// Note:
// * relocatable activate scripts appear not to be possible in csh and nu shell
// * `activate.ps1` is already relocatable by default.
_ => {
// SAFETY: `unwrap` is guaranteed to succeed because `location` is an `Utf8PathBuf`.
location.simplified().to_str().unwrap(),
)
location.simplified().to_str().unwrap()
}
};
let activator = template
.replace("{{ VIRTUAL_ENV_DIR }}", virtual_env_dir)
.replace("{{ BIN_NAME }}", bin_name)
.replace(
"{{ VIRTUAL_PROMPT }}",
@ -335,6 +351,14 @@ pub(crate) fn create(
"false".to_string()
},
),
(
"relocatable".to_string(),
if relocatable {
"true".to_string()
} else {
"false".to_string()
},
),
];
if let Some(prompt) = prompt {