mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
parent
3072c3265e
commit
b665f1489a
17 changed files with 638 additions and 35 deletions
5
.github/workflows/ci.yaml
vendored
5
.github/workflows/ci.yaml
vendored
|
@ -15,6 +15,7 @@ env:
|
||||||
CARGO_NET_RETRY: 10
|
CARGO_NET_RETRY: 10
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
RUSTUP_MAX_RETRIES: 10
|
RUSTUP_MAX_RETRIES: 10
|
||||||
|
PYTHON_VERSION: "3.12"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cargo-fmt:
|
cargo-fmt:
|
||||||
|
@ -47,6 +48,10 @@ jobs:
|
||||||
name: "cargo test | ${{ matrix.os }}"
|
name: "cargo test | ${{ matrix.os }}"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
- name: "Install Python"
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
- name: "Install Rust toolchain"
|
- name: "Install Rust toolchain"
|
||||||
run: rustup show
|
run: rustup show
|
||||||
- name: "Install cargo insta"
|
- name: "Install cargo insta"
|
||||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1308,6 +1308,7 @@ dependencies = [
|
||||||
"console",
|
"console",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"linked-hash-map",
|
"linked-hash-map",
|
||||||
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"similar",
|
"similar",
|
||||||
"yaml-rust",
|
"yaml-rust",
|
||||||
|
@ -2060,6 +2061,7 @@ dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"gourgeist",
|
"gourgeist",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
|
"insta",
|
||||||
"insta-cmd",
|
"insta-cmd",
|
||||||
"install-wheel-rs",
|
"install-wheel-rs",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
|
|
@ -48,4 +48,11 @@ which = { workspace = true }
|
||||||
assert_cmd = { version = "2.0.8" }
|
assert_cmd = { version = "2.0.8" }
|
||||||
assert_fs = { version = "1.0.13" }
|
assert_fs = { version = "1.0.13" }
|
||||||
insta-cmd = { version = "0.4.0" }
|
insta-cmd = { version = "0.4.0" }
|
||||||
|
insta = { version = "1.34.0", features = ["filters"] }
|
||||||
predicates = { version = "3.0.4" }
|
predicates = { version = "3.0.4" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
# Introduces a dependency on a local Python installation.
|
||||||
|
python = []
|
||||||
|
# Introduces a dependency on PyPI.
|
||||||
|
pypi = []
|
||||||
|
|
|
@ -32,6 +32,10 @@ struct Cli {
|
||||||
/// Avoid reading from or writing to the cache.
|
/// Avoid reading from or writing to the cache.
|
||||||
#[arg(global = true, long, short)]
|
#[arg(global = true, long, short)]
|
||||||
no_cache: bool,
|
no_cache: bool,
|
||||||
|
|
||||||
|
/// Path to the cache directory.
|
||||||
|
#[arg(global = true, long, env = "PUFFIN_CACHE_DIR")]
|
||||||
|
cache_dir: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
|
@ -133,9 +137,17 @@ async fn main() -> ExitCode {
|
||||||
printer::Printer::Default
|
printer::Printer::Default
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let project_dirs = ProjectDirs::from("", "", "puffin");
|
||||||
|
let cache_dir = (!cli.no_cache)
|
||||||
|
.then(|| {
|
||||||
|
cli.cache_dir
|
||||||
|
.as_deref()
|
||||||
|
.or_else(|| project_dirs.as_ref().map(ProjectDirs::cache_dir))
|
||||||
|
})
|
||||||
|
.flatten();
|
||||||
|
|
||||||
let result = match cli.command {
|
let result = match cli.command {
|
||||||
Commands::PipCompile(args) => {
|
Commands::PipCompile(args) => {
|
||||||
let dirs = ProjectDirs::from("", "", "puffin");
|
|
||||||
let requirements = args
|
let requirements = args
|
||||||
.src_file
|
.src_file
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -151,59 +163,30 @@ async fn main() -> ExitCode {
|
||||||
&constraints,
|
&constraints,
|
||||||
args.output_file.as_deref(),
|
args.output_file.as_deref(),
|
||||||
args.resolution.unwrap_or_default(),
|
args.resolution.unwrap_or_default(),
|
||||||
dirs.as_ref()
|
cache_dir,
|
||||||
.map(ProjectDirs::cache_dir)
|
|
||||||
.filter(|_| !cli.no_cache),
|
|
||||||
printer,
|
printer,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
Commands::PipSync(args) => {
|
Commands::PipSync(args) => {
|
||||||
let dirs = ProjectDirs::from("", "", "puffin");
|
|
||||||
let sources = args
|
let sources = args
|
||||||
.src_file
|
.src_file
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(RequirementsSource::from)
|
.map(RequirementsSource::from)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
commands::pip_sync(
|
commands::pip_sync(&sources, cache_dir, printer).await
|
||||||
&sources,
|
|
||||||
dirs.as_ref()
|
|
||||||
.map(ProjectDirs::cache_dir)
|
|
||||||
.filter(|_| !cli.no_cache),
|
|
||||||
printer,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
Commands::PipUninstall(args) => {
|
Commands::PipUninstall(args) => {
|
||||||
let dirs = ProjectDirs::from("", "", "puffin");
|
|
||||||
let sources = args
|
let sources = args
|
||||||
.package
|
.package
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(RequirementsSource::from)
|
.map(RequirementsSource::from)
|
||||||
.chain(args.requirement.into_iter().map(RequirementsSource::from))
|
.chain(args.requirement.into_iter().map(RequirementsSource::from))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
commands::pip_uninstall(
|
commands::pip_uninstall(&sources, cache_dir, printer).await
|
||||||
&sources,
|
|
||||||
dirs.as_ref()
|
|
||||||
.map(ProjectDirs::cache_dir)
|
|
||||||
.filter(|_| !cli.no_cache),
|
|
||||||
printer,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
Commands::Clean => {
|
|
||||||
let dirs = ProjectDirs::from("", "", "puffin");
|
|
||||||
commands::clean(dirs.as_ref().map(ProjectDirs::cache_dir), printer).await
|
|
||||||
}
|
|
||||||
Commands::Freeze => {
|
|
||||||
let dirs = ProjectDirs::from("", "", "puffin");
|
|
||||||
commands::freeze(
|
|
||||||
dirs.as_ref()
|
|
||||||
.map(ProjectDirs::cache_dir)
|
|
||||||
.filter(|_| !cli.no_cache),
|
|
||||||
printer,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
Commands::Clean => commands::clean(cache_dir, printer).await,
|
||||||
|
Commands::Freeze => commands::freeze(cache_dir, printer),
|
||||||
Commands::Venv(args) => commands::venv(&args.name, args.python.as_deref(), printer).await,
|
Commands::Venv(args) => commands::venv(&args.name, args.python.as_deref(), printer).await,
|
||||||
Commands::Add(args) => commands::add(&args.name, printer),
|
Commands::Add(args) => commands::add(&args.name, printer),
|
||||||
Commands::Remove(args) => commands::remove(&args.name, printer),
|
Commands::Remove(args) => commands::remove(&args.name, printer),
|
||||||
|
|
387
crates/puffin-cli/tests/pip_sync.rs
Normal file
387
crates/puffin-cli/tests/pip_sync.rs
Normal file
|
@ -0,0 +1,387 @@
|
||||||
|
#![cfg(all(feature = "python", feature = "pypi"))]
|
||||||
|
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use assert_cmd::prelude::*;
|
||||||
|
use assert_fs::prelude::*;
|
||||||
|
use insta_cmd::_macro_support::insta;
|
||||||
|
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
|
||||||
|
|
||||||
|
const BIN_NAME: &str = "puffin";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_requirements_txt() -> Result<()> {
|
||||||
|
let temp_dir = assert_fs::TempDir::new()?;
|
||||||
|
let cache_dir = assert_fs::TempDir::new()?;
|
||||||
|
let requirements_txt = temp_dir.child("requirements.txt");
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-sync")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.current_dir(&temp_dir));
|
||||||
|
|
||||||
|
requirements_txt.assert(predicates::path::missing());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_venv() -> Result<()> {
|
||||||
|
let temp_dir = assert_fs::TempDir::new()?;
|
||||||
|
let cache_dir = assert_fs::TempDir::new()?;
|
||||||
|
let venv = temp_dir.child(".venv");
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-sync")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.env("VIRTUAL_ENV", venv.as_os_str())
|
||||||
|
.current_dir(&temp_dir));
|
||||||
|
|
||||||
|
venv.assert(predicates::path::missing());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install a package into a virtual environment.
|
||||||
|
#[test]
|
||||||
|
fn install() -> Result<()> {
|
||||||
|
let temp_dir = assert_fs::TempDir::new()?;
|
||||||
|
let cache_dir = assert_fs::TempDir::new()?;
|
||||||
|
let venv = temp_dir.child(".venv");
|
||||||
|
|
||||||
|
Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("venv")
|
||||||
|
.arg(venv.as_os_str())
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
venv.assert(predicates::path::is_dir());
|
||||||
|
|
||||||
|
let requirements_txt = temp_dir.child("requirements.txt");
|
||||||
|
requirements_txt.touch()?;
|
||||||
|
requirements_txt.write_str("MarkupSafe==2.1.3")?;
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => vec![
|
||||||
|
(r"\d+ms", "[TIME]"),
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-sync")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.env("VIRTUAL_ENV", venv.as_os_str())
|
||||||
|
.current_dir(&temp_dir));
|
||||||
|
});
|
||||||
|
|
||||||
|
Command::new(venv.join("bin").join("python"))
|
||||||
|
.arg("-c")
|
||||||
|
.arg("import markupsafe")
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install multiple packages into a virtual environment.
|
||||||
|
#[test]
|
||||||
|
fn install_many() -> Result<()> {
|
||||||
|
let temp_dir = assert_fs::TempDir::new()?;
|
||||||
|
let cache_dir = assert_fs::TempDir::new()?;
|
||||||
|
let venv = temp_dir.child(".venv");
|
||||||
|
|
||||||
|
Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("venv")
|
||||||
|
.arg(venv.as_os_str())
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
venv.assert(predicates::path::is_dir());
|
||||||
|
|
||||||
|
let requirements_txt = temp_dir.child("requirements.txt");
|
||||||
|
requirements_txt.touch()?;
|
||||||
|
requirements_txt.write_str("MarkupSafe==2.1.3\ntomli==2.0.1")?;
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => vec![
|
||||||
|
(r"\d+ms", "[TIME]"),
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-sync")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.env("VIRTUAL_ENV", venv.as_os_str())
|
||||||
|
.current_dir(&temp_dir));
|
||||||
|
});
|
||||||
|
|
||||||
|
Command::new(venv.join("bin").join("python"))
|
||||||
|
.arg("-c")
|
||||||
|
.arg("import markupsafe; import tomli")
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to install an already-installed package into a virtual environment.
|
||||||
|
#[test]
|
||||||
|
fn noop() -> Result<()> {
|
||||||
|
let temp_dir = assert_fs::TempDir::new()?;
|
||||||
|
let cache_dir = assert_fs::TempDir::new()?;
|
||||||
|
let venv = temp_dir.child(".venv");
|
||||||
|
|
||||||
|
Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("venv")
|
||||||
|
.arg(venv.as_os_str())
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
venv.assert(predicates::path::is_dir());
|
||||||
|
|
||||||
|
let requirements_txt = temp_dir.child("requirements.txt");
|
||||||
|
requirements_txt.touch()?;
|
||||||
|
requirements_txt.write_str("MarkupSafe==2.1.3")?;
|
||||||
|
|
||||||
|
Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-sync")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.env("VIRTUAL_ENV", venv.as_os_str())
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => vec![
|
||||||
|
(r"\d+ms", "[TIME]"),
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-sync")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.env("VIRTUAL_ENV", venv.as_os_str())
|
||||||
|
.current_dir(&temp_dir));
|
||||||
|
});
|
||||||
|
|
||||||
|
Command::new(venv.join("bin").join("python"))
|
||||||
|
.arg("-c")
|
||||||
|
.arg("import markupsafe")
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install a package into a virtual environment, then install the same package into a different
|
||||||
|
/// virtual environment.
|
||||||
|
#[test]
|
||||||
|
fn link() -> Result<()> {
|
||||||
|
let temp_dir = assert_fs::TempDir::new()?;
|
||||||
|
let cache_dir = assert_fs::TempDir::new()?;
|
||||||
|
let venv = temp_dir.child(".venv");
|
||||||
|
|
||||||
|
Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("venv")
|
||||||
|
.arg(venv.as_os_str())
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
venv.assert(predicates::path::is_dir());
|
||||||
|
|
||||||
|
let requirements_txt = temp_dir.child("requirements.txt");
|
||||||
|
requirements_txt.touch()?;
|
||||||
|
requirements_txt.write_str("MarkupSafe==2.1.3")?;
|
||||||
|
|
||||||
|
Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-sync")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.env("VIRTUAL_ENV", venv.as_os_str())
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
let venv = temp_dir.child(".venv");
|
||||||
|
Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("venv")
|
||||||
|
.arg(venv.as_os_str())
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
venv.assert(predicates::path::is_dir());
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => vec![
|
||||||
|
(r"\d+ms", "[TIME]"),
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-sync")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.env("VIRTUAL_ENV", venv.as_os_str())
|
||||||
|
.current_dir(&temp_dir));
|
||||||
|
});
|
||||||
|
|
||||||
|
Command::new(venv.join("bin").join("python"))
|
||||||
|
.arg("-c")
|
||||||
|
.arg("import markupsafe")
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install a package into a virtual environment, then sync the virtual environment with a
|
||||||
|
/// different requirements file.
|
||||||
|
#[test]
|
||||||
|
fn add_remove() -> Result<()> {
|
||||||
|
let temp_dir = assert_fs::TempDir::new()?;
|
||||||
|
let cache_dir = assert_fs::TempDir::new()?;
|
||||||
|
let venv = temp_dir.child(".venv");
|
||||||
|
|
||||||
|
Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("venv")
|
||||||
|
.arg(venv.as_os_str())
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
venv.assert(predicates::path::is_dir());
|
||||||
|
|
||||||
|
let requirements_txt = temp_dir.child("requirements.txt");
|
||||||
|
requirements_txt.touch()?;
|
||||||
|
requirements_txt.write_str("MarkupSafe==2.1.3")?;
|
||||||
|
|
||||||
|
Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-sync")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.env("VIRTUAL_ENV", venv.as_os_str())
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
let requirements_txt = temp_dir.child("requirements.txt");
|
||||||
|
requirements_txt.touch()?;
|
||||||
|
requirements_txt.write_str("tomli==2.0.1")?;
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => vec![
|
||||||
|
(r"\d+ms", "[TIME]"),
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-sync")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.env("VIRTUAL_ENV", venv.as_os_str())
|
||||||
|
.current_dir(&temp_dir));
|
||||||
|
});
|
||||||
|
|
||||||
|
Command::new(venv.join("bin").join("python"))
|
||||||
|
.arg("-c")
|
||||||
|
.arg("import tomli")
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
Command::new(venv.join("bin").join("python"))
|
||||||
|
.arg("-c")
|
||||||
|
.arg("import markupsafe")
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.failure();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install a package into a virtual environment, then install a second package into the same
|
||||||
|
/// virtual environment.
|
||||||
|
#[test]
|
||||||
|
fn install_sequential() -> Result<()> {
|
||||||
|
let temp_dir = assert_fs::TempDir::new()?;
|
||||||
|
let cache_dir = assert_fs::TempDir::new()?;
|
||||||
|
let venv = temp_dir.child(".venv");
|
||||||
|
|
||||||
|
Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("venv")
|
||||||
|
.arg(venv.as_os_str())
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
venv.assert(predicates::path::is_dir());
|
||||||
|
|
||||||
|
let requirements_txt = temp_dir.child("requirements.txt");
|
||||||
|
requirements_txt.touch()?;
|
||||||
|
requirements_txt.write_str("MarkupSafe==2.1.3")?;
|
||||||
|
|
||||||
|
Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-sync")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.env("VIRTUAL_ENV", venv.as_os_str())
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
let requirements_txt = temp_dir.child("requirements.txt");
|
||||||
|
requirements_txt.touch()?;
|
||||||
|
requirements_txt.write_str("MarkupSafe==2.1.3\ntomli==2.0.1")?;
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => vec![
|
||||||
|
(r"\d+ms", "[TIME]"),
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-sync")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(cache_dir.path())
|
||||||
|
.env("VIRTUAL_ENV", venv.as_os_str())
|
||||||
|
.current_dir(&temp_dir));
|
||||||
|
});
|
||||||
|
|
||||||
|
Command::new(venv.join("bin").join("python"))
|
||||||
|
.arg("-c")
|
||||||
|
.arg("import markupsafe; import tomli")
|
||||||
|
.current_dir(&temp_dir)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
25
crates/puffin-cli/tests/snapshots/pip_sync__add_remove.snap
Normal file
25
crates/puffin-cli/tests/snapshots/pip_sync__add_remove.snap
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/pip_sync.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- pip-sync
|
||||||
|
- requirements.txt
|
||||||
|
- "--cache-dir"
|
||||||
|
- /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpdWoKpL
|
||||||
|
env:
|
||||||
|
VIRTUAL_ENV: /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpVhsDyT/.venv
|
||||||
|
---
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 1 package in [TIME]
|
||||||
|
Downloaded 1 package in [TIME]
|
||||||
|
Unzipped 1 package in [TIME]
|
||||||
|
Uninstalled 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
- markupsafe@2.1.3
|
||||||
|
+ tomli@2.0.1
|
||||||
|
|
20
crates/puffin-cli/tests/snapshots/pip_sync__install.snap
Normal file
20
crates/puffin-cli/tests/snapshots/pip_sync__install.snap
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/pip_sync.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- pip-sync
|
||||||
|
- requirements.txt
|
||||||
|
- "--no-cache"
|
||||||
|
---
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 1 package in [TIME]
|
||||||
|
Downloaded 1 package in [TIME]
|
||||||
|
Unzipped 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ markupsafe@2.1.3
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/pip_sync.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- pip-sync
|
||||||
|
- requirements.txt
|
||||||
|
- "--cache-dir"
|
||||||
|
- /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpoqh2hP
|
||||||
|
env:
|
||||||
|
VIRTUAL_ENV: /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpZCdve2/.venv
|
||||||
|
---
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 2 packages in [TIME]
|
||||||
|
Downloaded 2 packages in [TIME]
|
||||||
|
Unzipped 2 packages in [TIME]
|
||||||
|
Installed 2 packages in [TIME]
|
||||||
|
+ markupsafe@2.1.3
|
||||||
|
+ tomli@2.0.1
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/pip_sync.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- pip-sync
|
||||||
|
- requirements.txt
|
||||||
|
- "--cache-dir"
|
||||||
|
- /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpWxh1hG
|
||||||
|
env:
|
||||||
|
VIRTUAL_ENV: /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpUUUwuX/.venv
|
||||||
|
---
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 1 package in [TIME]
|
||||||
|
Downloaded 1 package in [TIME]
|
||||||
|
Unzipped 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ tomli@2.0.1
|
||||||
|
|
20
crates/puffin-cli/tests/snapshots/pip_sync__link.snap
Normal file
20
crates/puffin-cli/tests/snapshots/pip_sync__link.snap
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/pip_sync.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- pip-sync
|
||||||
|
- requirements.txt
|
||||||
|
- "--cache-dir"
|
||||||
|
- /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmprConY4
|
||||||
|
env:
|
||||||
|
VIRTUAL_ENV: /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpxukjZu/.venv
|
||||||
|
---
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ markupsafe@2.1.3
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/pip_sync.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- pip-sync
|
||||||
|
- requirements.txt
|
||||||
|
---
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: failed to open file `requirements.txt`
|
||||||
|
Caused by: No such file or directory (os error 2)
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/pip_sync.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- pip-sync
|
||||||
|
- requirements.txt
|
||||||
|
env:
|
||||||
|
VIRTUAL_ENV: /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpvJQh45/.venv
|
||||||
|
---
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: failed to open file `requirements.txt`
|
||||||
|
Caused by: No such file or directory (os error 2)
|
||||||
|
|
19
crates/puffin-cli/tests/snapshots/pip_sync__noop.snap
Normal file
19
crates/puffin-cli/tests/snapshots/pip_sync__noop.snap
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/pip_sync.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- pip-sync
|
||||||
|
- requirements.txt
|
||||||
|
- "--cache-dir"
|
||||||
|
- /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpEvtFoB
|
||||||
|
env:
|
||||||
|
VIRTUAL_ENV: /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpYZqcOB/.venv
|
||||||
|
---
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Audited 1 package in [TIME]
|
||||||
|
|
16
crates/puffin-cli/tests/snapshots/venv__create_venv.snap
Normal file
16
crates/puffin-cli/tests/snapshots/venv__create_venv.snap
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/venv.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- venv
|
||||||
|
- /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpn0fxWx/.venv
|
||||||
|
---
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Using Python interpreter: /usr/bin/python3
|
||||||
|
Creating virtual environment at: /home/ferris/project/.venv
|
||||||
|
|
32
crates/puffin-cli/tests/venv.rs
Normal file
32
crates/puffin-cli/tests/venv.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#![cfg(feature = "python")]
|
||||||
|
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use assert_fs::prelude::*;
|
||||||
|
use insta_cmd::_macro_support::insta;
|
||||||
|
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
|
||||||
|
|
||||||
|
const BIN_NAME: &str = "puffin";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_venv() -> Result<()> {
|
||||||
|
let tempdir = assert_fs::TempDir::new()?;
|
||||||
|
let venv = tempdir.child(".venv");
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => vec![
|
||||||
|
(r"Using Python interpreter: .+", "Using Python interpreter: /usr/bin/python3"),
|
||||||
|
(tempdir.to_str().unwrap(), "/home/ferris/project"),
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("venv")
|
||||||
|
.arg(venv.as_os_str())
|
||||||
|
.current_dir(&tempdir));
|
||||||
|
});
|
||||||
|
|
||||||
|
venv.assert(predicates::path::is_dir());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -36,3 +36,7 @@ waitmap = { workspace = true }
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
once_cell = { version = "1.18.0" }
|
once_cell = { version = "1.18.0" }
|
||||||
insta = { version = "1.34.0" }
|
insta = { version = "1.34.0" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
# Introduces a dependency on PyPI.
|
||||||
|
pypi = []
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![cfg(feature = "pypi")]
|
||||||
|
|
||||||
//! Integration tests for the resolver. These tests rely on a live network connection, and hit
|
//! Integration tests for the resolver. These tests rely on a live network connection, and hit
|
||||||
//! `PyPI` directly.
|
//! `PyPI` directly.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue