mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Improve and test diagnostics for requirements-reading CLI commands (#143)
Also removes `owo_colors` because it was really painful to get it to avoid printing colors during tests.
This commit is contained in:
parent
ba181eacdd
commit
d5105a76c5
20 changed files with 332 additions and 34 deletions
15
Cargo.lock
generated
15
Cargo.lock
generated
|
@ -448,6 +448,17 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colored"
|
||||||
|
version = "2.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6"
|
||||||
|
dependencies = [
|
||||||
|
"is-terminal",
|
||||||
|
"lazy_static",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "configparser"
|
name = "configparser"
|
||||||
version = "3.0.2"
|
version = "3.0.2"
|
||||||
|
@ -1966,13 +1977,13 @@ version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
"colored",
|
||||||
"directories",
|
"directories",
|
||||||
"flate2",
|
"flate2",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"gourgeist",
|
"gourgeist",
|
||||||
"indoc 2.0.4",
|
"indoc 2.0.4",
|
||||||
"itertools",
|
"itertools",
|
||||||
"owo-colors",
|
|
||||||
"pep508_rs",
|
"pep508_rs",
|
||||||
"platform-host",
|
"platform-host",
|
||||||
"platform-tags",
|
"platform-tags",
|
||||||
|
@ -2006,6 +2017,7 @@ dependencies = [
|
||||||
"bitflags 2.4.1",
|
"bitflags 2.4.1",
|
||||||
"cacache",
|
"cacache",
|
||||||
"clap",
|
"clap",
|
||||||
|
"colored",
|
||||||
"directories",
|
"directories",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"futures",
|
"futures",
|
||||||
|
@ -2015,7 +2027,6 @@ dependencies = [
|
||||||
"install-wheel-rs",
|
"install-wheel-rs",
|
||||||
"itertools",
|
"itertools",
|
||||||
"miette",
|
"miette",
|
||||||
"owo-colors",
|
|
||||||
"pep440_rs 0.3.12",
|
"pep440_rs 0.3.12",
|
||||||
"pep508_rs",
|
"pep508_rs",
|
||||||
"platform-host",
|
"platform-host",
|
||||||
|
|
|
@ -18,6 +18,7 @@ bitflags = { version = "2.4.0" }
|
||||||
cacache = { version = "11.7.1", default-features = false, features = ["tokio-runtime"] }
|
cacache = { version = "11.7.1", default-features = false, features = ["tokio-runtime"] }
|
||||||
camino = { version = "1.1.6", features = ["serde1"] }
|
camino = { version = "1.1.6", features = ["serde1"] }
|
||||||
clap = { version = "4.4.6" }
|
clap = { version = "4.4.6" }
|
||||||
|
colored = { version = "2.0.4" }
|
||||||
configparser = { version = "3.0.2" }
|
configparser = { version = "3.0.2" }
|
||||||
csv = { version = "1.3.0" }
|
csv = { version = "1.3.0" }
|
||||||
data-encoding = { version = "2.4.0" }
|
data-encoding = { version = "2.4.0" }
|
||||||
|
@ -37,7 +38,6 @@ mailparse = { version = "0.14.0" }
|
||||||
memchr = { version = "2.6.4" }
|
memchr = { version = "2.6.4" }
|
||||||
miette = { version = "5.10.0" }
|
miette = { version = "5.10.0" }
|
||||||
once_cell = { version = "1.18.0" }
|
once_cell = { version = "1.18.0" }
|
||||||
owo-colors = { version = "3.5.0" }
|
|
||||||
platform-info = { version = "2.0.2" }
|
platform-info = { version = "2.0.2" }
|
||||||
plist = { version = "1.5.0" }
|
plist = { version = "1.5.0" }
|
||||||
pyproject-toml = { version = "0.7.0" }
|
pyproject-toml = { version = "0.7.0" }
|
||||||
|
|
|
@ -24,12 +24,12 @@ puffin-workspace = { path = "../puffin-workspace" }
|
||||||
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
clap = { workspace = true, features = ["derive"] }
|
clap = { workspace = true, features = ["derive"] }
|
||||||
|
colored = { workspace = true }
|
||||||
directories = { workspace = true }
|
directories = { workspace = true }
|
||||||
flate2 = { workspace = true }
|
flate2 = { workspace = true }
|
||||||
fs-err = { workspace = true }
|
fs-err = { workspace = true }
|
||||||
indoc = { workspace = true }
|
indoc = { workspace = true }
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
owo-colors = { workspace = true }
|
|
||||||
pyproject-toml = { workspace = true }
|
pyproject-toml = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use colored::Colorize;
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
use owo_colors::OwoColorize;
|
|
||||||
use puffin_build::{Error, SourceDistributionBuilder};
|
use puffin_build::{Error, SourceDistributionBuilder};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
|
|
|
@ -26,13 +26,13 @@ anyhow = { workspace = true }
|
||||||
bitflags = { workspace = true }
|
bitflags = { workspace = true }
|
||||||
cacache = { workspace = true }
|
cacache = { workspace = true }
|
||||||
clap = { workspace = true, features = ["derive"] }
|
clap = { workspace = true, features = ["derive"] }
|
||||||
|
colored = { workspace = true }
|
||||||
directories = { workspace = true }
|
directories = { workspace = true }
|
||||||
fs-err = { workspace = true, features = ["tokio"] }
|
fs-err = { workspace = true, features = ["tokio"] }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
indicatif = { workspace = true }
|
indicatif = { workspace = true }
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
miette = { workspace = true, features = ["fancy"] }
|
miette = { workspace = true, features = ["fancy"] }
|
||||||
owo-colors = { workspace = true }
|
|
||||||
pyproject-toml = { workspace = true }
|
pyproject-toml = { workspace = true }
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
|
|
@ -3,9 +3,9 @@ use std::io::{stdout, BufWriter};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use colored::Colorize;
|
||||||
use fs_err::File;
|
use fs_err::File;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use owo_colors::OwoColorize;
|
|
||||||
use pubgrub::report::Reporter;
|
use pubgrub::report::Reporter;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
|
@ -28,6 +28,13 @@ pub(crate) async fn pip_compile(
|
||||||
) -> Result<ExitStatus> {
|
) -> Result<ExitStatus> {
|
||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
|
|
||||||
|
// Read all requirements from the provided sources.
|
||||||
|
let requirements = sources
|
||||||
|
.iter()
|
||||||
|
.map(RequirementsSource::requirements)
|
||||||
|
.flatten_ok()
|
||||||
|
.collect::<Result<Vec<Requirement>>>()?;
|
||||||
|
|
||||||
// Detect the current Python interpreter.
|
// Detect the current Python interpreter.
|
||||||
let platform = Platform::current()?;
|
let platform = Platform::current()?;
|
||||||
let python = PythonExecutable::from_env(platform, cache)?;
|
let python = PythonExecutable::from_env(platform, cache)?;
|
||||||
|
@ -36,13 +43,6 @@ pub(crate) async fn pip_compile(
|
||||||
python.executable().display()
|
python.executable().display()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Read all requirements from the provided sources.
|
|
||||||
let requirements = sources
|
|
||||||
.iter()
|
|
||||||
.map(RequirementsSource::requirements)
|
|
||||||
.flatten_ok()
|
|
||||||
.collect::<Result<Vec<Requirement>>>()?;
|
|
||||||
|
|
||||||
// Determine the current environment markers.
|
// Determine the current environment markers.
|
||||||
let markers = python.markers();
|
let markers = python.markers();
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ use std::fmt::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use colored::Colorize;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use owo_colors::OwoColorize;
|
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use pep508_rs::Requirement;
|
use pep508_rs::Requirement;
|
||||||
|
@ -247,7 +247,7 @@ pub(crate) async fn sync_requirements(
|
||||||
printer,
|
printer,
|
||||||
" {} {}{}",
|
" {} {}{}",
|
||||||
"+".green(),
|
"+".green(),
|
||||||
event.distribution.name().white().bold(),
|
event.distribution.name().as_ref().white().bold(),
|
||||||
format!("@{}", event.distribution.version()).dimmed()
|
format!("@{}", event.distribution.version()).dimmed()
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
@ -256,7 +256,7 @@ pub(crate) async fn sync_requirements(
|
||||||
printer,
|
printer,
|
||||||
" {} {}{}",
|
" {} {}{}",
|
||||||
"-".red(),
|
"-".red(),
|
||||||
event.distribution.name().white().bold(),
|
event.distribution.name().as_ref().white().bold(),
|
||||||
format!("@{}", event.distribution.version()).dimmed()
|
format!("@{}", event.distribution.version()).dimmed()
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@ use std::fmt::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use colored::Colorize;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use owo_colors::OwoColorize;
|
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use pep508_rs::Requirement;
|
use pep508_rs::Requirement;
|
||||||
|
@ -23,6 +23,13 @@ pub(crate) async fn pip_uninstall(
|
||||||
) -> Result<ExitStatus> {
|
) -> Result<ExitStatus> {
|
||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
|
|
||||||
|
// Read all requirements from the provided sources.
|
||||||
|
let requirements = sources
|
||||||
|
.iter()
|
||||||
|
.map(RequirementsSource::requirements)
|
||||||
|
.flatten_ok()
|
||||||
|
.collect::<Result<Vec<Requirement>>>()?;
|
||||||
|
|
||||||
// Detect the current Python interpreter.
|
// Detect the current Python interpreter.
|
||||||
let platform = Platform::current()?;
|
let platform = Platform::current()?;
|
||||||
let python = PythonExecutable::from_env(platform, cache)?;
|
let python = PythonExecutable::from_env(platform, cache)?;
|
||||||
|
@ -31,13 +38,6 @@ pub(crate) async fn pip_uninstall(
|
||||||
python.executable().display()
|
python.executable().display()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Read all requirements from the provided sources.
|
|
||||||
let requirements = sources
|
|
||||||
.iter()
|
|
||||||
.map(RequirementsSource::requirements)
|
|
||||||
.flatten_ok()
|
|
||||||
.collect::<Result<Vec<Requirement>>>()?;
|
|
||||||
|
|
||||||
// Index the current `site-packages` directory.
|
// Index the current `site-packages` directory.
|
||||||
let site_packages = puffin_installer::SitePackages::from_executable(&python).await?;
|
let site_packages = puffin_installer::SitePackages::from_executable(&python).await?;
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ pub(crate) async fn pip_uninstall(
|
||||||
"{}{} Skipping {} as it is not installed.",
|
"{}{} Skipping {} as it is not installed.",
|
||||||
"warning".yellow().bold(),
|
"warning".yellow().bold(),
|
||||||
":".bold(),
|
":".bold(),
|
||||||
package.bold()
|
package.as_ref().bold()
|
||||||
);
|
);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@ use std::fmt::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use colored::Colorize;
|
||||||
use fs_err::tokio as fs;
|
use fs_err::tokio as fs;
|
||||||
use owo_colors::OwoColorize;
|
|
||||||
|
|
||||||
use crate::commands::ExitStatus;
|
use crate::commands::ExitStatus;
|
||||||
use crate::printer::Printer;
|
use crate::printer::Printer;
|
||||||
|
@ -25,7 +25,7 @@ pub(crate) async fn venv(
|
||||||
writeln!(
|
writeln!(
|
||||||
printer,
|
printer,
|
||||||
"Using Python interpreter: {}",
|
"Using Python interpreter: {}",
|
||||||
base_python.display().cyan()
|
format!("{}", base_python.display()).cyan()
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// If the path already exists, remove it.
|
// If the path already exists, remove it.
|
||||||
|
@ -35,7 +35,7 @@ pub(crate) async fn venv(
|
||||||
writeln!(
|
writeln!(
|
||||||
printer,
|
printer,
|
||||||
"Creating virtual environment at: {}",
|
"Creating virtual environment at: {}",
|
||||||
path.display().cyan()
|
format!("{}", path.display()).cyan()
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Create the virtual environment.
|
// Create the virtual environment.
|
||||||
|
|
|
@ -2,8 +2,8 @@ use std::path::PathBuf;
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
|
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
|
use colored::Colorize;
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
use owo_colors::OwoColorize;
|
|
||||||
|
|
||||||
use crate::commands::ExitStatus;
|
use crate::commands::ExitStatus;
|
||||||
use crate::requirements::RequirementsSource;
|
use crate::requirements::RequirementsSource;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
//! A standard interface for working with heterogeneous sources of requirements.
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
|
|
||||||
use pep508_rs::Requirement;
|
use pep508_rs::Requirement;
|
||||||
|
@ -37,7 +39,8 @@ impl RequirementsSource {
|
||||||
/// Return an iterator over the requirements in this source.
|
/// Return an iterator over the requirements in this source.
|
||||||
pub(crate) fn requirements(&self) -> Result<impl Iterator<Item = Requirement>> {
|
pub(crate) fn requirements(&self) -> Result<impl Iterator<Item = Requirement>> {
|
||||||
let iter_name = if let Self::Name(name) = self {
|
let iter_name = if let Self::Name(name) = self {
|
||||||
let requirement = Requirement::from_str(name)?;
|
let requirement =
|
||||||
|
Requirement::from_str(name).with_context(|| format!("Failed to parse `{name}`"))?;
|
||||||
Some(std::iter::once(requirement))
|
Some(std::iter::once(requirement))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -59,8 +62,9 @@ impl RequirementsSource {
|
||||||
};
|
};
|
||||||
|
|
||||||
let iter_pyproject_toml = if let Self::PyprojectToml(path) = self {
|
let iter_pyproject_toml = if let Self::PyprojectToml(path) = self {
|
||||||
let pyproject_toml =
|
let contents = fs::read_to_string(path)?;
|
||||||
toml::from_str::<pyproject_toml::PyProjectToml>(&fs::read_to_string(path)?)?;
|
let pyproject_toml = toml::from_str::<pyproject_toml::PyProjectToml>(&contents)
|
||||||
|
.with_context(|| format!("Failed to read `{}`", path.display()))?;
|
||||||
Some(
|
Some(
|
||||||
pyproject_toml
|
pyproject_toml
|
||||||
.project
|
.project
|
||||||
|
|
125
crates/puffin-cli/tests/pip_uninstall.rs
Normal file
125
crates/puffin-cli/tests/pip_uninstall.rs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use assert_fs::prelude::*;
|
||||||
|
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
|
||||||
|
|
||||||
|
const BIN_NAME: &str = "puffin";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_arguments() -> Result<()> {
|
||||||
|
let tempdir = assert_fs::TempDir::new()?;
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-uninstall")
|
||||||
|
.current_dir(&tempdir));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_requirement() -> Result<()> {
|
||||||
|
let tempdir = assert_fs::TempDir::new()?;
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-uninstall")
|
||||||
|
.arg("flask==1.0.x")
|
||||||
|
.current_dir(&tempdir));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_requirements_txt() -> Result<()> {
|
||||||
|
let tempdir = assert_fs::TempDir::new()?;
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-uninstall")
|
||||||
|
.arg("-r")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.current_dir(&tempdir));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_requirements_txt_requirement() -> Result<()> {
|
||||||
|
let tempdir = assert_fs::TempDir::new()?;
|
||||||
|
let requirements_txt = tempdir.child("requirements.txt");
|
||||||
|
requirements_txt.touch()?;
|
||||||
|
requirements_txt.write_str("flask==1.0.x")?;
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-uninstall")
|
||||||
|
.arg("-r")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.current_dir(&tempdir));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_pyproject_toml() -> Result<()> {
|
||||||
|
let tempdir = assert_fs::TempDir::new()?;
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-uninstall")
|
||||||
|
.arg("-r")
|
||||||
|
.arg("pyproject.toml")
|
||||||
|
.current_dir(&tempdir));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_pyproject_toml_syntax() -> Result<()> {
|
||||||
|
let tempdir = assert_fs::TempDir::new()?;
|
||||||
|
let pyproject_toml = tempdir.child("pyproject.toml");
|
||||||
|
pyproject_toml.touch()?;
|
||||||
|
pyproject_toml.write_str("123 - 456")?;
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-uninstall")
|
||||||
|
.arg("-r")
|
||||||
|
.arg("pyproject.toml")
|
||||||
|
.current_dir(&tempdir));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_pyproject_toml_schema() -> Result<()> {
|
||||||
|
let tempdir = assert_fs::TempDir::new()?;
|
||||||
|
let pyproject_toml = tempdir.child("pyproject.toml");
|
||||||
|
pyproject_toml.touch()?;
|
||||||
|
pyproject_toml.write_str("[project]")?;
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-uninstall")
|
||||||
|
.arg("-r")
|
||||||
|
.arg("pyproject.toml")
|
||||||
|
.current_dir(&tempdir));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_pyproject_toml_requirement() -> Result<()> {
|
||||||
|
let tempdir = assert_fs::TempDir::new()?;
|
||||||
|
let pyproject_toml = tempdir.child("pyproject.toml");
|
||||||
|
pyproject_toml.touch()?;
|
||||||
|
pyproject_toml.write_str(
|
||||||
|
r#"[project]
|
||||||
|
name = "project"
|
||||||
|
dependencies = ["flask==1.0.x"]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("pip-uninstall")
|
||||||
|
.arg("-r")
|
||||||
|
.arg("pyproject.toml")
|
||||||
|
.current_dir(&tempdir));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/pip_uninstall.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- pip-uninstall
|
||||||
|
- "-r"
|
||||||
|
- pyproject.toml
|
||||||
|
---
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Failed to read `pyproject.toml`
|
||||||
|
Caused by: TOML parse error at line 3, column 16
|
||||||
|
|
|
||||||
|
3 | dependencies = ["flask==1.0.x"]
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
Version specifier `==1.0.x` doesn't match PEP 440 rules
|
||||||
|
flask==1.0.x
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/pip_uninstall.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- pip-uninstall
|
||||||
|
- "-r"
|
||||||
|
- pyproject.toml
|
||||||
|
---
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Failed to read `pyproject.toml`
|
||||||
|
Caused by: TOML parse error at line 1, column 1
|
||||||
|
|
|
||||||
|
1 | [project]
|
||||||
|
| ^^^^^^^^^
|
||||||
|
missing field `name`
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/pip_uninstall.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- pip-uninstall
|
||||||
|
- "-r"
|
||||||
|
- pyproject.toml
|
||||||
|
---
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Failed to read `pyproject.toml`
|
||||||
|
Caused by: TOML parse error at line 1, column 5
|
||||||
|
|
|
||||||
|
1 | 123 - 456
|
||||||
|
| ^
|
||||||
|
expected `.`, `=`
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/pip_uninstall.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- pip-uninstall
|
||||||
|
- flask==1.0.x
|
||||||
|
---
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Failed to parse `flask==1.0.x`
|
||||||
|
Caused by: Version specifier `==1.0.x` doesn't match PEP 440 rules
|
||||||
|
flask==1.0.x
|
||||||
|
^^^^^^^
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/pip_uninstall.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- pip-uninstall
|
||||||
|
- "-r"
|
||||||
|
- requirements.txt
|
||||||
|
---
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Couldn't parse requirement in requirements.txt position 0 to 12
|
||||||
|
Caused by: Version specifier `==1.0.x` doesn't match PEP 440 rules
|
||||||
|
flask==1.0.x
|
||||||
|
^^^^^^^
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/pip_uninstall.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- pip-uninstall
|
||||||
|
- "-r"
|
||||||
|
- pyproject.toml
|
||||||
|
---
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: failed to open file `pyproject.toml`
|
||||||
|
Caused by: No such file or directory (os error 2)
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/pip_uninstall.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- pip-uninstall
|
||||||
|
- "-r"
|
||||||
|
- 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,19 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-cli/tests/pip_uninstall.rs
|
||||||
|
info:
|
||||||
|
program: puffin
|
||||||
|
args:
|
||||||
|
- pip-uninstall
|
||||||
|
---
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: the following required arguments were not provided:
|
||||||
|
<PACKAGE|--requirement <REQUIREMENT>>
|
||||||
|
|
||||||
|
Usage: puffin pip-uninstall <PACKAGE|--requirement <REQUIREMENT>>
|
||||||
|
|
||||||
|
For more information, try '--help'.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue