Fix PythonDownloadRequest parsing for partial keys (#12925)

In #12909, I noticed we failed to parse partial download keys with `any`
placeholders. Here, parsing for that is fixed.
This commit is contained in:
Zanie Blue
2025-04-18 11:25:52 -05:00
committed by GitHub
parent 6eb6475afd
commit 91410acf82
3 changed files with 107 additions and 31 deletions
+76
View File
@@ -2705,12 +2705,15 @@ mod tests {
use std::{path::PathBuf, str::FromStr};
use assert_fs::{prelude::*, TempDir};
use target_lexicon::{Aarch64Architecture, Architecture};
use test_log::test;
use uv_pep440::{Prerelease, PrereleaseKind, VersionSpecifiers};
use crate::{
discovery::{PythonRequest, VersionRequest},
downloads::PythonDownloadRequest,
implementation::ImplementationName,
platform::{Arch, Libc, Os},
};
use super::{Error, PythonVariant};
@@ -2763,6 +2766,7 @@ mod tests {
PythonRequest::parse("cpython"),
PythonRequest::Implementation(ImplementationName::CPython)
);
assert_eq!(
PythonRequest::parse("cpython3.12.2"),
PythonRequest::ImplementationVersion(
@@ -2770,6 +2774,78 @@ mod tests {
VersionRequest::from_str("3.12.2").unwrap(),
)
);
assert_eq!(
PythonRequest::parse("cpython-3.13.2"),
PythonRequest::Key(PythonDownloadRequest {
version: Some(VersionRequest::MajorMinorPatch(
3,
13,
2,
PythonVariant::Default
)),
implementation: Some(ImplementationName::CPython),
arch: None,
os: None,
libc: None,
prereleases: None
})
);
assert_eq!(
PythonRequest::parse("cpython-3.13.2-macos-aarch64-none"),
PythonRequest::Key(PythonDownloadRequest {
version: Some(VersionRequest::MajorMinorPatch(
3,
13,
2,
PythonVariant::Default
)),
implementation: Some(ImplementationName::CPython),
arch: Some(Arch {
family: Architecture::Aarch64(Aarch64Architecture::Aarch64),
variant: None
}),
os: Some(Os(target_lexicon::OperatingSystem::Darwin(None))),
libc: Some(Libc::None),
prereleases: None
})
);
assert_eq!(
PythonRequest::parse("any-3.13.2"),
PythonRequest::Key(PythonDownloadRequest {
version: Some(VersionRequest::MajorMinorPatch(
3,
13,
2,
PythonVariant::Default
)),
implementation: None,
arch: None,
os: None,
libc: None,
prereleases: None
})
);
assert_eq!(
PythonRequest::parse("any-3.13.2-any-aarch64"),
PythonRequest::Key(PythonDownloadRequest {
version: Some(VersionRequest::MajorMinorPatch(
3,
13,
2,
PythonVariant::Default
)),
implementation: None,
arch: Some(Arch {
family: Architecture::Aarch64(Aarch64Architecture::Aarch64),
variant: None
}),
os: None,
libc: None,
prereleases: None
})
);
assert_eq!(
PythonRequest::parse("pypy"),
PythonRequest::Implementation(ImplementationName::PyPy)
+21 -31
View File
@@ -107,15 +107,15 @@ pub struct ManagedPythonDownload {
#[derive(Debug, Clone, Default, Eq, PartialEq)]
pub struct PythonDownloadRequest {
version: Option<VersionRequest>,
implementation: Option<ImplementationName>,
arch: Option<Arch>,
os: Option<Os>,
libc: Option<Libc>,
pub(crate) version: Option<VersionRequest>,
pub(crate) implementation: Option<ImplementationName>,
pub(crate) arch: Option<Arch>,
pub(crate) os: Option<Os>,
pub(crate) libc: Option<Libc>,
/// Whether to allow pre-releases or not. If not set, defaults to true if [`Self::version`] is
/// not None, and false otherwise.
prereleases: Option<bool>,
pub(crate) prereleases: Option<bool>,
}
impl PythonDownloadRequest {
@@ -424,39 +424,29 @@ impl FromStr for PythonDownloadRequest {
let mut arch = None;
let mut libc = None;
let mut position = 0;
loop {
// Consume each part
let Some(part) = parts.next() else { break };
position += 1;
if implementation.is_none() {
implementation = Some(ImplementationName::from_str(part)?);
if part.eq_ignore_ascii_case("any") {
continue;
}
if version.is_none() {
version = Some(
VersionRequest::from_str(part)
.map_err(|_| Error::InvalidPythonVersion(part.to_string()))?,
);
continue;
match position {
1 => implementation = Some(ImplementationName::from_str(part)?),
2 => {
version = Some(
VersionRequest::from_str(part)
.map_err(|_| Error::InvalidPythonVersion(part.to_string()))?,
);
}
3 => os = Some(Os::from_str(part)?),
4 => arch = Some(Arch::from_str(part)?),
5 => libc = Some(Libc::from_str(part)?),
_ => return Err(Error::TooManyParts(s.to_string())),
}
if os.is_none() {
os = Some(Os::from_str(part)?);
continue;
}
if arch.is_none() {
arch = Some(Arch::from_str(part)?);
continue;
}
if libc.is_none() {
libc = Some(Libc::from_str(part)?);
continue;
}
return Err(Error::TooManyParts(s.to_string()));
}
Ok(Self::new(version, implementation, arch, os, libc, None))
}