Skip to content

Commit

Permalink
Add a test
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Mar 2, 2024
1 parent 181e12b commit 6b1305c
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 3 deletions.
23 changes: 20 additions & 3 deletions crates/uv-virtualenv/src/bare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,28 @@ pub fn create_bare_venv(
system_site_packages: bool,
extra_cfg: Vec<(String, String)>,
) -> Result<Virtualenv, 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
// from the [`Interpreter`]; however, if the interpreter is a virtual environment itself, then
// the base Python executable is the Python executable of the interpreter's base interpreter.
let base_python = if cfg!(unix) {
// On Unix, follow symlinks to resolve the base interpreter, since the Python executable in
// a virtual environment is a symlink to the base interpreter.
fs_err::canonicalize(interpreter.sys_executable())?
} else if cfg!(windows) {
if let Some(base_executable) = interpreter.base_executable() {
base_executable.to_path_buf()
// On Windows, follow `virtualenv`. If we're in a virtual environment, use
// `sys._base_executable` if it exists; if not, use `sys.base_prefix`. If we're _not_ in a
// virtual environment, use the interpreter's executable, since it's already a "system
// Python". We canonicalize the path to ensure that it's consistent, though we don't expect
// any symlinks on Windows.
if interpreter.is_virtualenv() {
if let Some(base_executable) = interpreter.base_executable() {
base_executable.to_path_buf()
} else {
// Assume `python.exe`, though the exact executable name is never used (below) on
// Windows, only its parent directory.
interpreter.base_prefix().join("python.exe")
}
} else {
fs_err::canonicalize(interpreter.sys_executable())?
}
Expand Down Expand Up @@ -202,7 +219,7 @@ pub fn create_bare_venv(
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::NotFound,
"The python interpreter needs to have a parent directory",
"The Python interpreter needs to have a parent directory",
)
})?
.simplified_display()
Expand Down
68 changes: 68 additions & 0 deletions crates/uv/tests/venv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::process::Command;

use anyhow::Result;
use assert_cmd::prelude::*;
use assert_fs::prelude::*;

use uv_fs::Simplified;
Expand Down Expand Up @@ -663,3 +664,70 @@ fn verify_pyvenv_cfg() {
let search_string = format!("uv = {version}");
pyvenv_cfg.assert(predicates::str::contains(search_string));
}

/// Ensure that a nested virtual environment uses the same `home` directory as the parent.
#[test]
fn verify_nested_pyvenv_cfg() -> Result<()> {
let temp_dir = assert_fs::TempDir::new()?;
let cache_dir = assert_fs::TempDir::new()?;
let bin = create_bin_with_executables(&temp_dir, &["3.12"]).expect("Failed to create bin dir");
let venv = temp_dir.child(".venv");

// Create a virtual environment at `.venv`.
Command::new(get_bin())
.arg("venv")
.arg(venv.as_os_str())
.arg("--python")
.arg("3.12")
.arg("--cache-dir")
.arg(cache_dir.path())
.arg("--exclude-newer")
.arg(EXCLUDE_NEWER)
.env("UV_TEST_PYTHON_PATH", bin.clone())
.current_dir(&temp_dir)
.assert()
.success();

let pyvenv_cfg = venv.child("pyvenv.cfg");

// Check pyvenv.cfg exists
pyvenv_cfg.assert(predicates::path::is_file());

// Extract the "home" line from the pyvenv.cfg file.
let contents = fs_err::read_to_string(pyvenv_cfg.path())?;
let venv_home = contents
.lines()
.find(|line| line.starts_with("home"))
.expect("home line not found");

// Now, create a virtual environment from within the virtual environment.
let subvenv = temp_dir.child(".subvenv");
Command::new(get_bin())
.arg("venv")
.arg(subvenv.as_os_str())
.arg("--python")
.arg("3.12")
.arg("--cache-dir")
.arg(cache_dir.path())
.arg("--exclude-newer")
.arg(EXCLUDE_NEWER)
.env("VIRTUAL_ENV", venv.as_os_str())
.env("UV_TEST_PYTHON_PATH", bin.clone())
.current_dir(&temp_dir)
.assert()
.success();

let sub_pyvenv_cfg = subvenv.child("pyvenv.cfg");

// Extract the "home" line from the pyvenv.cfg file.
let contents = fs_err::read_to_string(sub_pyvenv_cfg.path())?;
let sub_venv_home = contents
.lines()
.find(|line| line.starts_with("home"))
.expect("home line not found");

// Check that both directories point to the same home.
assert_eq!(sub_venv_home, venv_home);

Ok(())
}

0 comments on commit 6b1305c

Please sign in to comment.