diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs index 89640845bdc0..a3db508ada8d 100644 --- a/crates/uv-resolver/src/resolver/mod.rs +++ b/crates/uv-resolver/src/resolver/mod.rs @@ -154,6 +154,9 @@ impl<'a, Context: BuildContext, InstalledPackages: InstalledPackagesProvider> database, flat_index, tags, + python_requirement + .target() + .and_then(|target| target.as_requires_python()), AllowedYanks::from_manifest(&manifest, markers, options.dependency_mode), hasher, options.exclude_newer, diff --git a/crates/uv-resolver/src/resolver/provider.rs b/crates/uv-resolver/src/resolver/provider.rs index e87ccfd827d7..20d0224c0bac 100644 --- a/crates/uv-resolver/src/resolver/provider.rs +++ b/crates/uv-resolver/src/resolver/provider.rs @@ -10,7 +10,7 @@ use uv_types::{BuildContext, HashStrategy}; use crate::flat_index::FlatIndex; use crate::version_map::VersionMap; use crate::yanks::AllowedYanks; -use crate::ExcludeNewer; +use crate::{ExcludeNewer, RequiresPython}; pub type PackageVersionsResult = Result; pub type WheelMetadataResult = Result; @@ -76,6 +76,7 @@ pub struct DefaultResolverProvider<'a, Context: BuildContext> { /// These are the entries from `--find-links` that act as overrides for index responses. flat_index: FlatIndex, tags: Option, + requires_python: Option, allowed_yanks: AllowedYanks, hasher: HashStrategy, exclude_newer: Option, @@ -88,6 +89,7 @@ impl<'a, Context: BuildContext> DefaultResolverProvider<'a, Context> { fetcher: DistributionDatabase<'a, Context>, flat_index: &'a FlatIndex, tags: Option<&'a Tags>, + requires_python: Option<&'a RequiresPython>, allowed_yanks: AllowedYanks, hasher: &'a HashStrategy, exclude_newer: Option, @@ -97,6 +99,7 @@ impl<'a, Context: BuildContext> DefaultResolverProvider<'a, Context> { fetcher, flat_index: flat_index.clone(), tags: tags.cloned(), + requires_python: requires_python.cloned(), allowed_yanks, hasher: hasher.clone(), exclude_newer, @@ -127,6 +130,7 @@ impl<'a, Context: BuildContext> ResolverProvider for DefaultResolverProvider<'a, package_name, &index, self.tags.as_ref(), + self.requires_python.as_ref(), &self.allowed_yanks, &self.hasher, self.exclude_newer.as_ref(), diff --git a/crates/uv-resolver/src/version_map.rs b/crates/uv-resolver/src/version_map.rs index 8a720bbea199..341cd7b95156 100644 --- a/crates/uv-resolver/src/version_map.rs +++ b/crates/uv-resolver/src/version_map.rs @@ -8,7 +8,8 @@ use tracing::instrument; use distribution_filename::{DistFilename, WheelFilename}; use distribution_types::{ HashComparison, IncompatibleSource, IncompatibleWheel, IndexUrl, PrioritizedDist, - RegistryBuiltWheel, RegistrySourceDist, SourceDistCompatibility, WheelCompatibility, + PythonRequirementKind, RegistryBuiltWheel, RegistrySourceDist, SourceDistCompatibility, + WheelCompatibility, }; use pep440_rs::Version; use platform_tags::{TagCompatibility, Tags}; @@ -20,7 +21,7 @@ use uv_types::HashStrategy; use uv_warnings::warn_user_once; use crate::flat_index::FlatDistributions; -use crate::{yanks::AllowedYanks, ExcludeNewer}; +use crate::{yanks::AllowedYanks, ExcludeNewer, RequiresPython}; /// A map from versions to distributions. #[derive(Debug)] @@ -44,6 +45,7 @@ impl VersionMap { package_name: &PackageName, index: &IndexUrl, tags: Option<&Tags>, + requires_python: Option<&RequiresPython>, allowed_yanks: &AllowedYanks, hasher: &HashStrategy, exclude_newer: Option<&ExcludeNewer>, @@ -105,6 +107,7 @@ impl VersionMap { no_build: build_options.no_build_package(package_name), index: index.clone(), tags: tags.cloned(), + requires_python: requires_python.cloned(), exclude_newer: exclude_newer.copied(), allowed_yanks, required_hashes, @@ -288,6 +291,8 @@ struct VersionMapLazy { allowed_yanks: FxHashSet, /// The hashes of allowed distributions. required_hashes: Vec, + /// The `requires-python` constraint for the resolution. + requires_python: Option, } impl VersionMapLazy { @@ -508,6 +513,17 @@ impl VersionMapLazy { } }; + // Check if the wheel is compatible with the `requires-python` (i.e., the Python ABI tag + // is not less than the `requires-python` minimum version). + if let Some(requires_python) = self.requires_python.as_ref() { + if !filename.matches_requires_python(requires_python.specifiers()) { + return WheelCompatibility::Incompatible(IncompatibleWheel::RequiresPython( + requires_python.specifiers().clone(), + PythonRequirementKind::Target, + )); + } + } + // Break ties with the build tag. let build_tag = filename.build_tag.clone();