mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 10:58:28 +00:00
Respect release-only semantics of python_full_version when constructing markers (#6171)
## Summary In the resolver, we use release-only semantics to normalize `python_full_version`. So, if we see `python_full_version < '3.13'`, we treat that as `(Unbounded, Exclude(3.13))`. `3.13b0` evaluates as `true` to that range, so we were accepting pre-releases for these markers. Instead, we need to exclude pre-release segments when performing these evaluations. Closes https://github.com/astral-sh/uv/issues/6169. ## Test Plan Hard to write a test for this because you need a pre-release Python locally... so: `echo "sqlalchemy==2.0.32" | cargo run pip compile - --python 3.13 -n`
This commit is contained in:
parent
ad8e3a2c32
commit
5ac0b98e00
7 changed files with 61 additions and 8 deletions
|
@ -380,6 +380,15 @@ pub struct StringVersion {
|
|||
pub version: Version,
|
||||
}
|
||||
|
||||
impl From<Version> for StringVersion {
|
||||
fn from(version: Version) -> Self {
|
||||
Self {
|
||||
string: version.to_string(),
|
||||
version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for StringVersion {
|
||||
type Err = VersionParseError;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<MarkerEnvironment> 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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()),
|
||||
)
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue