diff --git a/.config/nextest.toml b/.config/nextest.toml index e3a8de680..01d7536c4 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -1,13 +1,3 @@ [profile.default] # Mark tests that take longer than 10s as slow slow-timeout = "10s" - -[[profile.default.overrides]] -# Some of these tests have a non-determinstic snapshot -filter = 'binary(pip_install_scenarios)' -retries = 2 - -[[profile.no-retry.overrides]] -# An optional profile to avoid retries -filter = 'binary(pip_install_scenarios)' -retries = 0 diff --git a/crates/puffin-resolver/src/error.rs b/crates/puffin-resolver/src/error.rs index c84a3181d..32b17c6fa 100644 --- a/crates/puffin-resolver/src/error.rs +++ b/crates/puffin-resolver/src/error.rs @@ -2,6 +2,7 @@ use std::collections::BTreeSet; use std::convert::Infallible; use std::fmt::Formatter; +use dashmap::DashSet; use indexmap::IndexMap; use pubgrub::range::Range; use pubgrub::report::{DefaultStringReporter, DerivationTree, Reporter}; @@ -167,6 +168,7 @@ impl NoSolutionError { pub(crate) fn with_available_versions( mut self, python_requirement: &PythonRequirement, + visited: &DashSet, package_versions: &OnceMap, ) -> Self { let mut available_versions = IndexMap::default(); @@ -186,15 +188,21 @@ impl NoSolutionError { ); } PubGrubPackage::Package(name, ..) => { - if let Some(entry) = package_versions.get(name) { - let version_map = entry.value(); - available_versions.insert( - package.clone(), - version_map - .iter() - .map(|(version, _)| version.clone()) - .collect(), - ); + // Avoid including available versions for packages that exist in the derivation + // tree, but were never visited during resolution. We _may_ have metadata for + // these packages, but it's non-deterministic, and omitting them ensures that + // we represent the state of the resolver at the time of failure. + if visited.contains(name) { + if let Some(entry) = package_versions.get(name) { + let version_map = entry.value(); + available_versions.insert( + package.clone(), + version_map + .iter() + .map(|(version, _)| version.clone()) + .collect(), + ); + } } } } diff --git a/crates/puffin-resolver/src/resolver/mod.rs b/crates/puffin-resolver/src/resolver/mod.rs index 435c4a796..998600398 100644 --- a/crates/puffin-resolver/src/resolver/mod.rs +++ b/crates/puffin-resolver/src/resolver/mod.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use anyhow::Result; -use dashmap::DashMap; +use dashmap::{DashMap, DashSet}; use futures::channel::mpsc::UnboundedReceiver; use futures::{pin_mut, FutureExt, StreamExt}; use itertools::Itertools; @@ -68,6 +68,8 @@ pub struct Resolver<'a, Provider: ResolverProvider> { index: &'a InMemoryIndex, /// A map from [`PackageId`] to the `Requires-Python` version specifiers for that package. incompatibilities: DashMap, + /// The set of all registry-based packages visited during resolution. + visited: DashSet, editables: FxHashMap, reporter: Option>, provider: Provider, @@ -167,6 +169,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> { Self { index, incompatibilities: DashMap::default(), + visited: DashSet::default(), selector, allowed_urls, project: manifest.project, @@ -223,7 +226,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> { if let ResolveError::NoSolution(err) = err { ResolveError::NoSolution( err - .with_available_versions(&self.python_requirement, &self.index.packages) + .with_available_versions(&self.python_requirement, &self.visited, &self.index.packages) .with_selector(self.selector.clone()) .with_python_requirement(&self.python_requirement) ) @@ -504,6 +507,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> { // Wait for the metadata to be available. let entry = self.index.packages.wait(package_name).await?; let version_map = entry.value(); + self.visited.insert(package_name.clone()); if let Some(extra) = extra { debug!( diff --git a/crates/puffin/tests/pip_install_scenarios.rs b/crates/puffin/tests/pip_install_scenarios.rs index 563aa5c72..f62c65e51 100644 --- a/crates/puffin/tests/pip_install_scenarios.rs +++ b/crates/puffin/tests/pip_install_scenarios.rs @@ -566,8 +566,8 @@ fn dependency_excludes_range_of_compatible_versions() -> Result<()> { Because only albatross<=3.0.0 is available and albatross==3.0.0 depends on bluebird==3.0.0, we can conclude that albatross>=3.0.0 depends on bluebird==3.0.0. And because we know from (2) that all versions of crow, bluebird!=1.0.0, albatross<3.0.0 are incompatible, we can conclude that all versions of crow depend on one of: - bluebird<=1.0.0 - bluebird>=3.0.0 + bluebird==1.0.0 + bluebird==3.0.0 And because you require crow and you require bluebird>=2.0.0,<3.0.0, we can conclude that the requirements are unsatisfiable. "###); @@ -697,8 +697,8 @@ fn dependency_excludes_non_contiguous_range_of_compatible_versions() -> Result<( Because only albatross<=3.0.0 is available and albatross==3.0.0 depends on bluebird==3.0.0, we can conclude that albatross>=3.0.0 depends on bluebird==3.0.0. And because we know from (2) that all versions of crow, bluebird!=1.0.0, albatross<3.0.0 are incompatible, we can conclude that all versions of crow depend on one of: - bluebird<=1.0.0 - bluebird>=3.0.0 + bluebird==1.0.0 + bluebird==3.0.0 And because you require crow and you require bluebird>=2.0.0,<3.0.0, we can conclude that the requirements are unsatisfiable. "###);