Files
uv/crates/uv-virtualenv/src/lib.rs
T
Pavel Dikov cb47aed9de 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`
2024-07-29 00:10:11 +00:00

71 lines
2.0 KiB
Rust

use std::io;
use std::path::Path;
use thiserror::Error;
use platform_tags::PlatformError;
use uv_python::{Interpreter, PythonEnvironment};
mod virtualenv;
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
Io(#[from] io::Error),
#[error("Failed to determine Python interpreter to use")]
Discovery(#[from] uv_python::DiscoveryError),
#[error("Failed to determine Python interpreter to use")]
InterpreterNotFound(#[from] uv_python::PythonNotFound),
#[error(transparent)]
Platform(#[from] PlatformError),
#[error("Could not find a suitable Python executable for the virtual environment based on the interpreter: {0}")]
NotFound(String),
}
/// The value to use for the shell prompt when inside a virtual environment.
#[derive(Debug)]
pub enum Prompt {
/// Use the current directory name as the prompt.
CurrentDirectoryName,
/// Use the fixed string as the prompt.
Static(String),
/// Default to no prompt. The prompt is then set by the activator script
/// to the virtual environment's directory name.
None,
}
impl Prompt {
/// Determine the prompt value to be used from the command line arguments.
pub fn from_args(prompt: Option<String>) -> Self {
match prompt {
Some(prompt) if prompt == "." => Self::CurrentDirectoryName,
Some(prompt) => Self::Static(prompt),
None => Self::None,
}
}
}
/// Create a virtualenv.
pub fn create_venv(
location: &Path,
interpreter: Interpreter,
prompt: Prompt,
system_site_packages: bool,
allow_existing: bool,
relocatable: bool,
) -> Result<PythonEnvironment, Error> {
// Create the virtualenv at the given location.
let virtualenv = virtualenv::create(
location,
&interpreter,
prompt,
system_site_packages,
allow_existing,
relocatable,
)?;
// Create the corresponding `PythonEnvironment`.
let interpreter = interpreter.with_virtualenv(virtualenv);
Ok(PythonEnvironment::from_interpreter(interpreter))
}