diff --git a/crates/pep508-rs/src/marker/tree.rs b/crates/pep508-rs/src/marker/tree.rs index 556d45f145f7..e634c5f4f8e0 100644 --- a/crates/pep508-rs/src/marker/tree.rs +++ b/crates/pep508-rs/src/marker/tree.rs @@ -380,6 +380,15 @@ pub struct StringVersion { pub version: Version, } +impl From for StringVersion { + fn from(version: Version) -> Self { + Self { + string: version.to_string(), + version, + } + } +} + impl FromStr for StringVersion { type Err = VersionParseError; diff --git a/crates/uv-dispatch/src/lib.rs b/crates/uv-dispatch/src/lib.rs index 79d36e97f841..3d307e4c0269 100644 --- a/crates/uv-dispatch/src/lib.rs +++ b/crates/uv-dispatch/src/lib.rs @@ -155,7 +155,7 @@ impl<'a> BuildContext for BuildDispatch<'a> { .index_strategy(self.index_strategy) .build(), &python_requirement, - ResolverMarkers::SpecificEnvironment(markers.clone()), + ResolverMarkers::specific_environment(markers.clone()), Some(tags), self.flat_index, self.index, diff --git a/crates/uv-resolver/src/resolver/resolver_markers.rs b/crates/uv-resolver/src/resolver/resolver_markers.rs index 71af23cbd3fd..6832eee92375 100644 --- a/crates/uv-resolver/src/resolver/resolver_markers.rs +++ b/crates/uv-resolver/src/resolver/resolver_markers.rs @@ -1,4 +1,5 @@ use std::fmt::{Display, Formatter}; +use tracing::debug; use pep508_rs::{MarkerEnvironment, MarkerTree}; @@ -6,7 +7,7 @@ use pep508_rs::{MarkerEnvironment, MarkerTree}; /// Whether we're solving for a specific environment, universally or for a specific fork. pub enum ResolverMarkers { /// We're solving for this specific environment only. - SpecificEnvironment(MarkerEnvironment), + SpecificEnvironment(ResolverMarkerEnvironment), /// We're doing a universal resolution for all environments (a python version /// constraint is expressed separately). Universal { @@ -20,7 +21,7 @@ pub enum ResolverMarkers { impl ResolverMarkers { /// Set the resolver to perform a resolution for a specific environment. pub fn specific_environment(markers: MarkerEnvironment) -> Self { - Self::SpecificEnvironment(markers) + Self::SpecificEnvironment(ResolverMarkerEnvironment::from(markers)) } /// Set the resolver to perform a universal resolution. @@ -70,3 +71,46 @@ impl Display for ResolverMarkers { } } } + +/// A wrapper type around [`MarkerEnvironment`] that ensures the Python version markers are +/// release-only, to match the resolver's semantics. +#[derive(Debug, Clone)] +pub struct ResolverMarkerEnvironment(MarkerEnvironment); + +impl From for ResolverMarkerEnvironment { + fn from(value: MarkerEnvironment) -> Self { + // Strip `python_version`. + let python_version = value.python_version().only_release(); + let value = if python_version == **value.python_version() { + value + } else { + debug!( + "Stripping pre-release from `python_version`: {}", + value.python_version() + ); + value.with_python_version(python_version) + }; + + // Strip `python_full_version`. + let python_full_version = value.python_full_version().only_release(); + let value = if python_full_version == **value.python_full_version() { + value + } else { + debug!( + "Stripping pre-release from `python_full_version`: {}", + value.python_full_version() + ); + value.with_python_full_version(python_full_version) + }; + + Self(value) + } +} + +impl std::ops::Deref for ResolverMarkerEnvironment { + type Target = MarkerEnvironment; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/crates/uv/src/commands/pip/compile.rs b/crates/uv/src/commands/pip/compile.rs index 7562ae7ef309..9dc075177255 100644 --- a/crates/uv/src/commands/pip/compile.rs +++ b/crates/uv/src/commands/pip/compile.rs @@ -251,7 +251,7 @@ pub(crate) async fn pip_compile( resolution_environment(python_version, python_platform, &interpreter)?; ( Some(tags), - ResolverMarkers::SpecificEnvironment((*markers).clone()), + ResolverMarkers::specific_environment((*markers).clone()), ) }; diff --git a/crates/uv/src/commands/pip/install.rs b/crates/uv/src/commands/pip/install.rs index ff44bb5af564..65e0deb05702 100644 --- a/crates/uv/src/commands/pip/install.rs +++ b/crates/uv/src/commands/pip/install.rs @@ -348,7 +348,7 @@ pub(crate) async fn pip_install( &reinstall, &upgrade, Some(&tags), - ResolverMarkers::SpecificEnvironment((*markers).clone()), + ResolverMarkers::specific_environment((*markers).clone()), python_requirement, &client, &flat_index, diff --git a/crates/uv/src/commands/pip/sync.rs b/crates/uv/src/commands/pip/sync.rs index 89466fdaf4ec..fc997d7d766f 100644 --- a/crates/uv/src/commands/pip/sync.rs +++ b/crates/uv/src/commands/pip/sync.rs @@ -296,7 +296,7 @@ pub(crate) async fn pip_sync( &reinstall, &upgrade, Some(&tags), - ResolverMarkers::SpecificEnvironment((*markers).clone()), + ResolverMarkers::specific_environment((*markers).clone()), python_requirement, &client, &flat_index, diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs index ad0b5cbb5a6e..71187ab51437 100644 --- a/crates/uv/src/commands/project/mod.rs +++ b/crates/uv/src/commands/project/mod.rs @@ -626,7 +626,7 @@ pub(crate) async fn resolve_environment<'a>( &reinstall, &upgrade, Some(tags), - ResolverMarkers::SpecificEnvironment(markers.clone()), + ResolverMarkers::specific_environment(markers.clone()), python_requirement, &client, &flat_index, @@ -949,7 +949,7 @@ pub(crate) async fn update_environment( reinstall, upgrade, Some(tags), - ResolverMarkers::SpecificEnvironment(markers.clone()), + ResolverMarkers::specific_environment(markers.clone()), python_requirement, &client, &flat_index,