mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 13:25:00 +00:00
Show requirement sources in pip-compile
output (#149)
Builds up a complete resolved graph from PubGrub, and shows the sources that led to each package being included in the resolution, like `pip-compile`. Closes https://github.com/astral-sh/puffin/issues/60.
This commit is contained in:
parent
e662fe341b
commit
8001c792e7
20 changed files with 336 additions and 233 deletions
18
Cargo.lock
generated
18
Cargo.lock
generated
|
@ -737,6 +737,12 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.28"
|
||||
|
@ -1795,6 +1801,16 @@ version = "2.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap 2.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.13"
|
||||
|
@ -2139,9 +2155,11 @@ dependencies = [
|
|||
"bitflags 2.4.1",
|
||||
"colored",
|
||||
"futures",
|
||||
"insta",
|
||||
"once_cell",
|
||||
"pep440_rs 0.3.12",
|
||||
"pep508_rs",
|
||||
"petgraph",
|
||||
"platform-host",
|
||||
"platform-tags",
|
||||
"pubgrub",
|
||||
|
|
|
@ -71,11 +71,11 @@ pub(crate) async fn pip_compile(
|
|||
derivation_tree.collapse_no_versions();
|
||||
#[allow(clippy::print_stderr)]
|
||||
{
|
||||
eprintln!("{}: {}", "error".red().bold(), "no solution found".bold());
|
||||
eprintln!(
|
||||
"{}",
|
||||
pubgrub::report::DefaultStringReporter::report(&derivation_tree)
|
||||
);
|
||||
let report = miette::Report::msg(pubgrub::report::DefaultStringReporter::report(
|
||||
&derivation_tree,
|
||||
))
|
||||
.context("No solution found when resolving dependencies:");
|
||||
eprint!("{report:?}");
|
||||
}
|
||||
return Ok(ExitStatus::Failure);
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ pub(crate) async fn pip_compile(
|
|||
"{}",
|
||||
format!("# {}", env::args().join(" ")).green()
|
||||
)?;
|
||||
writeln!(writer, "{resolution}")?;
|
||||
write!(writer, "{resolution}")?;
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ use puffin_installer::{
|
|||
};
|
||||
use puffin_interpreter::PythonExecutable;
|
||||
use puffin_package::package_name::PackageName;
|
||||
use puffin_resolver::Resolution;
|
||||
|
||||
use crate::commands::reporters::{
|
||||
DownloadReporter, InstallReporter, UnzipReporter, WheelFinderReporter,
|
||||
|
@ -93,8 +92,8 @@ pub(crate) async fn sync_requirements(
|
|||
let client = PypiClientBuilder::default().cache(cache).build();
|
||||
|
||||
// Resolve the dependencies.
|
||||
let resolution = if remote.is_empty() {
|
||||
Resolution::default()
|
||||
let remote = if remote.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
|
@ -115,33 +114,32 @@ pub(crate) async fn sync_requirements(
|
|||
)?;
|
||||
|
||||
resolution
|
||||
.into_files()
|
||||
.map(RemoteDistribution::from_file)
|
||||
.collect::<Result<Vec<_>>>()?
|
||||
};
|
||||
|
||||
// Download any missing distributions.
|
||||
let staging = tempfile::tempdir()?;
|
||||
let uncached = resolution
|
||||
.into_files()
|
||||
.map(RemoteDistribution::from_file)
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let downloads = if uncached.is_empty() {
|
||||
let downloads = if remote.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
let downloader = puffin_installer::Downloader::new(&client, cache)
|
||||
.with_reporter(DownloadReporter::from(printer).with_length(uncached.len() as u64));
|
||||
.with_reporter(DownloadReporter::from(printer).with_length(remote.len() as u64));
|
||||
|
||||
let downloads = downloader
|
||||
.download(&uncached, cache.unwrap_or(staging.path()))
|
||||
.download(&remote, cache.unwrap_or(staging.path()))
|
||||
.await?;
|
||||
|
||||
let s = if uncached.len() == 1 { "" } else { "s" };
|
||||
let s = if remote.len() == 1 { "" } else { "s" };
|
||||
writeln!(
|
||||
printer,
|
||||
"{}",
|
||||
format!(
|
||||
"Downloaded {} in {}",
|
||||
format!("{} package{}", uncached.len(), s).bold(),
|
||||
format!("{} package{}", remote.len(), s).bold(),
|
||||
elapsed(start.elapsed())
|
||||
)
|
||||
.dimmed()
|
||||
|
|
|
@ -28,6 +28,8 @@ thiserror = { workspace = true }
|
|||
tokio = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
waitmap = { workspace = true }
|
||||
petgraph = "0.6.4"
|
||||
|
||||
[dev-dependencies]
|
||||
once_cell = { version = "1.18.0" }
|
||||
insta = { version = "1.34.0" }
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
pub use error::ResolveError;
|
||||
pub use resolution::{PinnedPackage, Resolution};
|
||||
pub use resolution::PinnedPackage;
|
||||
pub use resolver::Resolver;
|
||||
pub use wheel_finder::{Reporter, WheelFinder};
|
||||
|
||||
|
|
|
@ -38,12 +38,6 @@ impl pubgrub::version::Version for PubGrubVersion {
|
|||
}
|
||||
}
|
||||
|
||||
// impl From<PubGrubVersion> for Range<PubGrubVersion> {
|
||||
// fn from(value: PubGrubVersion) -> Self {
|
||||
// Range::from(value)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<'a> From<&'a PubGrubVersion> for &'a pep440_rs::Version {
|
||||
fn from(version: &'a PubGrubVersion) -> Self {
|
||||
&version.0
|
||||
|
|
|
@ -1,53 +1,17 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
use colored::Colorize;
|
||||
use petgraph::visit::EdgeRef;
|
||||
use pubgrub::range::Range;
|
||||
use pubgrub::solver::{Kind, State};
|
||||
use pubgrub::type_aliases::SelectedDependencies;
|
||||
|
||||
use pep440_rs::Version;
|
||||
use puffin_client::File;
|
||||
use puffin_package::package_name::PackageName;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Resolution(BTreeMap<PackageName, PinnedPackage>);
|
||||
|
||||
impl Resolution {
|
||||
/// Create a new resolution from the given pinned packages.
|
||||
pub(crate) fn new(packages: BTreeMap<PackageName, PinnedPackage>) -> Self {
|
||||
Self(packages)
|
||||
}
|
||||
|
||||
/// Iterate over the pinned packages in this resolution.
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&PackageName, &PinnedPackage)> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
/// Iterate over the wheels in this resolution.
|
||||
pub fn into_files(self) -> impl Iterator<Item = File> {
|
||||
self.0.into_values().map(|package| package.file)
|
||||
}
|
||||
|
||||
/// Return the number of pinned packages in this resolution.
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
/// Return `true` if there are no pinned packages in this resolution.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the resolution in the `{name}=={version}` format of requirements.txt that pip uses.
|
||||
impl std::fmt::Display for Resolution {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut first = true;
|
||||
for (name, pin) in self.iter() {
|
||||
if !first {
|
||||
writeln!(f)?;
|
||||
}
|
||||
first = false;
|
||||
write!(f, "{}=={}", name, pin.version())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
use crate::pubgrub::package::PubGrubPackage;
|
||||
use crate::pubgrub::version::PubGrubVersion;
|
||||
|
||||
/// A package pinned at a specific version.
|
||||
#[derive(Debug)]
|
||||
|
@ -82,3 +46,146 @@ impl PinnedPackage {
|
|||
&self.file
|
||||
}
|
||||
}
|
||||
|
||||
/// A set of packages pinned at specific versions.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Resolution(BTreeMap<PackageName, PinnedPackage>);
|
||||
|
||||
impl Resolution {
|
||||
/// Create a new resolution from the given pinned packages.
|
||||
pub(crate) fn new(packages: BTreeMap<PackageName, PinnedPackage>) -> Self {
|
||||
Self(packages)
|
||||
}
|
||||
|
||||
/// Iterate over the pinned packages in this resolution.
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&PackageName, &PinnedPackage)> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
/// Iterate over the wheels in this resolution.
|
||||
pub fn into_files(self) -> impl Iterator<Item = File> {
|
||||
self.0.into_values().map(|package| package.file)
|
||||
}
|
||||
|
||||
/// Return the number of pinned packages in this resolution.
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
/// Return `true` if there are no pinned packages in this resolution.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
/// A complete resolution graph in which every node represents a pinned package and every edge
|
||||
/// represents a dependency between two pinned packages.
|
||||
#[derive(Debug)]
|
||||
pub struct Graph(petgraph::graph::Graph<PinnedPackage, (), petgraph::Directed>);
|
||||
|
||||
impl Graph {
|
||||
/// Create a new graph from the resolved `PubGrub` state.
|
||||
pub fn from_state(
|
||||
selection: &SelectedDependencies<PubGrubPackage, PubGrubVersion>,
|
||||
pins: &HashMap<PackageName, HashMap<Version, File>>,
|
||||
state: &State<PubGrubPackage, Range<PubGrubVersion>>,
|
||||
) -> Self {
|
||||
// TODO(charlie): petgraph is a really heavy and unnecessary dependency here. We should
|
||||
// write our own graph, given that our requirements are so simple.
|
||||
let mut graph = petgraph::graph::Graph::with_capacity(selection.len(), selection.len());
|
||||
|
||||
// Add every package to the graph.
|
||||
let mut inverse = HashMap::with_capacity(selection.len());
|
||||
for (package, version) in selection {
|
||||
let PubGrubPackage::Package(package_name, None) = package else {
|
||||
continue;
|
||||
};
|
||||
let version = Version::from(version.clone());
|
||||
let file = pins
|
||||
.get(package_name)
|
||||
.and_then(|versions| versions.get(&version))
|
||||
.unwrap()
|
||||
.clone();
|
||||
let pinned_package = PinnedPackage::new(package_name.clone(), version, file);
|
||||
let index = graph.add_node(pinned_package);
|
||||
|
||||
inverse.insert(package_name, index);
|
||||
}
|
||||
|
||||
// Add every edge to the graph.
|
||||
for (package, version) in selection {
|
||||
for id in &state.incompatibilities[package] {
|
||||
if let Kind::FromDependencyOf(self_package, self_version, dependency_package, _) =
|
||||
&state.incompatibility_store[*id].kind
|
||||
{
|
||||
let PubGrubPackage::Package(self_package, None) = self_package else {
|
||||
continue;
|
||||
};
|
||||
let PubGrubPackage::Package(dependency_package, None) = dependency_package
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if self_version.contains(version) {
|
||||
let self_index = &inverse[self_package];
|
||||
let dependency_index = &inverse[dependency_package];
|
||||
graph.add_edge(*dependency_index, *self_index, ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Self(graph)
|
||||
}
|
||||
|
||||
/// Return the number of packages in the graph.
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.node_count()
|
||||
}
|
||||
|
||||
/// Return `true` if there are no packages in the graph.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.node_count() == 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the graph in the `{name}=={version}` format of requirements.txt that pip uses.
|
||||
impl std::fmt::Display for Graph {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// Collect and sort all packages.
|
||||
let mut nodes = self
|
||||
.0
|
||||
.node_indices()
|
||||
.map(|node| (node, &self.0[node]))
|
||||
.collect::<Vec<_>>();
|
||||
nodes.sort_unstable_by_key(|(_, package)| package.name());
|
||||
|
||||
// Print out the dependency graph.
|
||||
for (index, package) in nodes {
|
||||
writeln!(f, "{}=={}", package.name(), package.version())?;
|
||||
|
||||
let mut edges = self
|
||||
.0
|
||||
.edges(index)
|
||||
.map(|edge| &self.0[edge.target()])
|
||||
.collect::<Vec<_>>();
|
||||
edges.sort_unstable_by_key(|package| package.name());
|
||||
|
||||
match edges.len() {
|
||||
0 => {}
|
||||
1 => {
|
||||
for dependency in edges {
|
||||
writeln!(f, "{}", format!(" # via {}", dependency.name()).green())?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
writeln!(f, "{}", " # via".green())?;
|
||||
for dependency in edges {
|
||||
writeln!(f, "{}", format!(" # {}", dependency.name()).green())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -13,7 +13,7 @@ use futures::{pin_mut, FutureExt, StreamExt, TryFutureExt};
|
|||
use pubgrub::error::PubGrubError;
|
||||
use pubgrub::range::Range;
|
||||
use pubgrub::solver::{Incompatibility, State};
|
||||
use pubgrub::type_aliases::{DependencyConstraints, SelectedDependencies};
|
||||
use pubgrub::type_aliases::DependencyConstraints;
|
||||
use tokio::select;
|
||||
use tracing::{debug, trace};
|
||||
use waitmap::WaitMap;
|
||||
|
@ -30,7 +30,7 @@ use crate::error::ResolveError;
|
|||
use crate::pubgrub::package::PubGrubPackage;
|
||||
use crate::pubgrub::version::{PubGrubVersion, MIN_VERSION};
|
||||
use crate::pubgrub::{iter_requirements, version_range};
|
||||
use crate::resolution::{PinnedPackage, Resolution};
|
||||
use crate::resolution::Graph;
|
||||
|
||||
pub struct Resolver<'a> {
|
||||
requirements: Vec<Requirement>,
|
||||
|
@ -61,7 +61,7 @@ impl<'a> Resolver<'a> {
|
|||
}
|
||||
|
||||
/// Resolve a set of requirements into a set of pinned versions.
|
||||
pub async fn resolve(self) -> Result<Resolution, ResolveError> {
|
||||
pub async fn resolve(self) -> Result<Graph, ResolveError> {
|
||||
// A channel to fetch package metadata (e.g., given `flask`, fetch all versions) and version
|
||||
// metadata (e.g., given `flask==1.0.0`, fetch the metadata for that version).
|
||||
let (request_sink, request_stream) = futures::channel::mpsc::unbounded();
|
||||
|
@ -93,14 +93,14 @@ impl<'a> Resolver<'a> {
|
|||
}
|
||||
};
|
||||
|
||||
Ok(resolution.into())
|
||||
Ok(resolution)
|
||||
}
|
||||
|
||||
/// Run the `PubGrub` solver.
|
||||
async fn solve(
|
||||
&self,
|
||||
request_sink: &futures::channel::mpsc::UnboundedSender<Request>,
|
||||
) -> Result<PubGrubResolution, ResolveError> {
|
||||
) -> Result<Graph, ResolveError> {
|
||||
let root = PubGrubPackage::Root;
|
||||
|
||||
// Keep track of the packages for which we've requested metadata.
|
||||
|
@ -126,7 +126,8 @@ impl<'a> Resolver<'a> {
|
|||
)
|
||||
.into());
|
||||
};
|
||||
return Ok(PubGrubResolution { selection, pins });
|
||||
|
||||
return Ok(Graph::from_state(&selection, &pins, &state));
|
||||
};
|
||||
|
||||
// Choose a package version.
|
||||
|
@ -306,8 +307,6 @@ impl<'a> Resolver<'a> {
|
|||
PubGrubPackage::Root => Ok((package, Some(MIN_VERSION.clone()))),
|
||||
PubGrubPackage::Package(package_name, _) => {
|
||||
// Wait for the metadata to be available.
|
||||
// TODO(charlie): Ideally, we'd choose the first package for which metadata is
|
||||
// available.
|
||||
let entry = self.cache.packages.wait(package_name).await.unwrap();
|
||||
let simple_json = entry.value();
|
||||
|
||||
|
@ -572,33 +571,3 @@ enum Dependencies {
|
|||
/// Container for all available package versions.
|
||||
Known(DependencyConstraints<PubGrubPackage, Range<PubGrubVersion>>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PubGrubResolution {
|
||||
/// The selected dependencies.
|
||||
selection: SelectedDependencies<PubGrubPackage, PubGrubVersion>,
|
||||
/// The selected file (source or built distribution) for each package.
|
||||
pins: HashMap<PackageName, HashMap<pep440_rs::Version, File>>,
|
||||
}
|
||||
|
||||
impl From<PubGrubResolution> for Resolution {
|
||||
fn from(value: PubGrubResolution) -> Self {
|
||||
let mut packages = BTreeMap::new();
|
||||
for (package, version) in value.selection {
|
||||
let PubGrubPackage::Package(package_name, None) = package else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let version = pep440_rs::Version::from(version);
|
||||
let file = value
|
||||
.pins
|
||||
.get(&package_name)
|
||||
.and_then(|versions| versions.get(&version))
|
||||
.unwrap()
|
||||
.clone();
|
||||
let pinned_package = PinnedPackage::new(package_name.clone(), version, file);
|
||||
packages.insert(package_name, pinned_package);
|
||||
}
|
||||
Resolution::new(packages)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ use puffin_resolver::Resolver;
|
|||
|
||||
#[tokio::test]
|
||||
async fn pylint() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let client = PypiClientBuilder::default().build();
|
||||
|
||||
let requirements = vec![Requirement::from_str("pylint==2.3.0").unwrap()];
|
||||
|
@ -21,22 +23,15 @@ async fn pylint() -> Result<()> {
|
|||
let resolver = Resolver::new(requirements, constraints, &MARKERS_311, &TAGS_311, &client);
|
||||
let resolution = resolver.resolve().await?;
|
||||
|
||||
assert_eq!(
|
||||
format!("{resolution}"),
|
||||
[
|
||||
"astroid==3.0.1",
|
||||
"isort==6.0.0b2",
|
||||
"mccabe==0.7.0",
|
||||
"pylint==2.3.0"
|
||||
]
|
||||
.join("\n")
|
||||
);
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn black() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let client = PypiClientBuilder::default().build();
|
||||
|
||||
let requirements = vec![Requirement::from_str("black<=23.9.1").unwrap()];
|
||||
|
@ -44,24 +39,15 @@ async fn black() -> Result<()> {
|
|||
let resolver = Resolver::new(requirements, constraints, &MARKERS_311, &TAGS_311, &client);
|
||||
let resolution = resolver.resolve().await?;
|
||||
|
||||
assert_eq!(
|
||||
format!("{resolution}"),
|
||||
[
|
||||
"black==23.9.1",
|
||||
"click==8.1.7",
|
||||
"mypy-extensions==1.0.0",
|
||||
"packaging==23.2",
|
||||
"pathspec==0.11.2",
|
||||
"platformdirs==3.11.0"
|
||||
]
|
||||
.join("\n")
|
||||
);
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn black_colorama() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let client = PypiClientBuilder::default().build();
|
||||
|
||||
let requirements = vec![Requirement::from_str("black[colorama]<=23.9.1").unwrap()];
|
||||
|
@ -69,25 +55,15 @@ async fn black_colorama() -> Result<()> {
|
|||
let resolver = Resolver::new(requirements, constraints, &MARKERS_311, &TAGS_311, &client);
|
||||
let resolution = resolver.resolve().await?;
|
||||
|
||||
assert_eq!(
|
||||
format!("{resolution}"),
|
||||
[
|
||||
"black==23.9.1",
|
||||
"click==8.1.7",
|
||||
"colorama==0.4.6",
|
||||
"mypy-extensions==1.0.0",
|
||||
"packaging==23.2",
|
||||
"pathspec==0.11.2",
|
||||
"platformdirs==3.11.0"
|
||||
]
|
||||
.join("\n")
|
||||
);
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn black_python_310() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let client = PypiClientBuilder::default().build();
|
||||
|
||||
let requirements = vec![Requirement::from_str("black<=23.9.1").unwrap()];
|
||||
|
@ -95,20 +71,7 @@ async fn black_python_310() -> Result<()> {
|
|||
let resolver = Resolver::new(requirements, constraints, &MARKERS_310, &TAGS_310, &client);
|
||||
let resolution = resolver.resolve().await?;
|
||||
|
||||
assert_eq!(
|
||||
format!("{resolution}"),
|
||||
[
|
||||
"black==23.9.1",
|
||||
"click==8.1.7",
|
||||
"mypy-extensions==1.0.0",
|
||||
"packaging==23.2",
|
||||
"pathspec==0.11.2",
|
||||
"platformdirs==3.11.0",
|
||||
"tomli==2.0.1",
|
||||
"typing-extensions==4.8.0"
|
||||
]
|
||||
.join("\n")
|
||||
);
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -117,6 +80,8 @@ async fn black_python_310() -> Result<()> {
|
|||
/// respected.
|
||||
#[tokio::test]
|
||||
async fn black_mypy_extensions() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let client = PypiClientBuilder::default().build();
|
||||
|
||||
let requirements = vec![Requirement::from_str("black<=23.9.1").unwrap()];
|
||||
|
@ -124,18 +89,7 @@ async fn black_mypy_extensions() -> Result<()> {
|
|||
let resolver = Resolver::new(requirements, constraints, &MARKERS_311, &TAGS_311, &client);
|
||||
let resolution = resolver.resolve().await?;
|
||||
|
||||
assert_eq!(
|
||||
format!("{resolution}"),
|
||||
[
|
||||
"black==23.9.1",
|
||||
"click==8.1.7",
|
||||
"mypy-extensions==0.4.3",
|
||||
"packaging==23.2",
|
||||
"pathspec==0.11.2",
|
||||
"platformdirs==3.11.0"
|
||||
]
|
||||
.join("\n")
|
||||
);
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -144,6 +98,8 @@ async fn black_mypy_extensions() -> Result<()> {
|
|||
/// ignored when resolving constraints.
|
||||
#[tokio::test]
|
||||
async fn black_mypy_extensions_extra() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let client = PypiClientBuilder::default().build();
|
||||
|
||||
let requirements = vec![Requirement::from_str("black<=23.9.1").unwrap()];
|
||||
|
@ -151,18 +107,7 @@ async fn black_mypy_extensions_extra() -> Result<()> {
|
|||
let resolver = Resolver::new(requirements, constraints, &MARKERS_311, &TAGS_311, &client);
|
||||
let resolution = resolver.resolve().await?;
|
||||
|
||||
assert_eq!(
|
||||
format!("{resolution}"),
|
||||
[
|
||||
"black==23.9.1",
|
||||
"click==8.1.7",
|
||||
"mypy-extensions==0.4.3",
|
||||
"packaging==23.2",
|
||||
"pathspec==0.11.2",
|
||||
"platformdirs==3.11.0"
|
||||
]
|
||||
.join("\n")
|
||||
);
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -171,6 +116,8 @@ async fn black_mypy_extensions_extra() -> Result<()> {
|
|||
/// introduce new dependencies.
|
||||
#[tokio::test]
|
||||
async fn black_flake8() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let client = PypiClientBuilder::default().build();
|
||||
|
||||
let requirements = vec![Requirement::from_str("black<=23.9.1").unwrap()];
|
||||
|
@ -178,49 +125,7 @@ async fn black_flake8() -> Result<()> {
|
|||
let resolver = Resolver::new(requirements, constraints, &MARKERS_311, &TAGS_311, &client);
|
||||
let resolution = resolver.resolve().await?;
|
||||
|
||||
assert_eq!(
|
||||
format!("{resolution}"),
|
||||
[
|
||||
"black==23.9.1",
|
||||
"click==8.1.7",
|
||||
"mypy-extensions==1.0.0",
|
||||
"packaging==23.2",
|
||||
"pathspec==0.11.2",
|
||||
"platformdirs==3.11.0"
|
||||
]
|
||||
.join("\n")
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn htmldate() -> Result<()> {
|
||||
let client = PypiClientBuilder::default().build();
|
||||
|
||||
let requirements = vec![Requirement::from_str("htmldate<=1.5.0").unwrap()];
|
||||
let constraints = vec![];
|
||||
let resolver = Resolver::new(requirements, constraints, &MARKERS_311, &TAGS_311, &client);
|
||||
let resolution = resolver.resolve().await?;
|
||||
|
||||
// Resolves to `htmldate==1.4.3` (rather than `htmldate==1.5.2`) because `htmldate==1.5.2` has
|
||||
// a dependency on `lxml` versions that don't provide universal wheels.
|
||||
assert_eq!(
|
||||
format!("{resolution}"),
|
||||
[
|
||||
"charset-normalizer==3.3.0",
|
||||
"dateparser==1.1.8",
|
||||
"htmldate==1.4.3",
|
||||
"lxml==4.9.3",
|
||||
"python-dateutil==2.8.2",
|
||||
"pytz==2023.3.post1",
|
||||
"regex==2023.10.3",
|
||||
"six==1.16.0",
|
||||
"tzlocal==5.1",
|
||||
"urllib3==2.0.7"
|
||||
]
|
||||
.join("\n")
|
||||
);
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
16
crates/puffin-resolver/tests/snapshots/resolver__black.snap
Normal file
16
crates/puffin-resolver/tests/snapshots/resolver__black.snap
Normal file
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: crates/puffin-resolver/tests/resolver.rs
|
||||
expression: resolution
|
||||
---
|
||||
black==23.9.1
|
||||
click==8.1.7
|
||||
# via black
|
||||
mypy-extensions==1.0.0
|
||||
# via black
|
||||
packaging==23.2
|
||||
# via black
|
||||
pathspec==0.11.2
|
||||
# via black
|
||||
platformdirs==3.11.0
|
||||
# via black
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
source: crates/puffin-resolver/tests/resolver.rs
|
||||
expression: resolution
|
||||
---
|
||||
black==23.9.1
|
||||
click==8.1.7
|
||||
# via black
|
||||
colorama==0.4.6
|
||||
mypy-extensions==1.0.0
|
||||
# via black
|
||||
packaging==23.2
|
||||
# via black
|
||||
pathspec==0.11.2
|
||||
# via black
|
||||
platformdirs==3.11.0
|
||||
# via black
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: crates/puffin-resolver/tests/resolver.rs
|
||||
expression: resolution
|
||||
---
|
||||
black==23.9.1
|
||||
click==8.1.7
|
||||
# via black
|
||||
mypy-extensions==1.0.0
|
||||
# via black
|
||||
packaging==23.2
|
||||
# via black
|
||||
pathspec==0.11.2
|
||||
# via black
|
||||
platformdirs==3.11.0
|
||||
# via black
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: crates/puffin-resolver/tests/resolver.rs
|
||||
expression: resolution
|
||||
---
|
||||
black==23.9.1
|
||||
click==8.1.7
|
||||
# via black
|
||||
mypy-extensions==0.4.3
|
||||
# via black
|
||||
packaging==23.2
|
||||
# via black
|
||||
pathspec==0.11.2
|
||||
# via black
|
||||
platformdirs==3.11.0
|
||||
# via black
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: crates/puffin-resolver/tests/resolver.rs
|
||||
expression: resolution
|
||||
---
|
||||
black==23.9.1
|
||||
click==8.1.7
|
||||
# via black
|
||||
mypy-extensions==0.4.3
|
||||
# via black
|
||||
packaging==23.2
|
||||
# via black
|
||||
pathspec==0.11.2
|
||||
# via black
|
||||
platformdirs==3.11.0
|
||||
# via black
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
source: crates/puffin-resolver/tests/resolver.rs
|
||||
expression: resolution
|
||||
---
|
||||
black==23.9.1
|
||||
click==8.1.7
|
||||
# via black
|
||||
mypy-extensions==1.0.0
|
||||
# via black
|
||||
packaging==23.2
|
||||
# via black
|
||||
pathspec==0.11.2
|
||||
# via black
|
||||
platformdirs==3.11.0
|
||||
# via black
|
||||
tomli==2.0.1
|
||||
# via black
|
||||
typing-extensions==4.8.0
|
||||
# via black
|
||||
|
12
crates/puffin-resolver/tests/snapshots/resolver__pylint.snap
Normal file
12
crates/puffin-resolver/tests/snapshots/resolver__pylint.snap
Normal file
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
source: crates/puffin-resolver/tests/resolver.rs
|
||||
expression: resolution
|
||||
---
|
||||
astroid==3.0.1
|
||||
# via pylint
|
||||
isort==6.0.0b2
|
||||
# via pylint
|
||||
mccabe==0.7.0
|
||||
# via pylint
|
||||
pylint==2.3.0
|
||||
|
2
vendor/pubgrub/src/internal/core.rs
vendored
2
vendor/pubgrub/src/internal/core.rs
vendored
|
@ -24,7 +24,7 @@ pub struct State<P: Package, VS: VersionSet> {
|
|||
root_package: P,
|
||||
root_version: VS::V,
|
||||
|
||||
incompatibilities: Map<P, Vec<IncompId<P, VS>>>,
|
||||
pub incompatibilities: Map<P, Vec<IncompId<P, VS>>>,
|
||||
|
||||
/// Store the ids of incompatibilities that are already contradicted
|
||||
/// and will stay that way until the next conflict and backtrack is operated.
|
||||
|
|
|
@ -30,15 +30,15 @@ use crate::version_set::VersionSet;
|
|||
/// [PubGrub documentation](https://github.com/dart-lang/pub/blob/master/doc/solver.md#incompatibility).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Incompatibility<P: Package, VS: VersionSet> {
|
||||
package_terms: SmallMap<P, Term<VS>>,
|
||||
kind: Kind<P, VS>,
|
||||
pub package_terms: SmallMap<P, Term<VS>>,
|
||||
pub kind: Kind<P, VS>,
|
||||
}
|
||||
|
||||
/// Type alias of unique identifiers for incompatibilities.
|
||||
pub type IncompId<P, VS> = Id<Incompatibility<P, VS>>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Kind<P: Package, VS: VersionSet> {
|
||||
pub enum Kind<P: Package, VS: VersionSet> {
|
||||
/// Initial incompatibility aiming at picking the root package for the first decision.
|
||||
NotRoot(P, VS::V),
|
||||
/// There are no versions in the given range for this package.
|
||||
|
|
3
vendor/pubgrub/src/lib.rs
vendored
3
vendor/pubgrub/src/lib.rs
vendored
|
@ -215,9 +215,6 @@
|
|||
//! with a cache, you may want to know that some versions
|
||||
//! do not exist in your cache.
|
||||
|
||||
#![allow(clippy::rc_buffer)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
pub mod error;
|
||||
pub mod package;
|
||||
pub mod range;
|
||||
|
|
2
vendor/pubgrub/src/solver.rs
vendored
2
vendor/pubgrub/src/solver.rs
vendored
|
@ -74,7 +74,7 @@ use std::error::Error;
|
|||
|
||||
use crate::error::PubGrubError;
|
||||
pub use crate::internal::core::State;
|
||||
pub use crate::internal::incompatibility::Incompatibility;
|
||||
pub use crate::internal::incompatibility::{Incompatibility, Kind};
|
||||
use crate::package::Package;
|
||||
use crate::type_aliases::{DependencyConstraints, Map, SelectedDependencies};
|
||||
use crate::version_set::VersionSet;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue