mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Add support for lowest and lowest-direct resolution modes (#160)
Borrows terminology from pnpm by introducing three resolution modes: - "Highest": always choose the highest compliant version (default). - "Lowest": always choose the lowest compliant version. - "LowestDirect": choose the lowest compliant version of direct dependencies, and the highest compliant version of any transitive dependencies. (This makes a bit more sense than "lowest".) Closes https://github.com/astral-sh/puffin/issues/142.
This commit is contained in:
parent
ae9d1f7572
commit
3072c3265e
11 changed files with 267 additions and 44 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2173,11 +2173,13 @@ version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bitflags 2.4.1",
|
"bitflags 2.4.1",
|
||||||
|
"clap",
|
||||||
"colored",
|
"colored",
|
||||||
"distribution-filename",
|
"distribution-filename",
|
||||||
"futures",
|
"futures",
|
||||||
"fxhash",
|
"fxhash",
|
||||||
"insta",
|
"insta",
|
||||||
|
"itertools",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pep440_rs 0.3.12",
|
"pep440_rs 0.3.12",
|
||||||
"pep508_rs",
|
"pep508_rs",
|
||||||
|
|
|
@ -19,7 +19,7 @@ puffin-client = { path = "../puffin-client" }
|
||||||
puffin-installer = { path = "../puffin-installer" }
|
puffin-installer = { path = "../puffin-installer" }
|
||||||
puffin-interpreter = { path = "../puffin-interpreter" }
|
puffin-interpreter = { path = "../puffin-interpreter" }
|
||||||
puffin-package = { path = "../puffin-package" }
|
puffin-package = { path = "../puffin-package" }
|
||||||
puffin-resolver = { path = "../puffin-resolver" }
|
puffin-resolver = { path = "../puffin-resolver", features = ["clap"] }
|
||||||
puffin-workspace = { path = "../puffin-workspace" }
|
puffin-workspace = { path = "../puffin-workspace" }
|
||||||
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
|
|
@ -15,6 +15,7 @@ use platform_host::Platform;
|
||||||
use platform_tags::Tags;
|
use platform_tags::Tags;
|
||||||
use puffin_client::PypiClientBuilder;
|
use puffin_client::PypiClientBuilder;
|
||||||
use puffin_interpreter::PythonExecutable;
|
use puffin_interpreter::PythonExecutable;
|
||||||
|
use puffin_resolver::ResolutionMode;
|
||||||
|
|
||||||
use crate::commands::{elapsed, ExitStatus};
|
use crate::commands::{elapsed, ExitStatus};
|
||||||
use crate::printer::Printer;
|
use crate::printer::Printer;
|
||||||
|
@ -27,6 +28,7 @@ pub(crate) async fn pip_compile(
|
||||||
requirements: &[RequirementsSource],
|
requirements: &[RequirementsSource],
|
||||||
constraints: &[RequirementsSource],
|
constraints: &[RequirementsSource],
|
||||||
output_file: Option<&Path>,
|
output_file: Option<&Path>,
|
||||||
|
mode: ResolutionMode,
|
||||||
cache: Option<&Path>,
|
cache: Option<&Path>,
|
||||||
mut printer: Printer,
|
mut printer: Printer,
|
||||||
) -> Result<ExitStatus> {
|
) -> Result<ExitStatus> {
|
||||||
|
@ -63,7 +65,7 @@ pub(crate) async fn pip_compile(
|
||||||
|
|
||||||
// Resolve the dependencies.
|
// Resolve the dependencies.
|
||||||
let resolver =
|
let resolver =
|
||||||
puffin_resolver::Resolver::new(requirements, constraints, markers, &tags, &client);
|
puffin_resolver::Resolver::new(requirements, constraints, mode, markers, &tags, &client);
|
||||||
let resolution = match resolver.resolve().await {
|
let resolution = match resolver.resolve().await {
|
||||||
Err(puffin_resolver::ResolveError::PubGrub(pubgrub::error::PubGrubError::NoSolution(
|
Err(puffin_resolver::ResolveError::PubGrub(pubgrub::error::PubGrubError::NoSolution(
|
||||||
mut derivation_tree,
|
mut derivation_tree,
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::process::ExitCode;
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
|
use puffin_resolver::ResolutionMode;
|
||||||
|
|
||||||
use crate::commands::ExitStatus;
|
use crate::commands::ExitStatus;
|
||||||
use crate::requirements::RequirementsSource;
|
use crate::requirements::RequirementsSource;
|
||||||
|
@ -63,6 +64,9 @@ struct PipCompileArgs {
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
constraint: Vec<PathBuf>,
|
constraint: Vec<PathBuf>,
|
||||||
|
|
||||||
|
#[clap(long, value_enum)]
|
||||||
|
resolution: Option<ResolutionMode>,
|
||||||
|
|
||||||
/// Write the compiled requirements to the given `requirements.txt` file.
|
/// Write the compiled requirements to the given `requirements.txt` file.
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
output_file: Option<PathBuf>,
|
output_file: Option<PathBuf>,
|
||||||
|
@ -146,6 +150,7 @@ async fn main() -> ExitCode {
|
||||||
&requirements,
|
&requirements,
|
||||||
&constraints,
|
&constraints,
|
||||||
args.output_file.as_deref(),
|
args.output_file.as_deref(),
|
||||||
|
args.resolution.unwrap_or_default(),
|
||||||
dirs.as_ref()
|
dirs.as_ref()
|
||||||
.map(ProjectDirs::cache_dir)
|
.map(ProjectDirs::cache_dir)
|
||||||
.filter(|_| !cli.no_cache),
|
.filter(|_| !cli.no_cache),
|
||||||
|
|
|
@ -21,9 +21,11 @@ distribution-filename = { path = "../distribution-filename" }
|
||||||
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
bitflags = { workspace = true }
|
bitflags = { workspace = true }
|
||||||
|
clap = { workspace = true, features = ["derive"], optional = true }
|
||||||
colored = { workspace = true }
|
colored = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
fxhash = { workspace = true }
|
fxhash = { workspace = true }
|
||||||
|
itertools = { workspace = true }
|
||||||
once_cell = { workspace = true }
|
once_cell = { workspace = true }
|
||||||
petgraph = { workspace = true }
|
petgraph = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
pub use error::ResolveError;
|
pub use error::ResolveError;
|
||||||
|
pub use mode::ResolutionMode;
|
||||||
pub use resolution::PinnedPackage;
|
pub use resolution::PinnedPackage;
|
||||||
pub use resolver::Resolver;
|
pub use resolver::Resolver;
|
||||||
pub use wheel_finder::{Reporter, WheelFinder};
|
pub use wheel_finder::{Reporter, WheelFinder};
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
|
mod mode;
|
||||||
mod pubgrub;
|
mod pubgrub;
|
||||||
mod resolution;
|
mod resolution;
|
||||||
mod resolver;
|
mod resolver;
|
||||||
|
|
67
crates/puffin-resolver/src/mode.rs
Normal file
67
crates/puffin-resolver/src/mode.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use fxhash::FxHashSet;
|
||||||
|
use itertools::Either;
|
||||||
|
|
||||||
|
use pep508_rs::Requirement;
|
||||||
|
use puffin_client::File;
|
||||||
|
use puffin_package::package_name::PackageName;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
|
||||||
|
pub enum ResolutionMode {
|
||||||
|
/// Resolve the highest compatible version of each package.
|
||||||
|
#[default]
|
||||||
|
Highest,
|
||||||
|
/// Resolve the lowest compatible version of each package.
|
||||||
|
Lowest,
|
||||||
|
/// Resolve the lowest compatible version of any direct dependencies, and the highest
|
||||||
|
/// compatible version of any transitive dependencies.
|
||||||
|
LowestDirect,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) enum CandidateSelector {
|
||||||
|
/// Resolve the highest compatible version of each package.
|
||||||
|
Highest,
|
||||||
|
/// Resolve the lowest compatible version of each package.
|
||||||
|
Lowest,
|
||||||
|
/// Resolve the lowest compatible version of any direct dependencies, and the highest
|
||||||
|
/// compatible version of any transitive dependencies.
|
||||||
|
LowestDirect(FxHashSet<PackageName>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CandidateSelector {
|
||||||
|
/// Return a candidate selector for the given resolution mode.
|
||||||
|
pub(crate) fn from_mode(mode: ResolutionMode, direct_dependencies: &[Requirement]) -> Self {
|
||||||
|
match mode {
|
||||||
|
ResolutionMode::Highest => Self::Highest,
|
||||||
|
ResolutionMode::Lowest => Self::Lowest,
|
||||||
|
ResolutionMode::LowestDirect => Self::LowestDirect(
|
||||||
|
direct_dependencies
|
||||||
|
.iter()
|
||||||
|
.map(|requirement| PackageName::normalize(&requirement.name))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CandidateSelector {
|
||||||
|
/// Return an iterator over the candidates for the given package name.
|
||||||
|
pub(crate) fn iter_candidates<'a>(
|
||||||
|
&self,
|
||||||
|
package_name: &PackageName,
|
||||||
|
candidates: &'a [File],
|
||||||
|
) -> impl Iterator<Item = &'a File> {
|
||||||
|
match self {
|
||||||
|
CandidateSelector::Highest => Either::Left(candidates.iter().rev()),
|
||||||
|
CandidateSelector::Lowest => Either::Right(candidates.iter()),
|
||||||
|
CandidateSelector::LowestDirect(direct_dependencies) => {
|
||||||
|
if direct_dependencies.contains(package_name) {
|
||||||
|
Either::Right(candidates.iter())
|
||||||
|
} else {
|
||||||
|
Either::Left(candidates.iter().rev())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ use puffin_package::metadata::Metadata21;
|
||||||
use puffin_package::package_name::PackageName;
|
use puffin_package::package_name::PackageName;
|
||||||
|
|
||||||
use crate::error::ResolveError;
|
use crate::error::ResolveError;
|
||||||
|
use crate::mode::{CandidateSelector, ResolutionMode};
|
||||||
use crate::pubgrub::package::PubGrubPackage;
|
use crate::pubgrub::package::PubGrubPackage;
|
||||||
use crate::pubgrub::version::{PubGrubVersion, MIN_VERSION};
|
use crate::pubgrub::version::{PubGrubVersion, MIN_VERSION};
|
||||||
use crate::pubgrub::{iter_requirements, version_range};
|
use crate::pubgrub::{iter_requirements, version_range};
|
||||||
|
@ -38,6 +39,7 @@ pub struct Resolver<'a> {
|
||||||
markers: &'a MarkerEnvironment,
|
markers: &'a MarkerEnvironment,
|
||||||
tags: &'a Tags,
|
tags: &'a Tags,
|
||||||
client: &'a PypiClient,
|
client: &'a PypiClient,
|
||||||
|
selector: CandidateSelector,
|
||||||
cache: Arc<SolverCache>,
|
cache: Arc<SolverCache>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,17 +48,19 @@ impl<'a> Resolver<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
requirements: Vec<Requirement>,
|
requirements: Vec<Requirement>,
|
||||||
constraints: Vec<Requirement>,
|
constraints: Vec<Requirement>,
|
||||||
|
mode: ResolutionMode,
|
||||||
markers: &'a MarkerEnvironment,
|
markers: &'a MarkerEnvironment,
|
||||||
tags: &'a Tags,
|
tags: &'a Tags,
|
||||||
client: &'a PypiClient,
|
client: &'a PypiClient,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
selector: CandidateSelector::from_mode(mode, &requirements),
|
||||||
|
cache: Arc::new(SolverCache::default()),
|
||||||
requirements,
|
requirements,
|
||||||
constraints,
|
constraints,
|
||||||
markers,
|
markers,
|
||||||
tags,
|
tags,
|
||||||
client,
|
client,
|
||||||
cache: Arc::new(SolverCache::default()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,24 +267,28 @@ impl<'a> Resolver<'a> {
|
||||||
let simple_json = entry.value();
|
let simple_json = entry.value();
|
||||||
|
|
||||||
// Select the latest compatible version.
|
// Select the latest compatible version.
|
||||||
let Some(file) = simple_json.files.iter().rev().find(|file| {
|
let Some(file) = self
|
||||||
let Ok(name) = WheelFilename::from_str(file.filename.as_str()) else {
|
.selector
|
||||||
return false;
|
.iter_candidates(package_name, &simple_json.files)
|
||||||
};
|
.find(|file| {
|
||||||
|
let Ok(name) = WheelFilename::from_str(file.filename.as_str()) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
if !name.is_compatible(self.tags) {
|
if !name.is_compatible(self.tags) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !range
|
if !range
|
||||||
.borrow()
|
.borrow()
|
||||||
.contains(&PubGrubVersion::from(name.version.clone()))
|
.contains(&PubGrubVersion::from(name.version.clone()))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
true
|
true
|
||||||
}) else {
|
})
|
||||||
|
else {
|
||||||
// Short circuit: we couldn't find _any_ compatible versions for a package.
|
// Short circuit: we couldn't find _any_ compatible versions for a package.
|
||||||
let (package, _range) = potential_packages.swap_remove(index);
|
let (package, _range) = potential_packages.swap_remove(index);
|
||||||
return Ok((package, None));
|
return Ok((package, None));
|
||||||
|
@ -313,28 +321,32 @@ impl<'a> Resolver<'a> {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Find a compatible version.
|
// Find a compatible version.
|
||||||
let Some(wheel) = simple_json.files.iter().rev().find_map(|file| {
|
let Some(wheel) = self
|
||||||
let Ok(name) = WheelFilename::from_str(file.filename.as_str()) else {
|
.selector
|
||||||
return None;
|
.iter_candidates(package_name, &simple_json.files)
|
||||||
};
|
.find_map(|file| {
|
||||||
|
let Ok(name) = WheelFilename::from_str(file.filename.as_str()) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
if !name.is_compatible(self.tags) {
|
if !name.is_compatible(self.tags) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !range
|
if !range
|
||||||
.borrow()
|
.borrow()
|
||||||
.contains(&PubGrubVersion::from(name.version.clone()))
|
.contains(&PubGrubVersion::from(name.version.clone()))
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(Wheel {
|
Some(Wheel {
|
||||||
file: file.clone(),
|
file: file.clone(),
|
||||||
name: package_name.clone(),
|
name: package_name.clone(),
|
||||||
version: name.version.clone(),
|
version: name.version.clone(),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}) else {
|
else {
|
||||||
// Short circuit: we couldn't find _any_ compatible versions for a package.
|
// Short circuit: we couldn't find _any_ compatible versions for a package.
|
||||||
return Ok((package, None));
|
return Ok((package, None));
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,7 @@ use pep508_rs::{MarkerEnvironment, Requirement, StringVersion};
|
||||||
use platform_host::{Arch, Os, Platform};
|
use platform_host::{Arch, Os, Platform};
|
||||||
use platform_tags::Tags;
|
use platform_tags::Tags;
|
||||||
use puffin_client::PypiClientBuilder;
|
use puffin_client::PypiClientBuilder;
|
||||||
use puffin_resolver::Resolver;
|
use puffin_resolver::{ResolutionMode, Resolver};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn pylint() -> Result<()> {
|
async fn pylint() -> Result<()> {
|
||||||
|
@ -20,7 +20,14 @@ async fn pylint() -> Result<()> {
|
||||||
|
|
||||||
let requirements = vec![Requirement::from_str("pylint==2.3.0").unwrap()];
|
let requirements = vec![Requirement::from_str("pylint==2.3.0").unwrap()];
|
||||||
let constraints = vec![];
|
let constraints = vec![];
|
||||||
let resolver = Resolver::new(requirements, constraints, &MARKERS_311, &TAGS_311, &client);
|
let resolver = Resolver::new(
|
||||||
|
requirements,
|
||||||
|
constraints,
|
||||||
|
ResolutionMode::default(),
|
||||||
|
&MARKERS_311,
|
||||||
|
&TAGS_311,
|
||||||
|
&client,
|
||||||
|
);
|
||||||
let resolution = resolver.resolve().await?;
|
let resolution = resolver.resolve().await?;
|
||||||
|
|
||||||
insta::assert_display_snapshot!(resolution);
|
insta::assert_display_snapshot!(resolution);
|
||||||
|
@ -36,7 +43,14 @@ async fn black() -> Result<()> {
|
||||||
|
|
||||||
let requirements = vec![Requirement::from_str("black<=23.9.1").unwrap()];
|
let requirements = vec![Requirement::from_str("black<=23.9.1").unwrap()];
|
||||||
let constraints = vec![];
|
let constraints = vec![];
|
||||||
let resolver = Resolver::new(requirements, constraints, &MARKERS_311, &TAGS_311, &client);
|
let resolver = Resolver::new(
|
||||||
|
requirements,
|
||||||
|
constraints,
|
||||||
|
ResolutionMode::default(),
|
||||||
|
&MARKERS_311,
|
||||||
|
&TAGS_311,
|
||||||
|
&client,
|
||||||
|
);
|
||||||
let resolution = resolver.resolve().await?;
|
let resolution = resolver.resolve().await?;
|
||||||
|
|
||||||
insta::assert_display_snapshot!(resolution);
|
insta::assert_display_snapshot!(resolution);
|
||||||
|
@ -52,7 +66,14 @@ async fn black_colorama() -> Result<()> {
|
||||||
|
|
||||||
let requirements = vec![Requirement::from_str("black[colorama]<=23.9.1").unwrap()];
|
let requirements = vec![Requirement::from_str("black[colorama]<=23.9.1").unwrap()];
|
||||||
let constraints = vec![];
|
let constraints = vec![];
|
||||||
let resolver = Resolver::new(requirements, constraints, &MARKERS_311, &TAGS_311, &client);
|
let resolver = Resolver::new(
|
||||||
|
requirements,
|
||||||
|
constraints,
|
||||||
|
ResolutionMode::default(),
|
||||||
|
&MARKERS_311,
|
||||||
|
&TAGS_311,
|
||||||
|
&client,
|
||||||
|
);
|
||||||
let resolution = resolver.resolve().await?;
|
let resolution = resolver.resolve().await?;
|
||||||
|
|
||||||
insta::assert_display_snapshot!(resolution);
|
insta::assert_display_snapshot!(resolution);
|
||||||
|
@ -68,7 +89,14 @@ async fn black_python_310() -> Result<()> {
|
||||||
|
|
||||||
let requirements = vec![Requirement::from_str("black<=23.9.1").unwrap()];
|
let requirements = vec![Requirement::from_str("black<=23.9.1").unwrap()];
|
||||||
let constraints = vec![];
|
let constraints = vec![];
|
||||||
let resolver = Resolver::new(requirements, constraints, &MARKERS_310, &TAGS_310, &client);
|
let resolver = Resolver::new(
|
||||||
|
requirements,
|
||||||
|
constraints,
|
||||||
|
ResolutionMode::default(),
|
||||||
|
&MARKERS_310,
|
||||||
|
&TAGS_310,
|
||||||
|
&client,
|
||||||
|
);
|
||||||
let resolution = resolver.resolve().await?;
|
let resolution = resolver.resolve().await?;
|
||||||
|
|
||||||
insta::assert_display_snapshot!(resolution);
|
insta::assert_display_snapshot!(resolution);
|
||||||
|
@ -86,7 +114,14 @@ async fn black_mypy_extensions() -> Result<()> {
|
||||||
|
|
||||||
let requirements = vec![Requirement::from_str("black<=23.9.1").unwrap()];
|
let requirements = vec![Requirement::from_str("black<=23.9.1").unwrap()];
|
||||||
let constraints = vec![Requirement::from_str("mypy-extensions<1").unwrap()];
|
let constraints = vec![Requirement::from_str("mypy-extensions<1").unwrap()];
|
||||||
let resolver = Resolver::new(requirements, constraints, &MARKERS_311, &TAGS_311, &client);
|
let resolver = Resolver::new(
|
||||||
|
requirements,
|
||||||
|
constraints,
|
||||||
|
ResolutionMode::default(),
|
||||||
|
&MARKERS_311,
|
||||||
|
&TAGS_311,
|
||||||
|
&client,
|
||||||
|
);
|
||||||
let resolution = resolver.resolve().await?;
|
let resolution = resolver.resolve().await?;
|
||||||
|
|
||||||
insta::assert_display_snapshot!(resolution);
|
insta::assert_display_snapshot!(resolution);
|
||||||
|
@ -104,7 +139,14 @@ async fn black_mypy_extensions_extra() -> Result<()> {
|
||||||
|
|
||||||
let requirements = vec![Requirement::from_str("black<=23.9.1").unwrap()];
|
let requirements = vec![Requirement::from_str("black<=23.9.1").unwrap()];
|
||||||
let constraints = vec![Requirement::from_str("mypy-extensions[extra]<1").unwrap()];
|
let constraints = vec![Requirement::from_str("mypy-extensions[extra]<1").unwrap()];
|
||||||
let resolver = Resolver::new(requirements, constraints, &MARKERS_311, &TAGS_311, &client);
|
let resolver = Resolver::new(
|
||||||
|
requirements,
|
||||||
|
constraints,
|
||||||
|
ResolutionMode::default(),
|
||||||
|
&MARKERS_311,
|
||||||
|
&TAGS_311,
|
||||||
|
&client,
|
||||||
|
);
|
||||||
let resolution = resolver.resolve().await?;
|
let resolution = resolver.resolve().await?;
|
||||||
|
|
||||||
insta::assert_display_snapshot!(resolution);
|
insta::assert_display_snapshot!(resolution);
|
||||||
|
@ -122,7 +164,60 @@ async fn black_flake8() -> Result<()> {
|
||||||
|
|
||||||
let requirements = vec![Requirement::from_str("black<=23.9.1").unwrap()];
|
let requirements = vec![Requirement::from_str("black<=23.9.1").unwrap()];
|
||||||
let constraints = vec![Requirement::from_str("flake8<1").unwrap()];
|
let constraints = vec![Requirement::from_str("flake8<1").unwrap()];
|
||||||
let resolver = Resolver::new(requirements, constraints, &MARKERS_311, &TAGS_311, &client);
|
let resolver = Resolver::new(
|
||||||
|
requirements,
|
||||||
|
constraints,
|
||||||
|
ResolutionMode::default(),
|
||||||
|
&MARKERS_311,
|
||||||
|
&TAGS_311,
|
||||||
|
&client,
|
||||||
|
);
|
||||||
|
let resolution = resolver.resolve().await?;
|
||||||
|
|
||||||
|
insta::assert_display_snapshot!(resolution);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn black_lowest() -> Result<()> {
|
||||||
|
colored::control::set_override(false);
|
||||||
|
|
||||||
|
let client = PypiClientBuilder::default().build();
|
||||||
|
|
||||||
|
let requirements = vec![Requirement::from_str("black>21").unwrap()];
|
||||||
|
let constraints = vec![];
|
||||||
|
let resolver = Resolver::new(
|
||||||
|
requirements,
|
||||||
|
constraints,
|
||||||
|
ResolutionMode::Lowest,
|
||||||
|
&MARKERS_311,
|
||||||
|
&TAGS_311,
|
||||||
|
&client,
|
||||||
|
);
|
||||||
|
let resolution = resolver.resolve().await?;
|
||||||
|
|
||||||
|
insta::assert_display_snapshot!(resolution);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn black_lowest_direct() -> Result<()> {
|
||||||
|
colored::control::set_override(false);
|
||||||
|
|
||||||
|
let client = PypiClientBuilder::default().build();
|
||||||
|
|
||||||
|
let requirements = vec![Requirement::from_str("black>21").unwrap()];
|
||||||
|
let constraints = vec![];
|
||||||
|
let resolver = Resolver::new(
|
||||||
|
requirements,
|
||||||
|
constraints,
|
||||||
|
ResolutionMode::LowestDirect,
|
||||||
|
&MARKERS_311,
|
||||||
|
&TAGS_311,
|
||||||
|
&client,
|
||||||
|
);
|
||||||
let resolution = resolver.resolve().await?;
|
let resolution = resolver.resolve().await?;
|
||||||
|
|
||||||
insta::assert_display_snapshot!(resolution);
|
insta::assert_display_snapshot!(resolution);
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-resolver/tests/resolver.rs
|
||||||
|
expression: resolution
|
||||||
|
---
|
||||||
|
appdirs==1.4.0
|
||||||
|
# via black
|
||||||
|
black==21.4b0
|
||||||
|
click==7.1.2
|
||||||
|
# via black
|
||||||
|
mypy-extensions==0.4.3
|
||||||
|
# via black
|
||||||
|
pathspec==0.7.0
|
||||||
|
# via black
|
||||||
|
regex==2022.9.11
|
||||||
|
# via black
|
||||||
|
toml==0.10.1
|
||||||
|
# via black
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-resolver/tests/resolver.rs
|
||||||
|
expression: resolution
|
||||||
|
---
|
||||||
|
appdirs==1.4.4
|
||||||
|
# via black
|
||||||
|
black==21.4b0
|
||||||
|
click==8.1.7
|
||||||
|
# via black
|
||||||
|
mypy-extensions==1.0.0
|
||||||
|
# via black
|
||||||
|
pathspec==0.11.2
|
||||||
|
# via black
|
||||||
|
regex==2023.10.3
|
||||||
|
# via black
|
||||||
|
toml==0.10.2
|
||||||
|
# via black
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue