From 2851409bb07383827aed3ef61bfba53c1d4d0f4e Mon Sep 17 00:00:00 2001 From: konstin Date: Wed, 10 Dec 2025 11:29:57 +0100 Subject: [PATCH] Support `cp3-none-any` According to https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/, `cp3-none-any` is a valid tag, even though you shouldn't use it. We should accept it. --- .../src/prioritized_distribution.rs | 2 +- crates/uv-platform-tags/src/language_tag.rs | 45 ++++++++++++++++--- crates/uv-platform-tags/src/tags.rs | 5 ++- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/crates/uv-distribution-types/src/prioritized_distribution.rs b/crates/uv-distribution-types/src/prioritized_distribution.rs index c96ad9ade..591c7754a 100644 --- a/crates/uv-distribution-types/src/prioritized_distribution.rs +++ b/crates/uv-distribution-types/src/prioritized_distribution.rs @@ -930,7 +930,7 @@ fn implied_python_markers(filename: &WheelFilename) -> MarkerTree { // No Python tag means no Python version requirement. return MarkerTree::TRUE; } - LanguageTag::Python { major, minor: None } => { + LanguageTag::Python { major, minor: None } | LanguageTag::CPythonMajor { major } => { MarkerTree::expression(MarkerExpression::Version { key: uv_pep508::MarkerValueVersion::PythonVersion, specifier: VersionSpecifier::equals_star_version(Version::new([u64::from( diff --git a/crates/uv-platform-tags/src/language_tag.rs b/crates/uv-platform-tags/src/language_tag.rs index 54b9442fd..5fd20bcef 100644 --- a/crates/uv-platform-tags/src/language_tag.rs +++ b/crates/uv-platform-tags/src/language_tag.rs @@ -32,6 +32,8 @@ pub enum LanguageTag { GraalPy { python_version: (u8, u8) }, /// Ex) `pyston38` Pyston { python_version: (u8, u8) }, + /// Ex) `cp3` + CPythonMajor { major: u8 }, } impl LanguageTag { @@ -58,6 +60,7 @@ impl LanguageTag { Self::Pyston { python_version: (major, minor), } => Some(format!("Pyston {major}.{minor}")), + Self::CPythonMajor { major } => Some(format!("CPython {major}")), } } } @@ -94,6 +97,9 @@ impl std::fmt::Display for LanguageTag { } => { write!(f, "pyston{major}{minor}") } + Self::CPythonMajor { major } => { + write!(f, "cp{major}") + } } } } @@ -181,11 +187,36 @@ impl FromStr for LanguageTag { } } } else if let Some(cp) = s.strip_prefix("cp") { - // Ex) `cp39` - let (major, minor) = parse_python_version(cp, "CPython", s)?; - Ok(Self::CPython { - python_version: (major, minor), - }) + match cp.len() { + 0 => Err(ParseLanguageTagError::MissingMajorVersion { + implementation: "CPython", + tag: s.to_string(), + }), + 1 => { + // Ex) `cp3` + let major = cp + .chars() + .next() + .ok_or_else(|| ParseLanguageTagError::MissingMajorVersion { + implementation: "CPython", + tag: s.to_string(), + })? + .to_digit(10) + .ok_or_else(|| ParseLanguageTagError::InvalidMajorVersion { + implementation: "CPython", + tag: s.to_string(), + })? as u8; + Ok(Self::CPythonMajor { major }) + } + 2 | 3 => { + // Ex) `cp39`, `cp310` + let (major, minor) = parse_python_version(cp, "CPython", s)?; + Ok(Self::CPython { + python_version: (major, minor), + }) + } + _ => Err(ParseLanguageTagError::UnknownFormat(s.to_string())), + } } else if let Some(pp) = s.strip_prefix("pp") { // Ex) `pp39` let (major, minor) = parse_python_version(pp, "PyPy", s)?; @@ -290,6 +321,10 @@ mod tests { assert_eq!(LanguageTag::from_str("cp39"), Ok(tag)); assert_eq!(tag.to_string(), "cp39"); + let tag = LanguageTag::CPythonMajor { major: 3 }; + assert_eq!(LanguageTag::from_str("cp3"), Ok(tag)); + assert_eq!(tag.to_string(), "cp3"); + assert_eq!( LanguageTag::from_str("cp"), Err(ParseLanguageTagError::MissingMajorVersion { diff --git a/crates/uv-platform-tags/src/tags.rs b/crates/uv-platform-tags/src/tags.rs index dd0993678..720517fcd 100644 --- a/crates/uv-platform-tags/src/tags.rs +++ b/crates/uv-platform-tags/src/tags.rs @@ -401,7 +401,10 @@ impl Implementation { fn language_tag(self, python_version: (u8, u8)) -> LanguageTag { match self { // Ex) `cp39` - Self::CPython { .. } => LanguageTag::CPython { python_version }, + Self::CPython { .. } => LanguageTag::CPython { + major: python_version.0, + minor: Some(python_version.1), + }, // Ex) `pp39` Self::PyPy => LanguageTag::PyPy { python_version }, // Ex) `graalpy310`