diff --git a/crates/distribution-types/src/prioritized_distribution.rs b/crates/distribution-types/src/prioritized_distribution.rs index fe276faa9..7f0c324c1 100644 --- a/crates/distribution-types/src/prioritized_distribution.rs +++ b/crates/distribution-types/src/prioritized_distribution.rs @@ -150,10 +150,15 @@ pub enum WheelCompatibility { #[derive(Debug, PartialEq, Eq, Clone)] pub enum IncompatibleWheel { + /// The wheel was published after the exclude newer time. ExcludeNewer(Option), + /// The wheel tags do not match those of the target Python platform. Tag(IncompatibleTag), + /// The required Python version is not a superset of the target Python version range. RequiresPython(VersionSpecifiers, PythonRequirementKind), + /// The wheel was yanked. Yanked(Yanked), + /// The use of binary wheels is disabled. NoBinary, } diff --git a/crates/uv-resolver/src/version_map.rs b/crates/uv-resolver/src/version_map.rs index 9355adb0f..0b8f3cd97 100644 --- a/crates/uv-resolver/src/version_map.rs +++ b/crates/uv-resolver/src/version_map.rs @@ -7,11 +7,10 @@ use tracing::instrument; use distribution_filename::{DistFilename, WheelFilename}; use distribution_types::{ HashComparison, IncompatibleSource, IncompatibleWheel, IndexUrl, PrioritizedDist, - PythonRequirementKind, RegistryBuiltWheel, RegistrySourceDist, SourceDistCompatibility, - WheelCompatibility, + RegistryBuiltWheel, RegistrySourceDist, SourceDistCompatibility, WheelCompatibility, }; use pep440_rs::Version; -use platform_tags::{TagCompatibility, Tags}; +use platform_tags::{IncompatibleTag, TagCompatibility, Tags}; use pypi_types::{HashDigest, Yanked}; use uv_client::{OwnedArchive, SimpleMetadata, VersionFiles}; use uv_configuration::BuildOptions; @@ -512,9 +511,8 @@ impl VersionMapLazy { // is not less than the `requires-python` minimum version). if let Some(requires_python) = self.requires_python.as_ref() { if !requires_python.matches_wheel_tag(filename) { - return WheelCompatibility::Incompatible(IncompatibleWheel::RequiresPython( - requires_python.specifiers().clone(), - PythonRequirementKind::Target, + return WheelCompatibility::Incompatible(IncompatibleWheel::Tag( + IncompatibleTag::Abi, )); } } diff --git a/crates/uv/tests/lock.rs b/crates/uv/tests/lock.rs index d20ff215f..ca2e06086 100644 --- a/crates/uv/tests/lock.rs +++ b/crates/uv/tests/lock.rs @@ -4261,3 +4261,35 @@ fn lock_resolution_mode() -> Result<()> { Ok(()) } + +/// Lock a requirement from PyPI, filtering out wheels that target an ABI that is non-overlapping +/// with the `Requires-Python` constraint. +#[test] +fn lock_requires_python_no_wheels() -> Result<()> { + let context = TestContext::new("3.12"); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = ["dearpygui==1.9.1"] + "#, + )?; + + uv_snapshot!(context.filters(), context.lock(), @r###" + success: false + exit_code: 1 + ----- stdout ----- + + ----- stderr ----- + warning: `uv lock` is experimental and may change without warning + × No solution found when resolving dependencies: + ╰─▶ Because dearpygui==1.9.1 has no wheels are available with a matching Python ABI and project==0.1.0 depends on dearpygui==1.9.1, we can conclude that project==0.1.0 cannot be used. + And because only project==0.1.0 is available and you require project, we can conclude that the requirements are unsatisfiable. + "###); + + Ok(()) +}