uv-resolver: use named fields for some PubGrubPackage variants

I'm planning to add another field here (markers), which puts a lot of
stress on the positional approach. So let's just switch over to named
fields.
This commit is contained in:
Andrew Gallant 2024-05-20 09:31:17 -04:00 committed by Andrew Gallant
parent 44fe0f6749
commit eac8221718
8 changed files with 216 additions and 141 deletions

View file

@ -127,7 +127,7 @@ fn collapse_extra_proxies(
) { ) {
( (
DerivationTree::External(External::FromDependencyOf( DerivationTree::External(External::FromDependencyOf(
PubGrubPackage::Extra(..), PubGrubPackage::Extra { .. },
.., ..,
)), )),
ref mut cause, ref mut cause,
@ -138,7 +138,7 @@ fn collapse_extra_proxies(
( (
ref mut cause, ref mut cause,
DerivationTree::External(External::FromDependencyOf( DerivationTree::External(External::FromDependencyOf(
PubGrubPackage::Extra(..), PubGrubPackage::Extra { .. },
.., ..,
)), )),
) => { ) => {
@ -255,8 +255,8 @@ impl NoSolutionError {
BTreeSet::from([python_requirement.target().deref().clone()]), BTreeSet::from([python_requirement.target().deref().clone()]),
); );
} }
PubGrubPackage::Extra(_, _, _) => {} PubGrubPackage::Extra { .. } => {}
PubGrubPackage::Package(name, _, _) => { PubGrubPackage::Package { name, .. } => {
// Avoid including available versions for packages that exist in the derivation // Avoid including available versions for packages that exist in the derivation
// tree, but were never visited during resolution. We _may_ have metadata for // tree, but were never visited during resolution. We _may_ have metadata for
// these packages, but it's non-deterministic, and omitting them ensures that // these packages, but it's non-deterministic, and omitting them ensures that
@ -304,7 +304,7 @@ impl NoSolutionError {
) -> Self { ) -> Self {
let mut new = FxHashMap::default(); let mut new = FxHashMap::default();
for package in self.derivation_tree.packages() { for package in self.derivation_tree.packages() {
if let PubGrubPackage::Package(name, _, _) = package { if let PubGrubPackage::Package { name, .. } = package {
if let Some(reason) = unavailable_packages.get(name) { if let Some(reason) = unavailable_packages.get(name) {
new.insert(name.clone(), reason.clone()); new.insert(name.clone(), reason.clone());
} }
@ -322,7 +322,7 @@ impl NoSolutionError {
) -> Self { ) -> Self {
let mut new = FxHashMap::default(); let mut new = FxHashMap::default();
for package in self.derivation_tree.packages() { for package in self.derivation_tree.packages() {
if let PubGrubPackage::Package(name, _, _) = package { if let PubGrubPackage::Package { name, .. } = package {
if let Some(versions) = incomplete_packages.get(name) { if let Some(versions) = incomplete_packages.get(name) {
for entry in versions.iter() { for entry in versions.iter() {
let (version, reason) = entry.pair(); let (version, reason) = entry.pair();

View file

@ -103,7 +103,7 @@ fn add_requirements(
let PubGrubRequirement { package, version } = result?; let PubGrubRequirement { package, version } = result?;
match &package { match &package {
PubGrubPackage::Package(name, ..) => { PubGrubPackage::Package { name, .. } => {
// Detect self-dependencies. // Detect self-dependencies.
if source_name.is_some_and(|source_name| source_name == name) { if source_name.is_some_and(|source_name| source_name == name) {
warn!("{name} has a dependency on itself"); warn!("{name} has a dependency on itself");
@ -112,7 +112,7 @@ fn add_requirements(
dependencies.push((package.clone(), version.clone())); dependencies.push((package.clone(), version.clone()));
} }
PubGrubPackage::Extra(name, extra, ..) => { PubGrubPackage::Extra { name, extra, .. } => {
// Recursively add the dependencies of the current package (e.g., `black` depending on // Recursively add the dependencies of the current package (e.g., `black` depending on
// `black[colorama]`). // `black[colorama]`).
if source_name.is_some_and(|source_name| source_name == name) { if source_name.is_some_and(|source_name| source_name == name) {
@ -158,7 +158,7 @@ fn add_requirements(
PubGrubRequirement::from_constraint(constraint, urls, locals)?; PubGrubRequirement::from_constraint(constraint, urls, locals)?;
// Ignore self-dependencies. // Ignore self-dependencies.
if let PubGrubPackage::Package(name, ..) = &package { if let PubGrubPackage::Package { name, .. } = &package {
// Detect self-dependencies. // Detect self-dependencies.
if source_name.is_some_and(|source_name| source_name == name) { if source_name.is_some_and(|source_name| source_name == name) {
warn!("{name} has a dependency on itself"); warn!("{name} has a dependency on itself");
@ -244,11 +244,11 @@ impl PubGrubRequirement {
} }
Ok(Self { Ok(Self {
package: PubGrubPackage::Package( package: PubGrubPackage::Package {
requirement.name.clone(), name: requirement.name.clone(),
extra, extra,
Some(expected.clone()), url: Some(expected.clone()),
), },
version: Range::full(), version: Range::full(),
}) })
} }
@ -269,11 +269,11 @@ impl PubGrubRequirement {
} }
Ok(Self { Ok(Self {
package: PubGrubPackage::Package( package: PubGrubPackage::Package {
requirement.name.clone(), name: requirement.name.clone(),
extra, extra,
Some(expected.clone()), url: Some(expected.clone()),
), },
version: Range::full(), version: Range::full(),
}) })
} }
@ -294,11 +294,11 @@ impl PubGrubRequirement {
} }
Ok(Self { Ok(Self {
package: PubGrubPackage::Package( package: PubGrubPackage::Package {
requirement.name.clone(), name: requirement.name.clone(),
extra, extra,
Some(expected.clone()), url: Some(expected.clone()),
), },
version: Range::full(), version: Range::full(),
}) })
} }

View file

@ -20,9 +20,9 @@ pub enum PubGrubPackage {
/// A Python version. /// A Python version.
Python(PubGrubPython), Python(PubGrubPython),
/// A Python package. /// A Python package.
Package( Package {
PackageName, name: PackageName,
Option<ExtraName>, extra: Option<ExtraName>,
/// The URL of the package, if it was specified in the requirement. /// The URL of the package, if it was specified in the requirement.
/// ///
/// There are a few challenges that come with URL-based packages, and how they map to /// There are a few challenges that come with URL-based packages, and how they map to
@ -59,8 +59,8 @@ pub enum PubGrubPackage {
/// we're going to have a dependency that's provided as a URL, we _need_ to visit the URL /// we're going to have a dependency that's provided as a URL, we _need_ to visit the URL
/// version before the registry version. So we could just error if we visit a URL variant /// version before the registry version. So we could just error if we visit a URL variant
/// _after_ a registry variant. /// _after_ a registry variant.
Option<VerbatimParsedUrl>, url: Option<VerbatimParsedUrl>,
), },
/// A proxy package to represent a dependency with an extra (e.g., `black[colorama]`). /// A proxy package to represent a dependency with an extra (e.g., `black[colorama]`).
/// ///
/// For a given package `black`, and an extra `colorama`, we create a virtual package /// For a given package `black`, and an extra `colorama`, we create a virtual package
@ -74,7 +74,11 @@ pub enum PubGrubPackage {
/// the exact same version of the base variant. Without the proxy package, then when provided /// the exact same version of the base variant. Without the proxy package, then when provided
/// requirements like `black==23.0.1` and `black[colorama]`, PubGrub may attempt to retrieve /// requirements like `black==23.0.1` and `black[colorama]`, PubGrub may attempt to retrieve
/// metadata for `black[colorama]` versions other than `23.0.1`. /// metadata for `black[colorama]` versions other than `23.0.1`.
Extra(PackageName, ExtraName, Option<VerbatimParsedUrl>), Extra {
name: PackageName,
extra: ExtraName,
url: Option<VerbatimParsedUrl>,
},
} }
impl PubGrubPackage { impl PubGrubPackage {
@ -82,9 +86,9 @@ impl PubGrubPackage {
pub(crate) fn from_package(name: PackageName, extra: Option<ExtraName>, urls: &Urls) -> Self { pub(crate) fn from_package(name: PackageName, extra: Option<ExtraName>, urls: &Urls) -> Self {
let url = urls.get(&name).cloned(); let url = urls.get(&name).cloned();
if let Some(extra) = extra { if let Some(extra) = extra {
Self::Extra(name, extra, url) Self::Extra { name, extra, url }
} else { } else {
Self::Package(name, extra, url) Self::Package { name, extra, url }
} }
} }
} }
@ -108,11 +112,17 @@ impl std::fmt::Display for PubGrubPackage {
} }
} }
Self::Python(_) => write!(f, "Python"), Self::Python(_) => write!(f, "Python"),
Self::Package(name, None, ..) => write!(f, "{name}"), Self::Package {
Self::Package(name, Some(extra), ..) => { name, extra: None, ..
} => write!(f, "{name}"),
Self::Package {
name,
extra: Some(extra),
..
} => {
write!(f, "{name}[{extra}]") write!(f, "{name}[{extra}]")
} }
Self::Extra(name, extra, ..) => write!(f, "{name}[{extra}]"), Self::Extra { name, extra, .. } => write!(f, "{name}[{extra}]"),
} }
} }
} }

View file

@ -28,7 +28,12 @@ impl PubGrubPriorities {
PubGrubPackage::Root(_) => {} PubGrubPackage::Root(_) => {}
PubGrubPackage::Python(_) => {} PubGrubPackage::Python(_) => {}
PubGrubPackage::Extra(name, _, None) | PubGrubPackage::Package(name, _, None) => { PubGrubPackage::Extra {
name, url: None, ..
}
| PubGrubPackage::Package {
name, url: None, ..
} => {
match self.0.entry(name.clone()) { match self.0.entry(name.clone()) {
std::collections::hash_map::Entry::Occupied(mut entry) => { std::collections::hash_map::Entry::Occupied(mut entry) => {
// Preserve the original index. // Preserve the original index.
@ -61,7 +66,12 @@ impl PubGrubPriorities {
} }
} }
} }
PubGrubPackage::Extra(name, _, Some(_)) | PubGrubPackage::Package(name, _, Some(_)) => { PubGrubPackage::Extra {
name, url: Some(_), ..
}
| PubGrubPackage::Package {
name, url: Some(_), ..
} => {
match self.0.entry(name.clone()) { match self.0.entry(name.clone()) {
std::collections::hash_map::Entry::Occupied(mut entry) => { std::collections::hash_map::Entry::Occupied(mut entry) => {
// Preserve the original index. // Preserve the original index.
@ -94,8 +104,8 @@ impl PubGrubPriorities {
match package { match package {
PubGrubPackage::Root(_) => Some(PubGrubPriority::Root), PubGrubPackage::Root(_) => Some(PubGrubPriority::Root),
PubGrubPackage::Python(_) => Some(PubGrubPriority::Root), PubGrubPackage::Python(_) => Some(PubGrubPriority::Root),
PubGrubPackage::Extra(name, _, _) => self.0.get(name).copied(), PubGrubPackage::Extra { name, .. } => self.0.get(name).copied(),
PubGrubPackage::Package(name, _, _) => self.0.get(name).copied(), PubGrubPackage::Package { name, .. } => self.0.get(name).copied(),
} }
} }
} }

View file

@ -155,14 +155,14 @@ impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason>
let terms_vec: Vec<_> = terms.iter().collect(); let terms_vec: Vec<_> = terms.iter().collect();
match terms_vec.as_slice() { match terms_vec.as_slice() {
[] | [(PubGrubPackage::Root(_), _)] => "the requirements are unsatisfiable".into(), [] | [(PubGrubPackage::Root(_), _)] => "the requirements are unsatisfiable".into(),
[(package @ PubGrubPackage::Package(..), Term::Positive(range))] => { [(package @ PubGrubPackage::Package { .. }, Term::Positive(range))] => {
let range = self.simplify_set(range, package); let range = self.simplify_set(range, package);
format!( format!(
"{} cannot be used", "{} cannot be used",
PackageRange::compatibility(package, &range) PackageRange::compatibility(package, &range)
) )
} }
[(package @ PubGrubPackage::Package(..), Term::Negative(range))] => { [(package @ PubGrubPackage::Package { .. }, Term::Negative(range))] => {
let range = self.simplify_set(range, package); let range = self.simplify_set(range, package);
format!( format!(
"{} must be used", "{} must be used",
@ -399,10 +399,10 @@ impl PubGrubReportFormatter<'_> {
) -> IndexSet<PubGrubHint> { ) -> IndexSet<PubGrubHint> {
/// Returns `true` if pre-releases were allowed for a package. /// Returns `true` if pre-releases were allowed for a package.
fn allowed_prerelease(package: &PubGrubPackage, selector: &CandidateSelector) -> bool { fn allowed_prerelease(package: &PubGrubPackage, selector: &CandidateSelector) -> bool {
let PubGrubPackage::Package(package, ..) = package else { let PubGrubPackage::Package { name, .. } = package else {
return false; return false;
}; };
selector.prerelease_strategy().allows(package) selector.prerelease_strategy().allows(name)
} }
let mut hints = IndexSet::default(); let mut hints = IndexSet::default();
@ -457,7 +457,7 @@ impl PubGrubReportFormatter<'_> {
let no_find_links = let no_find_links =
index_locations.flat_index().peekable().peek().is_none(); index_locations.flat_index().peekable().peek().is_none();
if let PubGrubPackage::Package(name, ..) = package { if let PubGrubPackage::Package { name, .. } = package {
// Add hints due to the package being entirely unavailable. // Add hints due to the package being entirely unavailable.
match unavailable_packages.get(name) { match unavailable_packages.get(name) {
Some(UnavailablePackage::NoIndex) => { Some(UnavailablePackage::NoIndex) => {

View file

@ -56,8 +56,12 @@ impl ResolutionGraph {
let mut diagnostics = Vec::new(); let mut diagnostics = Vec::new();
for (package, version) in selection { for (package, version) in selection {
match package { match package {
PubGrubPackage::Package(package_name, Some(extra), None) => { PubGrubPackage::Package {
let dist = PubGrubDistribution::from_registry(package_name, version); name,
extra: Some(extra),
url: None,
} => {
let dist = PubGrubDistribution::from_registry(name, version);
let response = distributions.get(&dist.version_id()).unwrap_or_else(|| { let response = distributions.get(&dist.version_id()).unwrap_or_else(|| {
panic!( panic!(
@ -75,15 +79,13 @@ impl ResolutionGraph {
if archive.metadata.provides_extras.contains(extra) { if archive.metadata.provides_extras.contains(extra) {
extras extras
.entry(package_name.clone()) .entry(name.clone())
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.push(extra.clone()); .push(extra.clone());
} else { } else {
let dist = pins let dist = pins
.get(package_name, version) .get(name, version)
.unwrap_or_else(|| { .unwrap_or_else(|| panic!("Every package should be pinned: {name:?}"))
panic!("Every package should be pinned: {package_name:?}")
})
.clone(); .clone();
diagnostics.push(Diagnostic::MissingExtra { diagnostics.push(Diagnostic::MissingExtra {
@ -92,16 +94,19 @@ impl ResolutionGraph {
}); });
} }
} }
PubGrubPackage::Package(package_name, Some(extra), Some(url)) => { PubGrubPackage::Package {
if let Some(editable) = editables.get(package_name) { name,
extra: Some(extra),
url: Some(url),
} => {
if let Some(editable) = editables.get(name) {
if editable.metadata.provides_extras.contains(extra) { if editable.metadata.provides_extras.contains(extra) {
extras extras
.entry(package_name.clone()) .entry(name.clone())
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.push(extra.clone()); .push(extra.clone());
} else { } else {
let dist = let dist = Dist::from_editable(name.clone(), editable.built.clone())?;
Dist::from_editable(package_name.clone(), editable.built.clone())?;
diagnostics.push(Diagnostic::MissingExtra { diagnostics.push(Diagnostic::MissingExtra {
dist: dist.into(), dist: dist.into(),
@ -109,7 +114,7 @@ impl ResolutionGraph {
}); });
} }
} else { } else {
let dist = PubGrubDistribution::from_url(package_name, url); let dist = PubGrubDistribution::from_url(name, url);
let response = distributions.get(&dist.version_id()).unwrap_or_else(|| { let response = distributions.get(&dist.version_id()).unwrap_or_else(|| {
panic!( panic!(
@ -127,12 +132,11 @@ impl ResolutionGraph {
if archive.metadata.provides_extras.contains(extra) { if archive.metadata.provides_extras.contains(extra) {
extras extras
.entry(package_name.clone()) .entry(name.clone())
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.push(extra.clone()); .push(extra.clone());
} else { } else {
let dist = let dist = Dist::from_url(name.clone(), url_to_precise(url.clone()))?;
Dist::from_url(package_name.clone(), url_to_precise(url.clone()))?;
diagnostics.push(Diagnostic::MissingExtra { diagnostics.push(Diagnostic::MissingExtra {
dist: dist.into(), dist: dist.into(),
@ -154,21 +158,25 @@ impl ResolutionGraph {
for (package, version) in selection { for (package, version) in selection {
match package { match package {
PubGrubPackage::Package(package_name, None, None) => { PubGrubPackage::Package {
name,
extra: None,
url: None,
} => {
// Create the distribution. // Create the distribution.
let dist = pins let dist = pins
.get(package_name, version) .get(name, version)
.expect("Every package should be pinned") .expect("Every package should be pinned")
.clone(); .clone();
// Extract the hashes, preserving those that were already present in the // Extract the hashes, preserving those that were already present in the
// lockfile if necessary. // lockfile if necessary.
let hashes = if let Some(digests) = preferences let hashes = if let Some(digests) = preferences
.match_hashes(package_name, version) .match_hashes(name, version)
.filter(|digests| !digests.is_empty()) .filter(|digests| !digests.is_empty())
{ {
digests.to_vec() digests.to_vec()
} else if let Some(versions_response) = packages.get(package_name) { } else if let Some(versions_response) = packages.get(name) {
if let VersionsResponse::Found(ref version_maps) = *versions_response { if let VersionsResponse::Found(ref version_maps) = *versions_response {
version_maps version_maps
.iter() .iter()
@ -187,7 +195,7 @@ impl ResolutionGraph {
// Extract the metadata. // Extract the metadata.
let metadata = { let metadata = {
let dist = PubGrubDistribution::from_registry(package_name, version); let dist = PubGrubDistribution::from_registry(name, version);
let response = distributions.get(&dist.version_id()).unwrap_or_else(|| { let response = distributions.get(&dist.version_id()).unwrap_or_else(|| {
panic!( panic!(
@ -207,7 +215,7 @@ impl ResolutionGraph {
}; };
// Extract the extras. // Extract the extras.
let extras = extras.get(package_name).cloned().unwrap_or_default(); let extras = extras.get(name).cloned().unwrap_or_default();
// Add the distribution to the graph. // Add the distribution to the graph.
let index = petgraph.add_node(AnnotatedDist { let index = petgraph.add_node(AnnotatedDist {
@ -216,13 +224,16 @@ impl ResolutionGraph {
hashes, hashes,
metadata, metadata,
}); });
inverse.insert(package_name, index); inverse.insert(name, index);
} }
PubGrubPackage::Package(package_name, None, Some(url)) => { PubGrubPackage::Package {
name,
extra: None,
url: Some(url),
} => {
// Create the distribution. // Create the distribution.
if let Some(editable) = editables.get(package_name) { if let Some(editable) = editables.get(name) {
let dist = let dist = Dist::from_editable(name.clone(), editable.built.clone())?;
Dist::from_editable(package_name.clone(), editable.built.clone())?;
// Add the distribution to the graph. // Add the distribution to the graph.
let index = petgraph.add_node(AnnotatedDist { let index = petgraph.add_node(AnnotatedDist {
@ -231,15 +242,14 @@ impl ResolutionGraph {
hashes: vec![], hashes: vec![],
metadata: editable.metadata.clone(), metadata: editable.metadata.clone(),
}); });
inverse.insert(package_name, index); inverse.insert(name, index);
} else { } else {
let dist = let dist = Dist::from_url(name.clone(), url_to_precise(url.clone()))?;
Dist::from_url(package_name.clone(), url_to_precise(url.clone()))?;
// Extract the hashes, preserving those that were already present in the // Extract the hashes, preserving those that were already present in the
// lockfile if necessary. // lockfile if necessary.
let hashes = if let Some(digests) = preferences let hashes = if let Some(digests) = preferences
.match_hashes(package_name, version) .match_hashes(name, version)
.filter(|digests| !digests.is_empty()) .filter(|digests| !digests.is_empty())
{ {
digests.to_vec() digests.to_vec()
@ -259,7 +269,7 @@ impl ResolutionGraph {
// Extract the metadata. // Extract the metadata.
let metadata = { let metadata = {
let dist = PubGrubDistribution::from_url(package_name, url); let dist = PubGrubDistribution::from_url(name, url);
let response = let response =
distributions.get(&dist.version_id()).unwrap_or_else(|| { distributions.get(&dist.version_id()).unwrap_or_else(|| {
@ -280,7 +290,7 @@ impl ResolutionGraph {
}; };
// Extract the extras. // Extract the extras.
let extras = extras.get(package_name).cloned().unwrap_or_default(); let extras = extras.get(name).cloned().unwrap_or_default();
// Add the distribution to the graph. // Add the distribution to the graph.
let index = petgraph.add_node(AnnotatedDist { let index = petgraph.add_node(AnnotatedDist {
@ -289,7 +299,7 @@ impl ResolutionGraph {
hashes, hashes,
metadata, metadata,
}); });
inverse.insert(package_name, index); inverse.insert(name, index);
}; };
} }
_ => {} _ => {}
@ -314,22 +324,28 @@ impl ResolutionGraph {
continue; continue;
} }
let PubGrubPackage::Package(self_package, _, _) = self_package else { let PubGrubPackage::Package {
name: self_name, ..
} = self_package
else {
continue; continue;
}; };
let PubGrubPackage::Package(dependency_package, _, _) = dependency_package let PubGrubPackage::Package {
name: dependency_name,
..
} = dependency_package
else { else {
continue; continue;
}; };
// For extras, we include a dependency between the extra and the base package. // For extras, we include a dependency between the extra and the base package.
if self_package == dependency_package { if self_name == dependency_name {
continue; continue;
} }
if self_version.contains(version) { if self_version.contains(version) {
let self_index = &inverse[self_package]; let self_index = &inverse[self_name];
let dependency_index = &inverse[dependency_package]; let dependency_index = &inverse[dependency_name];
petgraph.update_edge( petgraph.update_edge(
*self_index, *self_index,
*dependency_index, *dependency_index,

View file

@ -53,7 +53,12 @@ impl BatchPrefetcher {
index: &InMemoryIndex, index: &InMemoryIndex,
selector: &CandidateSelector, selector: &CandidateSelector,
) -> anyhow::Result<(), ResolveError> { ) -> anyhow::Result<(), ResolveError> {
let PubGrubPackage::Package(package_name, None, None) = &next else { let PubGrubPackage::Package {
name,
extra: None,
url: None,
} = &next
else {
return Ok(()); return Ok(());
}; };
@ -66,7 +71,7 @@ impl BatchPrefetcher {
// This is immediate, we already fetched the version map. // This is immediate, we already fetched the version map.
let versions_response = index let versions_response = index
.packages() .packages()
.wait_blocking(package_name) .wait_blocking(name)
.ok_or(ResolveError::Unregistered)?; .ok_or(ResolveError::Unregistered)?;
let VersionsResponse::Found(ref version_map) = *versions_response else { let VersionsResponse::Found(ref version_map) = *versions_response else {
@ -85,7 +90,7 @@ impl BatchPrefetcher {
previous, previous,
} => { } => {
if let Some(candidate) = if let Some(candidate) =
selector.select_no_preference(package_name, &compatible, version_map) selector.select_no_preference(name, &compatible, version_map)
{ {
let compatible = compatible.intersection( let compatible = compatible.intersection(
&Range::singleton(candidate.version().clone()).complement(), &Range::singleton(candidate.version().clone()).complement(),
@ -103,13 +108,13 @@ impl BatchPrefetcher {
} }
} }
BatchPrefetchStrategy::InOrder { previous } => { BatchPrefetchStrategy::InOrder { previous } => {
let range = if selector.use_highest_version(package_name) { let range = if selector.use_highest_version(name) {
Range::strictly_lower_than(previous) Range::strictly_lower_than(previous)
} else { } else {
Range::strictly_higher_than(previous) Range::strictly_higher_than(previous)
}; };
if let Some(candidate) = if let Some(candidate) =
selector.select_no_preference(package_name, &range, version_map) selector.select_no_preference(name, &range, version_map)
{ {
phase = BatchPrefetchStrategy::InOrder { phase = BatchPrefetchStrategy::InOrder {
previous: candidate.version().clone(), previous: candidate.version().clone(),
@ -148,7 +153,7 @@ impl BatchPrefetcher {
} }
} }
debug!("Prefetching {prefetch_count} {package_name} versions"); debug!("Prefetching {prefetch_count} {name} versions");
self.last_prefetch.insert(next.clone(), num_tried); self.last_prefetch.insert(next.clone(), num_tried);
Ok(()) Ok(())
@ -157,7 +162,7 @@ impl BatchPrefetcher {
/// Each time we tried a version for a package, we register that here. /// Each time we tried a version for a package, we register that here.
pub(crate) fn version_tried(&mut self, package: PubGrubPackage) { pub(crate) fn version_tried(&mut self, package: PubGrubPackage) {
// Only track base packages, no virtual packages from extras. // Only track base packages, no virtual packages from extras.
if matches!(package, PubGrubPackage::Package(_, Some(_), _)) { if matches!(package, PubGrubPackage::Package { extra: Some(_), .. }) {
return; return;
} }
*self.tried_versions.entry(package).or_default() += 1; *self.tried_versions.entry(package).or_default() += 1;

View file

@ -480,8 +480,8 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
.expect("a package was chosen but we don't have a term."); .expect("a package was chosen but we don't have a term.");
// Check if the decision was due to the package being unavailable // Check if the decision was due to the package being unavailable
if let PubGrubPackage::Package(ref package_name, _, _) = state.next { if let PubGrubPackage::Package { ref name, .. } = state.next {
if let Some(entry) = self.unavailable_packages.get(package_name) { if let Some(entry) = self.unavailable_packages.get(name) {
state state
.pubgrub .pubgrub
.add_incompatibility(Incompatibility::custom_term( .add_incompatibility(Incompatibility::custom_term(
@ -636,8 +636,10 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
match package { match package {
PubGrubPackage::Root(_) => {} PubGrubPackage::Root(_) => {}
PubGrubPackage::Python(_) => {} PubGrubPackage::Python(_) => {}
PubGrubPackage::Extra(_, _, _) => {} PubGrubPackage::Extra { .. } => {}
PubGrubPackage::Package(name, _extra, None) => { PubGrubPackage::Package {
name, url: None, ..
} => {
// Verify that the package is allowed under the hash-checking policy. // Verify that the package is allowed under the hash-checking policy.
if !self.hasher.allows_package(name) { if !self.hasher.allows_package(name) {
return Err(ResolveError::UnhashedPackage(name.clone())); return Err(ResolveError::UnhashedPackage(name.clone()));
@ -648,7 +650,11 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
request_sink.blocking_send(Request::Package(name.clone()))?; request_sink.blocking_send(Request::Package(name.clone()))?;
} }
} }
PubGrubPackage::Package(name, _extra, Some(url)) => { PubGrubPackage::Package {
name,
url: Some(url),
..
} => {
// Verify that the package is allowed under the hash-checking policy. // Verify that the package is allowed under the hash-checking policy.
if !self.hasher.allows_url(&url.verbatim) { if !self.hasher.allows_url(&url.verbatim) {
return Err(ResolveError::UnhashedPackage(name.clone())); return Err(ResolveError::UnhashedPackage(name.clone()));
@ -678,10 +684,15 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
// Iterate over the potential packages, and fetch file metadata for any of them. These // Iterate over the potential packages, and fetch file metadata for any of them. These
// represent our current best guesses for the versions that we _might_ select. // represent our current best guesses for the versions that we _might_ select.
for (package, range) in packages { for (package, range) in packages {
let PubGrubPackage::Package(package_name, None, None) = package else { let PubGrubPackage::Package {
name,
extra: None,
url: None,
} = package
else {
continue; continue;
}; };
request_sink.blocking_send(Request::Prefetch(package_name.clone(), range.clone()))?; request_sink.blocking_send(Request::Prefetch(name.clone(), range.clone()))?;
} }
Ok(()) Ok(())
} }
@ -720,15 +731,23 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
} }
} }
PubGrubPackage::Extra(package_name, _, Some(url)) PubGrubPackage::Extra {
| PubGrubPackage::Package(package_name, _, Some(url)) => { name,
url: Some(url),
..
}
| PubGrubPackage::Package {
name,
url: Some(url),
..
} => {
debug!( debug!(
"Searching for a compatible version of {package} @ {} ({range})", "Searching for a compatible version of {package} @ {} ({range})",
url.verbatim url.verbatim
); );
// If the dist is an editable, return the version from the editable metadata. // If the dist is an editable, return the version from the editable metadata.
if let Some(editable) = self.editables.get(package_name) { if let Some(editable) = self.editables.get(name) {
let version = &editable.metadata.version; let version = &editable.metadata.version;
// The version is incompatible with the requirement. // The version is incompatible with the requirement.
@ -752,7 +771,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
return Ok(Some(ResolverVersion::Available(version.clone()))); return Ok(Some(ResolverVersion::Available(version.clone())));
} }
let dist = PubGrubDistribution::from_url(package_name, url); let dist = PubGrubDistribution::from_url(name, url);
let response = self let response = self
.index .index
.distributions() .distributions()
@ -764,26 +783,26 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
MetadataResponse::Found(archive) => &archive.metadata, MetadataResponse::Found(archive) => &archive.metadata,
MetadataResponse::Offline => { MetadataResponse::Offline => {
self.unavailable_packages self.unavailable_packages
.insert(package_name.clone(), UnavailablePackage::Offline); .insert(name.clone(), UnavailablePackage::Offline);
return Ok(None); return Ok(None);
} }
MetadataResponse::InvalidMetadata(err) => { MetadataResponse::InvalidMetadata(err) => {
self.unavailable_packages.insert( self.unavailable_packages.insert(
package_name.clone(), name.clone(),
UnavailablePackage::InvalidMetadata(err.to_string()), UnavailablePackage::InvalidMetadata(err.to_string()),
); );
return Ok(None); return Ok(None);
} }
MetadataResponse::InconsistentMetadata(err) => { MetadataResponse::InconsistentMetadata(err) => {
self.unavailable_packages.insert( self.unavailable_packages.insert(
package_name.clone(), name.clone(),
UnavailablePackage::InvalidMetadata(err.to_string()), UnavailablePackage::InvalidMetadata(err.to_string()),
); );
return Ok(None); return Ok(None);
} }
MetadataResponse::InvalidStructure(err) => { MetadataResponse::InvalidStructure(err) => {
self.unavailable_packages.insert( self.unavailable_packages.insert(
package_name.clone(), name.clone(),
UnavailablePackage::InvalidStructure(err.to_string()), UnavailablePackage::InvalidStructure(err.to_string()),
); );
return Ok(None); return Ok(None);
@ -813,31 +832,35 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
Ok(Some(ResolverVersion::Available(version.clone()))) Ok(Some(ResolverVersion::Available(version.clone())))
} }
PubGrubPackage::Extra(package_name, _, None) PubGrubPackage::Extra {
| PubGrubPackage::Package(package_name, _, None) => { name, url: None, ..
}
| PubGrubPackage::Package {
name, url: None, ..
} => {
// Wait for the metadata to be available. // Wait for the metadata to be available.
let versions_response = self let versions_response = self
.index .index
.packages() .packages()
.wait_blocking(package_name) .wait_blocking(name)
.ok_or(ResolveError::Unregistered)?; .ok_or(ResolveError::Unregistered)?;
visited.insert(package_name.clone()); visited.insert(name.clone());
let version_maps = match *versions_response { let version_maps = match *versions_response {
VersionsResponse::Found(ref version_maps) => version_maps.as_slice(), VersionsResponse::Found(ref version_maps) => version_maps.as_slice(),
VersionsResponse::NoIndex => { VersionsResponse::NoIndex => {
self.unavailable_packages self.unavailable_packages
.insert(package_name.clone(), UnavailablePackage::NoIndex); .insert(name.clone(), UnavailablePackage::NoIndex);
&[] &[]
} }
VersionsResponse::Offline => { VersionsResponse::Offline => {
self.unavailable_packages self.unavailable_packages
.insert(package_name.clone(), UnavailablePackage::Offline); .insert(name.clone(), UnavailablePackage::Offline);
&[] &[]
} }
VersionsResponse::NotFound => { VersionsResponse::NotFound => {
self.unavailable_packages self.unavailable_packages
.insert(package_name.clone(), UnavailablePackage::NotFound); .insert(name.clone(), UnavailablePackage::NotFound);
&[] &[]
} }
}; };
@ -846,7 +869,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
// Find a version. // Find a version.
let Some(candidate) = self.selector.select( let Some(candidate) = self.selector.select(
package_name, name,
range, range,
version_maps, version_maps,
&self.preferences, &self.preferences,
@ -892,7 +915,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
let version = candidate.version().clone(); let version = candidate.version().clone();
// Emit a request to fetch the metadata for this version. // Emit a request to fetch the metadata for this version.
if matches!(package, PubGrubPackage::Package(_, _, _)) { if matches!(package, PubGrubPackage::Package { .. }) {
if self.index.distributions().register(candidate.version_id()) { if self.index.distributions().register(candidate.version_id()) {
let request = Request::from(dist.for_resolution()); let request = Request::from(dist.for_resolution());
request_sink.blocking_send(request)?; request_sink.blocking_send(request)?;
@ -997,16 +1020,16 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
PubGrubPackage::Python(_) => Ok(Dependencies::Available(Vec::default())), PubGrubPackage::Python(_) => Ok(Dependencies::Available(Vec::default())),
PubGrubPackage::Package(package_name, extra, url) => { PubGrubPackage::Package { name, extra, url } => {
// If we're excluding transitive dependencies, short-circuit. // If we're excluding transitive dependencies, short-circuit.
if self.dependency_mode.is_direct() { if self.dependency_mode.is_direct() {
// If an extra is provided, wait for the metadata to be available, since it's // If an extra is provided, wait for the metadata to be available, since it's
// still required for generating the lock file. // still required for generating the lock file.
if !self.editables.contains(package_name) { if !self.editables.contains(name) {
// Determine the distribution to lookup. // Determine the distribution to lookup.
let dist = match url { let dist = match url {
Some(url) => PubGrubDistribution::from_url(package_name, url), Some(url) => PubGrubDistribution::from_url(name, url),
None => PubGrubDistribution::from_registry(package_name, version), None => PubGrubDistribution::from_registry(name, version),
}; };
let version_id = dist.version_id(); let version_id = dist.version_id();
@ -1021,7 +1044,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
} }
// Determine if the distribution is editable. // Determine if the distribution is editable.
if let Some(editable) = self.editables.get(package_name) { if let Some(editable) = self.editables.get(name) {
let requirements: Vec<_> = editable let requirements: Vec<_> = editable
.metadata .metadata
.requires_dist .requires_dist
@ -1033,7 +1056,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
&requirements, &requirements,
&self.constraints, &self.constraints,
&self.overrides, &self.overrides,
Some(package_name), Some(name),
extra.as_ref(), extra.as_ref(),
&self.urls, &self.urls,
&self.locals, &self.locals,
@ -1055,24 +1078,21 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
// Determine the distribution to lookup. // Determine the distribution to lookup.
let dist = match url { let dist = match url {
Some(url) => PubGrubDistribution::from_url(package_name, url), Some(url) => PubGrubDistribution::from_url(name, url),
None => PubGrubDistribution::from_registry(package_name, version), None => PubGrubDistribution::from_registry(name, version),
}; };
let version_id = dist.version_id(); let version_id = dist.version_id();
// If the package does not exist in the registry or locally, we cannot fetch its dependencies // If the package does not exist in the registry or locally, we cannot fetch its dependencies
if self.unavailable_packages.get(package_name).is_some() if self.unavailable_packages.get(name).is_some()
&& self && self.installed_packages.get_packages(name).is_empty()
.installed_packages
.get_packages(package_name)
.is_empty()
{ {
debug_assert!( debug_assert!(
false, false,
"Dependencies were requested for a package that is not available" "Dependencies were requested for a package that is not available"
); );
return Err(ResolveError::Failure(format!( return Err(ResolveError::Failure(format!(
"The package is unavailable: {package_name}" "The package is unavailable: {name}"
))); )));
} }
@ -1087,15 +1107,15 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
MetadataResponse::Found(archive) => &archive.metadata, MetadataResponse::Found(archive) => &archive.metadata,
MetadataResponse::Offline => { MetadataResponse::Offline => {
self.incomplete_packages self.incomplete_packages
.entry(package_name.clone()) .entry(name.clone())
.or_default() .or_default()
.insert(version.clone(), IncompletePackage::Offline); .insert(version.clone(), IncompletePackage::Offline);
return Ok(Dependencies::Unavailable(UnavailableVersion::Offline)); return Ok(Dependencies::Unavailable(UnavailableVersion::Offline));
} }
MetadataResponse::InvalidMetadata(err) => { MetadataResponse::InvalidMetadata(err) => {
warn!("Unable to extract metadata for {package_name}: {err}"); warn!("Unable to extract metadata for {name}: {err}");
self.incomplete_packages self.incomplete_packages
.entry(package_name.clone()) .entry(name.clone())
.or_default() .or_default()
.insert( .insert(
version.clone(), version.clone(),
@ -1106,9 +1126,9 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
)); ));
} }
MetadataResponse::InconsistentMetadata(err) => { MetadataResponse::InconsistentMetadata(err) => {
warn!("Unable to extract metadata for {package_name}: {err}"); warn!("Unable to extract metadata for {name}: {err}");
self.incomplete_packages self.incomplete_packages
.entry(package_name.clone()) .entry(name.clone())
.or_default() .or_default()
.insert( .insert(
version.clone(), version.clone(),
@ -1119,9 +1139,9 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
)); ));
} }
MetadataResponse::InvalidStructure(err) => { MetadataResponse::InvalidStructure(err) => {
warn!("Unable to extract metadata for {package_name}: {err}"); warn!("Unable to extract metadata for {name}: {err}");
self.incomplete_packages self.incomplete_packages
.entry(package_name.clone()) .entry(name.clone())
.or_default() .or_default()
.insert( .insert(
version.clone(), version.clone(),
@ -1143,7 +1163,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
&requirements, &requirements,
&self.constraints, &self.constraints,
&self.overrides, &self.overrides,
Some(package_name), Some(name),
extra.as_ref(), extra.as_ref(),
&self.urls, &self.urls,
&self.locals, &self.locals,
@ -1164,13 +1184,21 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
} }
// Add a dependency on both the extra and base package. // Add a dependency on both the extra and base package.
PubGrubPackage::Extra(package_name, extra, url) => Ok(Dependencies::Available(vec![ PubGrubPackage::Extra { name, extra, url } => Ok(Dependencies::Available(vec![
( (
PubGrubPackage::Package(package_name.clone(), None, url.clone()), PubGrubPackage::Package {
name: name.clone(),
extra: None,
url: url.clone(),
},
Range::singleton(version.clone()), Range::singleton(version.clone()),
), ),
( (
PubGrubPackage::Package(package_name.clone(), Some(extra.clone()), url.clone()), PubGrubPackage::Package {
name: name.clone(),
extra: Some(extra.clone()),
url: url.clone(),
},
Range::singleton(version.clone()), Range::singleton(version.clone()),
), ),
])), ])),
@ -1398,12 +1426,18 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
match package { match package {
PubGrubPackage::Root(_) => {} PubGrubPackage::Root(_) => {}
PubGrubPackage::Python(_) => {} PubGrubPackage::Python(_) => {}
PubGrubPackage::Extra(_, _, _) => {} PubGrubPackage::Extra { .. } => {}
PubGrubPackage::Package(package_name, _extra, Some(url)) => { PubGrubPackage::Package {
reporter.on_progress(package_name, &VersionOrUrlRef::Url(&url.verbatim)); name,
url: Some(url),
..
} => {
reporter.on_progress(name, &VersionOrUrlRef::Url(&url.verbatim));
} }
PubGrubPackage::Package(package_name, _extra, None) => { PubGrubPackage::Package {
reporter.on_progress(package_name, &VersionOrUrlRef::Version(version)); name, url: None, ..
} => {
reporter.on_progress(name, &VersionOrUrlRef::Version(version));
} }
} }
} }