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.
This commit is contained in:
konstin 2025-12-10 11:29:57 +01:00
parent 2b5d65e61d
commit 2851409bb0
3 changed files with 45 additions and 7 deletions

View File

@ -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(

View File

@ -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 {

View File

@ -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`