Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions crates/uv-bench/benches/uv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ mod resolver {
use uv_install_wheel::LinkMode;
use uv_pep440::Version;
use uv_pep508::{MarkerEnvironment, MarkerEnvironmentBuilder};
use uv_platform_tags::{Arch, Os, Platform, Tags};
use uv_platform_tags::{Arch, Os, Platform, Tags, TagsOptions};
use uv_preview::Preview;
use uv_pypi_types::{Conflicts, ResolverMarkerEnvironment};
use uv_python::Interpreter;
Expand Down Expand Up @@ -160,7 +160,14 @@ mod resolver {
);

static TAGS: LazyLock<Tags> = LazyLock::new(|| {
Tags::from_env(&PLATFORM, (3, 11), "cpython", (3, 11), false, false, false).unwrap()
Tags::from_env(
&PLATFORM,
(3, 11),
"cpython",
(3, 11),
TagsOptions::default(),
)
.unwrap()
});

pub(crate) async fn resolve(
Expand Down
29 changes: 19 additions & 10 deletions crates/uv-installer/src/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -787,7 +787,7 @@ impl Plan {
mod tests {
use super::*;
use std::str::FromStr;
use uv_platform_tags::{Arch, Os, Platform};
use uv_platform_tags::{Arch, Os, Platform, TagsOptions};

#[test]
fn test_abi3_on_free_threaded_python_hint() {
Expand All @@ -804,9 +804,12 @@ mod tests {
(3, 14), // python_version
"cpython", // implementation_name
(3, 14), // implementation_version
true, // manylinux_compatible
true, // gil_disabled (free-threaded)
false, // is_cross
TagsOptions {
manylinux_compatible: true,
gil_disabled: true,
debug_enabled: false,
is_cross: false,
},
)
.unwrap();

Expand Down Expand Up @@ -836,9 +839,12 @@ mod tests {
(3, 14), // python_version
"cpython", // implementation_name
(3, 14), // implementation_version
true, // manylinux_compatible
true, // gil_disabled (free-threaded)
false, // is_cross
TagsOptions {
manylinux_compatible: true,
gil_disabled: true,
debug_enabled: false,
is_cross: false,
},
)
.unwrap();

Expand Down Expand Up @@ -868,9 +874,12 @@ mod tests {
(3, 14), // python_version
"cpython", // implementation_name
(3, 14), // implementation_version
true, // manylinux_compatible
false, // gil_disabled (regular Python)
false, // is_cross
TagsOptions {
manylinux_compatible: true,
gil_disabled: false,
debug_enabled: false,
is_cross: false,
},
)
.unwrap();

Expand Down
4 changes: 3 additions & 1 deletion crates/uv-platform-tags/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ pub use abi_tag::{AbiTag, CPythonAbiVariants, ParseAbiTagError};
pub use language_tag::{LanguageTag, ParseLanguageTagError};
pub use platform::{Arch, Os, Platform, PlatformError};
pub use platform_tag::{ParsePlatformTagError, PlatformTag};
pub use tags::{BinaryFormat, IncompatibleTag, TagCompatibility, TagPriority, Tags, TagsError};
pub use tags::{
BinaryFormat, IncompatibleTag, TagCompatibility, TagPriority, Tags, TagsError, TagsOptions,
};

mod abi_tag;
mod language_tag;
Expand Down
125 changes: 106 additions & 19 deletions crates/uv-platform-tags/src/tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ pub enum TagsError {
InvalidPriority(usize, #[source] std::num::TryFromIntError),
#[error("Only CPython can be freethreading, not: {0}")]
GilIsACPythonProblem(String),
#[error("Only CPython can be debug-enabled, not: {0}")]
DebugIsACPythonProblem(String),
}

#[derive(Debug, Clone, Copy, Default)]
pub struct TagsOptions {
pub manylinux_compatible: bool,
pub gil_disabled: bool,
pub debug_enabled: bool,
pub is_cross: bool,
}

#[derive(Debug, Eq, Ord, PartialEq, PartialOrd, Copy, Clone)]
Expand All @@ -41,6 +51,10 @@ pub enum IncompatibleTag {
Platform,
}

/// Whether a wheel is compatible or incompatible with a set of tags, and which priority it has
/// compared to other wheels.
///
/// A higher tag compatibility means higher priority.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum TagCompatibility {
Incompatible(IncompatibleTag),
Expand Down Expand Up @@ -136,19 +150,25 @@ impl Tags {
python_version: (u8, u8),
implementation_name: &str,
implementation_version: (u8, u8),
manylinux_compatible: bool,
gil_disabled: bool,
is_cross: bool,
options: TagsOptions,
) -> Result<Self, TagsError> {
let mut variant = CPythonAbiVariants::default();
if gil_disabled {
if options.gil_disabled {
if implementation_name != "cpython" {
return Err(TagsError::GilIsACPythonProblem(
implementation_name.to_string(),
));
}
variant.insert(CPythonAbiVariants::Freethreading);
}
if options.debug_enabled {
if implementation_name != "cpython" {
return Err(TagsError::DebugIsACPythonProblem(
implementation_name.to_string(),
));
}
variant.insert(CPythonAbiVariants::Debug);
}
// Sufficiently correct assumption, pre-3.8 Pythons were generally built with pymalloc.
// https://docs.python.org/dev/whatsnew/3.8.html#build-and-c-api-changes
// > the m flag for pymalloc became useless (builds with and without pymalloc are ABI
Expand All @@ -162,7 +182,7 @@ impl Tags {
// Determine the compatible tags for the current platform.
let platform_tags = {
let mut platform_tags = compatible_tags(platform)?;
if matches!(platform.os(), Os::Manylinux { .. }) && !manylinux_compatible {
if matches!(platform.os(), Os::Manylinux { .. }) && !options.manylinux_compatible {
platform_tags.retain(|tag| !tag.is_manylinux());
}
platform_tags
Expand All @@ -178,6 +198,26 @@ impl Tags {
platform_tag.clone(),
));
}
// 1a. For CPython 3.8+, debug builds are ABI-compatible with release builds, so a debug
// interpreter also accept non-debug wheels.
if python_version >= (3, 8)
&& let Implementation::CPython { variant } = implementation
&& variant.contains(CPythonAbiVariants::Debug)
{
let mut non_debug_variant = variant;
non_debug_variant.remove(CPythonAbiVariants::Debug);
let debug_abi = AbiTag::CPython {
variant: non_debug_variant,
python_version,
};
for platform_tag in &platform_tags {
tags.push((
implementation.language_tag(python_version),
debug_abi,
platform_tag.clone(),
));
}
}
// 2. abi3/abi3t and no abi (e.g. executable binary)
if let Implementation::CPython { variant } = implementation {
// Emit `abi3t` everywhere we'd emit `abi3` for non-free-threaded builds.
Expand Down Expand Up @@ -270,8 +310,8 @@ impl Tags {
tags,
platform.clone(),
python_version,
is_cross,
gil_disabled,
options.is_cross,
options.gil_disabled,
))
}

Expand Down Expand Up @@ -1546,9 +1586,7 @@ mod tests {
(3, 9),
"cpython",
(3, 9),
false,
false,
false,
TagsOptions::default(),
)
.unwrap();
assert_snapshot!(
Expand Down Expand Up @@ -1610,9 +1648,10 @@ mod tests {
(3, 9),
"cpython",
(3, 9),
true,
false,
false,
TagsOptions {
manylinux_compatible: true,
..TagsOptions::default()
},
)
.unwrap();
assert_snapshot!(
Expand Down Expand Up @@ -2235,9 +2274,7 @@ mod tests {
(3, 9),
"cpython",
(3, 9),
false,
false,
false,
TagsOptions::default(),
)
.unwrap();
assert_snapshot!(
Expand Down Expand Up @@ -2713,9 +2750,11 @@ mod tests {
(3, 15),
"cpython",
(3, 15),
false,
true,
false,
TagsOptions {
manylinux_compatible: false,
gil_disabled: true,
..TagsOptions::default()
},
)
.unwrap();

Expand Down Expand Up @@ -2776,4 +2815,52 @@ mod tests {
"
);
}

#[test]
fn test_system_tags_debug_cpython() {
fn debug_compatibilities(debug_enabled: bool) -> (TagCompatibility, TagCompatibility) {
let tags = Tags::from_env(
&Platform::new(
Os::Manylinux {
major: 2,
minor: 28,
},
Arch::X86_64,
),
(3, 14),
"cpython",
(3, 14),
TagsOptions {
manylinux_compatible: true,
debug_enabled,
..TagsOptions::default()
},
)
.unwrap();

let debug_compatibility = tags.compatibility(
&[LanguageTag::from_str("cp314").unwrap()],
&[AbiTag::from_str("cp314d").unwrap()],
&[PlatformTag::from_str("manylinux_2_28_x86_64").unwrap()],
);
let non_debug_compatibility = tags.compatibility(
&[LanguageTag::from_str("cp314").unwrap()],
&[AbiTag::from_str("cp314").unwrap()],
&[PlatformTag::from_str("manylinux_2_28_x86_64").unwrap()],
);
(debug_compatibility, non_debug_compatibility)
}

// A regular CPython build is not compatible with debug wheels.
let (debug_compatibility, non_debug_compatibility) = debug_compatibilities(false);
assert!(!debug_compatibility.is_compatible());
assert!(non_debug_compatibility.is_compatible());

// A debug CPython build is compatible with debug and non-debug wheels, preferring debug
// wheels.
let (debug_compatibility, non_debug_compatibility) = debug_compatibilities(true);
assert!(debug_compatibility.is_compatible());
assert!(non_debug_compatibility.is_compatible());
assert!(debug_compatibility > non_debug_compatibility);
}
}
11 changes: 7 additions & 4 deletions crates/uv-python/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use uv_install_wheel::Layout;
use uv_pep440::Version;
use uv_pep508::{MarkerEnvironment, StringVersion};
use uv_platform::{Arch, Libc, Os};
use uv_platform_tags::{Platform, Tags, TagsError};
use uv_platform_tags::{Platform, Tags, TagsError, TagsOptions};
use uv_pypi_types::{ResolverMarkerEnvironment, Scheme};

use crate::implementation::LenientImplementationName;
Expand Down Expand Up @@ -253,9 +253,12 @@ impl Interpreter {
self.python_tuple(),
self.implementation_name(),
self.implementation_tuple(),
self.manylinux_compatible,
self.gil_disabled,
false,
TagsOptions {
manylinux_compatible: self.manylinux_compatible,
gil_disabled: self.gil_disabled,
debug_enabled: self.debug_enabled,
is_cross: false,
},
)?;
self.tags.set(tags).expect("tags should not be set");
}
Expand Down
11 changes: 7 additions & 4 deletions crates/uv/src/commands/pip/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::borrow::Cow;

use uv_configuration::TargetTriple;
use uv_platform_tags::{Tags, TagsError};
use uv_platform_tags::{Tags, TagsError, TagsOptions};
use uv_pypi_types::ResolverMarkerEnvironment;
use uv_python::{Interpreter, PythonVersion};

Expand Down Expand Up @@ -66,9 +66,12 @@ pub(crate) fn resolution_tags<'env>(
version_tuple,
interpreter.implementation_name(),
interpreter.implementation_tuple(),
manylinux_compatible,
interpreter.gil_disabled(),
true,
TagsOptions {
manylinux_compatible,
gil_disabled: interpreter.gil_disabled(),
debug_enabled: interpreter.debug_enabled(),
is_cross: true,
},
)?;
Ok(Cow::Owned(tags))
}
Loading
Loading