Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions crates/uv-resolver/src/candidate_selector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::preferences::{Entry, PreferenceSource, Preferences};
use crate::prerelease::{AllowPrerelease, PrereleaseStrategy};
use crate::resolution_mode::ResolutionStrategy;
use crate::version_map::{VersionMap, VersionMapDistHandle};
use crate::{Exclusions, Manifest, Options, ResolverEnvironment};
use crate::{ExcludeNewerTimestamp, Exclusions, Manifest, Options, ResolverEnvironment};

#[derive(Debug, Clone)]
#[allow(clippy::struct_field_names)]
Expand Down Expand Up @@ -86,6 +86,7 @@ impl CandidateSelector {
index: Option<&'a IndexUrl>,
env: &ResolverEnvironment,
tags: Option<&'a Tags>,
exclude_newer: Option<ExcludeNewerTimestamp>,
) -> Option<Candidate<'a>> {
let reinstall = exclusions.reinstall(package_name);
let upgrade = exclusions.upgrade(package_name);
Expand Down Expand Up @@ -134,7 +135,7 @@ impl CandidateSelector {
}

// Otherwise, find the best candidate from the version maps.
let compatible = self.select_no_preference(package_name, range, version_maps, env);
let compatible = self.select_no_preference(package_name, range, version_maps, env, exclude_newer);

// Cross-reference against the already-installed distribution.
//
Expand Down Expand Up @@ -418,6 +419,7 @@ impl CandidateSelector {
range: &Range<Version>,
version_maps: &'a [VersionMap],
env: &ResolverEnvironment,
exclude_newer: Option<ExcludeNewerTimestamp>,
) -> Option<Candidate<'a>> {
trace!(
"Selecting candidate for {package_name} with range {range} with {} remote versions",
Expand Down Expand Up @@ -457,6 +459,7 @@ impl CandidateSelector {
package_name,
range,
allow_prerelease,
exclude_newer,
)
} else {
Self::select_candidate(
Expand All @@ -479,6 +482,7 @@ impl CandidateSelector {
package_name,
range,
allow_prerelease,
exclude_newer,
)
}
} else {
Expand All @@ -489,6 +493,7 @@ impl CandidateSelector {
package_name,
range,
allow_prerelease,
exclude_newer,
)
})
} else {
Expand All @@ -498,6 +503,7 @@ impl CandidateSelector {
package_name,
range,
allow_prerelease,
exclude_newer,
)
})
}
Expand Down Expand Up @@ -526,11 +532,13 @@ impl CandidateSelector {
/// The returned [`Candidate`] _may not_ be compatible with the current platform; in such
/// cases, the resolver is responsible for tracking the incompatibility and re-running the
/// selection process with additional constraints.
#[allow(unused_variables)] // exclude_newer is unused here, logging handled at higher level
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why we need to pass it through here if it's not used here?

fn select_candidate<'a>(
versions: impl Iterator<Item = (&'a Version, VersionMapDistHandle<'a>)>,
package_name: &'a PackageName,
range: &Range<Version>,
allow_prerelease: bool,
exclude_newer: Option<ExcludeNewerTimestamp>,
) -> Option<Candidate<'a>> {
let mut steps = 0usize;
let mut incompatible: Option<Candidate> = None;
Expand Down
2 changes: 1 addition & 1 deletion crates/uv-resolver/src/pubgrub/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ impl PubGrubReportFormatter<'_> {
return None;
};

let candidate = selector.select_no_preference(name, set, version_maps, env)?;
let candidate = selector.select_no_preference(name, set, version_maps, env, None)?;

let prioritized = candidate.prioritized()?;

Expand Down
4 changes: 2 additions & 2 deletions crates/uv-resolver/src/resolver/batch_prefetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ impl BatchPrefetcherRunner {
previous,
} => {
if let Some(candidate) =
selector.select_no_preference(name, &compatible, version_map, env)
selector.select_no_preference(name, &compatible, version_map, env, None)
{
let compatible = compatible.intersection(
&Range::singleton(candidate.version().clone()).complement(),
Expand Down Expand Up @@ -266,7 +266,7 @@ impl BatchPrefetcherRunner {
};
}
if let Some(candidate) =
selector.select_no_preference(name, &range, version_map, env)
selector.select_no_preference(name, &range, version_map, env, None)
{
phase = BatchPrefetchStrategy::InOrder {
previous: candidate.version().clone(),
Expand Down
21 changes: 20 additions & 1 deletion crates/uv-resolver/src/resolver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::sync::Arc;
use std::time::Instant;
use std::{iter, slice, thread};

use dashmap::DashMap;
use dashmap::{DashMap, DashSet};
use either::Either;
use futures::{FutureExt, StreamExt};
use itertools::Itertools;
Expand Down Expand Up @@ -135,6 +135,8 @@ struct ResolverState<InstalledPackages: InstalledPackagesProvider> {
unavailable_packages: DashMap<PackageName, UnavailablePackage>,
/// Incompatibilities for packages that are unavailable at specific versions.
incomplete_packages: DashMap<PackageName, DashMap<Version, MetadataUnavailable>>,
/// Packages for which we've already logged `exclude_newer` messages.
logged_exclude_newer: DashSet<PackageName>,
/// The options that were used to configure this resolver.
options: Options,
/// The reporter to use for this resolver.
Expand Down Expand Up @@ -254,6 +256,7 @@ impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
installed_packages,
unavailable_packages: DashMap::default(),
incomplete_packages: DashMap::default(),
logged_exclude_newer: DashSet::default(),
options,
reporter: None,
};
Expand Down Expand Up @@ -1262,6 +1265,9 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag

debug!("Searching for a compatible version of {package} ({range})");

// Get the exclude_newer timestamp for this package
let exclude_newer = self.options.exclude_newer.exclude_newer_package(name);

// Find a version.
let Some(candidate) = self.selector.select(
name,
Expand All @@ -1273,8 +1279,17 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
index,
env,
self.tags.as_ref(),
exclude_newer,
) else {
// Short circuit: we couldn't find _any_ versions for a package.
// Log exclude_newer message once per package across all forks.
if let Some(exclude_newer_ts) = exclude_newer {
if self.logged_exclude_newer.insert(name.clone()) {
debug!(
"Excluding candidates for {name} published after {exclude_newer_ts}"
);
Comment on lines +1288 to +1290
Copy link
Member

@zanieb zanieb Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this being logged if exclude newer is set and we visit this package? That seems misleading? Shouldn't we only log it if we actually filtered a distribution due to exclude newer?

Why should we have this in a debug log rather than a hint on failed resolution?

}
}
return Ok(None);
};

Expand Down Expand Up @@ -1480,6 +1495,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
candidate.version().clone().without_local(),
));

let exclude_newer = self.options.exclude_newer.exclude_newer_package(name);
let Some(base_candidate) = self.selector.select(
name,
&range,
Expand All @@ -1490,6 +1506,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
index,
env,
self.tags.as_ref(),
exclude_newer,
) else {
return Ok(None);
};
Expand Down Expand Up @@ -2495,6 +2512,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag

// Try to find a compatible version. If there aren't any compatible versions,
// short-circuit.
let exclude_newer = self.options.exclude_newer.exclude_newer_package(&package_name);
let Some(candidate) = self.selector.select(
&package_name,
&range,
Expand All @@ -2505,6 +2523,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
None,
&env,
self.tags.as_ref(),
exclude_newer,
) else {
return Ok(None);
};
Expand Down
1 change: 1 addition & 0 deletions crates/uv/tests/it/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17995,6 +17995,7 @@ fn lock_explicit_default_index() -> Result<()> {
DEBUG Searching for a compatible version of project @ file://[TEMP_DIR]/ (*)
DEBUG Adding direct dependency: anyio*
DEBUG Searching for a compatible version of anyio (*)
DEBUG Excluding candidates for anyio published after 2024-03-25T00:00:00Z
DEBUG No compatible version found for: anyio
DEBUG Recording unit propagation conflict of anyio from incompatibility of (project)
DEBUG Searching for a compatible version of project @ file://[TEMP_DIR]/ (<0.1.0 | >0.1.0)
Expand Down
Loading