mirror of https://github.com/astral-sh/uv
490 lines
14 KiB
Rust
490 lines
14 KiB
Rust
#![cfg(feature = "python")]
|
||
|
||
use std::process::Command;
|
||
|
||
use anyhow::Result;
|
||
use assert_fs::prelude::*;
|
||
|
||
use puffin_fs::Normalized;
|
||
|
||
use crate::common::{create_bin_with_executables, get_bin, puffin_snapshot, EXCLUDE_NEWER};
|
||
|
||
mod common;
|
||
|
||
#[test]
|
||
fn create_venv() -> 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`.
|
||
let filter_venv = regex::escape(&venv.normalized_display().to_string());
|
||
let filters = &[
|
||
(
|
||
r"Using Python 3\.\d+\.\d+ interpreter at .+",
|
||
"Using Python [VERSION] interpreter at [PATH]",
|
||
),
|
||
(&filter_venv, "/home/ferris/project/.venv"),
|
||
];
|
||
puffin_snapshot!(filters, 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("PUFFIN_TEST_PYTHON_PATH", bin.clone())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
Using Python [VERSION] interpreter at [PATH]
|
||
Creating virtualenv at: /home/ferris/project/.venv
|
||
"###
|
||
);
|
||
|
||
venv.assert(predicates::path::is_dir());
|
||
|
||
// Create a virtual environment at the same location, which should replace it.
|
||
let filter_venv = regex::escape(&venv.normalized_display().to_string());
|
||
let filters = &[
|
||
(
|
||
r"Using Python 3\.\d+\.\d+ interpreter at .+",
|
||
"Using Python [VERSION] interpreter at [PATH]",
|
||
),
|
||
(&filter_venv, "/home/ferris/project/.venv"),
|
||
];
|
||
puffin_snapshot!(filters, 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("PUFFIN_NO_WRAP", "1")
|
||
.env("PUFFIN_TEST_PYTHON_PATH", bin)
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
Using Python [VERSION] interpreter at [PATH]
|
||
Creating virtualenv at: /home/ferris/project/.venv
|
||
"###
|
||
);
|
||
|
||
venv.assert(predicates::path::is_dir());
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[test]
|
||
fn create_venv_defaults_to_cwd() -> 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");
|
||
|
||
let filter_venv = regex::escape(&venv.normalized_display().to_string());
|
||
let filters = &[
|
||
(
|
||
r"Using Python 3\.\d+\.\d+ interpreter at .+",
|
||
"Using Python [VERSION] interpreter at [PATH]",
|
||
),
|
||
(&filter_venv, "/home/ferris/project/.venv"),
|
||
];
|
||
puffin_snapshot!(filters, Command::new(get_bin())
|
||
.arg("venv")
|
||
.arg("--python")
|
||
.arg("3.12")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("PUFFIN_NO_WRAP", "1")
|
||
.env("PUFFIN_TEST_PYTHON_PATH", bin)
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
Using Python [VERSION] interpreter at [PATH]
|
||
Creating virtualenv at: .venv
|
||
"###
|
||
);
|
||
|
||
venv.assert(predicates::path::is_dir());
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[test]
|
||
fn seed() -> 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");
|
||
|
||
let filter_venv = regex::escape(&venv.normalized_display().to_string());
|
||
let filters = &[
|
||
(
|
||
r"Using Python 3\.\d+\.\d+ interpreter at .+",
|
||
"Using Python [VERSION] interpreter at [PATH]",
|
||
),
|
||
(&filter_venv, "/home/ferris/project/.venv"),
|
||
];
|
||
puffin_snapshot!(filters, Command::new(get_bin())
|
||
.arg("venv")
|
||
.arg(venv.as_os_str())
|
||
.arg("--seed")
|
||
.arg("--python")
|
||
.arg("3.12")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("PUFFIN_NO_WRAP", "1")
|
||
.env("PUFFIN_TEST_PYTHON_PATH", bin)
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
Using Python [VERSION] interpreter at [PATH]
|
||
Creating virtualenv at: /home/ferris/project/.venv
|
||
+ setuptools==68.2.2
|
||
+ pip==23.3.1
|
||
+ wheel==0.41.3
|
||
"###
|
||
);
|
||
|
||
venv.assert(predicates::path::is_dir());
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[test]
|
||
fn create_venv_unknown_python_minor() -> 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");
|
||
|
||
let mut command = Command::new(get_bin());
|
||
command
|
||
.arg("venv")
|
||
.arg(venv.as_os_str())
|
||
.arg("--python")
|
||
.arg("3.15")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("PUFFIN_NO_WRAP", "1")
|
||
.env("PUFFIN_TEST_PYTHON_PATH", bin)
|
||
.current_dir(&temp_dir);
|
||
if cfg!(windows) {
|
||
puffin_snapshot!(&mut command, @r###"
|
||
success: false
|
||
exit_code: 1
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
× No Python 3.15 found through `py --list-paths`. Is Python 3.15 installed?
|
||
"###
|
||
);
|
||
} else {
|
||
puffin_snapshot!(&mut command, @r###"
|
||
success: false
|
||
exit_code: 1
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
× No Python 3.15 In `PATH`. Is Python 3.15 installed?
|
||
"###
|
||
);
|
||
}
|
||
|
||
venv.assert(predicates::path::missing());
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[test]
|
||
#[cfg(unix)] // TODO(konstin): Support patch versions on Windows
|
||
fn create_venv_unknown_python_patch() -> 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");
|
||
|
||
let filter_venv = regex::escape(&venv.normalized_display().to_string());
|
||
let filters = &[
|
||
(
|
||
r"Using Python 3\.\d+\.\d+ interpreter at .+",
|
||
"Using Python [VERSION] interpreter at [PATH]",
|
||
),
|
||
(&filter_venv, "/home/ferris/project/.venv"),
|
||
];
|
||
puffin_snapshot!(filters, Command::new(get_bin())
|
||
.arg("venv")
|
||
.arg(venv.as_os_str())
|
||
.arg("--python")
|
||
.arg("3.8.0")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("PUFFIN_NO_WRAP", "1")
|
||
.env("PUFFIN_TEST_PYTHON_PATH", bin)
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 1
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
× No Python 3.8.0 In `PATH`. Is Python 3.8.0 installed?
|
||
"###
|
||
);
|
||
|
||
venv.assert(predicates::path::missing());
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[test]
|
||
#[ignore] // TODO(konstin): Switch patch version strategy
|
||
#[cfg(unix)] // TODO(konstin): Support patch versions on Windows
|
||
fn create_venv_python_patch() -> 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.1"]).expect("Failed to create bin dir");
|
||
let venv = temp_dir.child(".venv");
|
||
|
||
let filter_venv = regex::escape(&venv.normalized_display().to_string());
|
||
let filters = &[
|
||
(r"interpreter at .+", "interpreter at [PATH]"),
|
||
(&filter_venv, "/home/ferris/project/.venv"),
|
||
];
|
||
puffin_snapshot!(filters, Command::new(get_bin())
|
||
.arg("venv")
|
||
.arg(venv.as_os_str())
|
||
.arg("--python")
|
||
.arg("3.12.1")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("PUFFIN_NO_WRAP", "1")
|
||
.env("PUFFIN_TEST_PYTHON_PATH", bin)
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
Using Python 3.12.1 interpreter at [PATH]
|
||
Creating virtualenv at: /home/ferris/project/.venv
|
||
"###
|
||
);
|
||
|
||
venv.assert(predicates::path::is_dir());
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[test]
|
||
fn file_exists() -> 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 file at `.venv`. Creating a virtualenv at the same path should fail.
|
||
venv.touch()?;
|
||
|
||
let filter_venv = regex::escape(&venv.normalized_display().to_string());
|
||
let filters = &[
|
||
(
|
||
r"Using Python 3\.\d+\.\d+ interpreter at .+",
|
||
"Using Python [VERSION] interpreter at [PATH]",
|
||
),
|
||
(&filter_venv, "/home/ferris/project/.venv"),
|
||
];
|
||
puffin_snapshot!(filters, 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("PUFFIN_NO_WRAP", "1")
|
||
.env("PUFFIN_TEST_PYTHON_PATH", bin)
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 1
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
Using Python [VERSION] interpreter at [PATH]
|
||
Creating virtualenv at: /home/ferris/project/.venv
|
||
puffin::venv::creation
|
||
|
||
× Failed to create virtualenv
|
||
╰─▶ File exists at `/home/ferris/project/.venv`
|
||
"###
|
||
);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[test]
|
||
fn empty_dir_exists() -> 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 an empty directory at `.venv`. Creating a virtualenv at the same path should succeed.
|
||
venv.create_dir_all()?;
|
||
|
||
let filter_venv = regex::escape(&venv.normalized_display().to_string());
|
||
let filters = &[
|
||
(
|
||
r"Using Python 3\.\d+\.\d+ interpreter at .+",
|
||
"Using Python [VERSION] interpreter at [PATH]",
|
||
),
|
||
(&filter_venv, "/home/ferris/project/.venv"),
|
||
];
|
||
puffin_snapshot!(filters, 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("PUFFIN_NO_WRAP", "1")
|
||
.env("PUFFIN_TEST_PYTHON_PATH", bin)
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
Using Python [VERSION] interpreter at [PATH]
|
||
Creating virtualenv at: /home/ferris/project/.venv
|
||
"###
|
||
);
|
||
|
||
venv.assert(predicates::path::is_dir());
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[test]
|
||
fn non_empty_dir_exists() -> 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 non-empty directory at `.venv`. Creating a virtualenv at the same path should fail.
|
||
venv.create_dir_all()?;
|
||
venv.child("file").touch()?;
|
||
|
||
let filter_venv = regex::escape(&venv.normalized_display().to_string());
|
||
let filters = &[
|
||
(
|
||
r"Using Python 3\.\d+\.\d+ interpreter at .+",
|
||
"Using Python [VERSION] interpreter at [PATH]",
|
||
),
|
||
(&filter_venv, "/home/ferris/project/.venv"),
|
||
];
|
||
puffin_snapshot!(filters, 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("PUFFIN_NO_WRAP", "1")
|
||
.env("PUFFIN_TEST_PYTHON_PATH", bin)
|
||
.current_dir(&temp_dir), @r###"
|
||
success: false
|
||
exit_code: 1
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
Using Python [VERSION] interpreter at [PATH]
|
||
Creating virtualenv at: /home/ferris/project/.venv
|
||
puffin::venv::creation
|
||
|
||
× Failed to create virtualenv
|
||
╰─▶ The directory `/home/ferris/project/.venv` exists, but it's not a virtualenv
|
||
"###
|
||
);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[test]
|
||
fn virtualenv_compatibility() -> 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`, passing the redundant `--clear` flag.
|
||
let filter_venv = regex::escape(&venv.normalized_display().to_string());
|
||
let filters = &[
|
||
(
|
||
r"Using Python 3\.\d+\.\d+ interpreter at .+",
|
||
"Using Python [VERSION] interpreter at [PATH]",
|
||
),
|
||
(&filter_venv, "/home/ferris/project/.venv"),
|
||
];
|
||
puffin_snapshot!(filters, Command::new(get_bin())
|
||
.arg("venv")
|
||
.arg(venv.as_os_str())
|
||
.arg("--clear")
|
||
.arg("--python")
|
||
.arg("3.12")
|
||
.arg("--cache-dir")
|
||
.arg(cache_dir.path())
|
||
.arg("--exclude-newer")
|
||
.arg(EXCLUDE_NEWER)
|
||
.env("PUFFIN_TEST_PYTHON_PATH", bin.clone())
|
||
.current_dir(&temp_dir), @r###"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
|
||
----- stderr -----
|
||
warning: virtualenv's `--clear` has no effect (Puffin always clears the virtual environment).
|
||
Using Python [VERSION] interpreter at [PATH]
|
||
Creating virtualenv at: /home/ferris/project/.venv
|
||
"###
|
||
);
|
||
|
||
venv.assert(predicates::path::is_dir());
|
||
|
||
Ok(())
|
||
}
|