mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-01 04:17:37 +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,
|
pub version: Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Version> for StringVersion {
|
||||||
|
fn from(version: Version) -> Self {
|
||||||
|
Self {
|
||||||
|
string: version.to_string(),
|
||||||
|
version,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromStr for StringVersion {
|
impl FromStr for StringVersion {
|
||||||
type Err = VersionParseError;
|
type Err = VersionParseError;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
|
||||||
.index_strategy(self.index_strategy)
|
.index_strategy(self.index_strategy)
|
||||||
.build(),
|
.build(),
|
||||||
&python_requirement,
|
&python_requirement,
|
||||||
ResolverMarkers::SpecificEnvironment(markers.clone()),
|
ResolverMarkers::specific_environment(markers.clone()),
|
||||||
Some(tags),
|
Some(tags),
|
||||||
self.flat_index,
|
self.flat_index,
|
||||||
self.index,
|
self.index,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
use pep508_rs::{MarkerEnvironment, MarkerTree};
|
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.
|
/// Whether we're solving for a specific environment, universally or for a specific fork.
|
||||||
pub enum ResolverMarkers {
|
pub enum ResolverMarkers {
|
||||||
/// We're solving for this specific environment only.
|
/// We're solving for this specific environment only.
|
||||||
SpecificEnvironment(MarkerEnvironment),
|
SpecificEnvironment(ResolverMarkerEnvironment),
|
||||||
/// We're doing a universal resolution for all environments (a python version
|
/// We're doing a universal resolution for all environments (a python version
|
||||||
/// constraint is expressed separately).
|
/// constraint is expressed separately).
|
||||||
Universal {
|
Universal {
|
||||||
|
|
@ -20,7 +21,7 @@ pub enum ResolverMarkers {
|
||||||
impl ResolverMarkers {
|
impl ResolverMarkers {
|
||||||
/// Set the resolver to perform a resolution for a specific environment.
|
/// Set the resolver to perform a resolution for a specific environment.
|
||||||
pub fn specific_environment(markers: MarkerEnvironment) -> Self {
|
pub fn specific_environment(markers: MarkerEnvironment) -> Self {
|
||||||
Self::SpecificEnvironment(markers)
|
Self::SpecificEnvironment(ResolverMarkerEnvironment::from(markers))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the resolver to perform a universal resolution.
|
/// 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)?;
|
resolution_environment(python_version, python_platform, &interpreter)?;
|
||||||
(
|
(
|
||||||
Some(tags),
|
Some(tags),
|
||||||
ResolverMarkers::SpecificEnvironment((*markers).clone()),
|
ResolverMarkers::specific_environment((*markers).clone()),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -348,7 +348,7 @@ pub(crate) async fn pip_install(
|
||||||
&reinstall,
|
&reinstall,
|
||||||
&upgrade,
|
&upgrade,
|
||||||
Some(&tags),
|
Some(&tags),
|
||||||
ResolverMarkers::SpecificEnvironment((*markers).clone()),
|
ResolverMarkers::specific_environment((*markers).clone()),
|
||||||
python_requirement,
|
python_requirement,
|
||||||
&client,
|
&client,
|
||||||
&flat_index,
|
&flat_index,
|
||||||
|
|
|
||||||
|
|
@ -296,7 +296,7 @@ pub(crate) async fn pip_sync(
|
||||||
&reinstall,
|
&reinstall,
|
||||||
&upgrade,
|
&upgrade,
|
||||||
Some(&tags),
|
Some(&tags),
|
||||||
ResolverMarkers::SpecificEnvironment((*markers).clone()),
|
ResolverMarkers::specific_environment((*markers).clone()),
|
||||||
python_requirement,
|
python_requirement,
|
||||||
&client,
|
&client,
|
||||||
&flat_index,
|
&flat_index,
|
||||||
|
|
|
||||||
|
|
@ -626,7 +626,7 @@ pub(crate) async fn resolve_environment<'a>(
|
||||||
&reinstall,
|
&reinstall,
|
||||||
&upgrade,
|
&upgrade,
|
||||||
Some(tags),
|
Some(tags),
|
||||||
ResolverMarkers::SpecificEnvironment(markers.clone()),
|
ResolverMarkers::specific_environment(markers.clone()),
|
||||||
python_requirement,
|
python_requirement,
|
||||||
&client,
|
&client,
|
||||||
&flat_index,
|
&flat_index,
|
||||||
|
|
@ -949,7 +949,7 @@ pub(crate) async fn update_environment(
|
||||||
reinstall,
|
reinstall,
|
||||||
upgrade,
|
upgrade,
|
||||||
Some(tags),
|
Some(tags),
|
||||||
ResolverMarkers::SpecificEnvironment(markers.clone()),
|
ResolverMarkers::specific_environment(markers.clone()),
|
||||||
python_requirement,
|
python_requirement,
|
||||||
&client,
|
&client,
|
||||||
&flat_index,
|
&flat_index,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue