Rename some Simple API structs (#15530)

## Summary

For clarity.
This commit is contained in:
Charlie Marsh 2025-08-26 09:55:58 -04:00 committed by GitHub
parent 439395dadf
commit 615e076beb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 58 additions and 52 deletions

View file

@ -6,7 +6,7 @@ use tracing::{debug, instrument, warn};
use url::Url;
use uv_pep440::VersionSpecifiers;
use uv_pypi_types::{BaseUrl, CoreMetadata, File, Hashes, Yanked};
use uv_pypi_types::{BaseUrl, CoreMetadata, Hashes, PypiFile, Yanked};
use uv_pypi_types::{HashError, LenientVersionSpecifiers};
use uv_redacted::DisplaySafeUrl;
@ -15,12 +15,12 @@ use uv_redacted::DisplaySafeUrl;
pub(crate) struct SimpleHtml {
/// The [`BaseUrl`] to which all relative URLs should be resolved.
pub(crate) base: BaseUrl,
/// The list of [`File`]s available for download sorted by filename.
pub(crate) files: Vec<File>,
/// The list of [`PypiFile`]s available for download sorted by filename.
pub(crate) files: Vec<PypiFile>,
}
impl SimpleHtml {
/// Parse the list of [`File`]s from the simple HTML page returned by the given URL.
/// Parse the list of [`PypiFile`]s from the simple HTML page returned by the given URL.
#[instrument(skip_all, fields(url = % url))]
pub(crate) fn parse(text: &str, url: &Url) -> Result<Self, Error> {
let dom = tl::parse(text, tl::ParserOptions::default())?;
@ -41,7 +41,7 @@ impl SimpleHtml {
));
// Parse each `<a>` tag, to extract the filename, hash, and URL.
let mut files: Vec<File> = dom
let mut files: Vec<PypiFile> = dom
.nodes()
.iter()
.filter_map(|node| node.as_tag())
@ -76,10 +76,10 @@ impl SimpleHtml {
Ok(Some(url))
}
/// Parse a [`File`] from an `<a>` tag.
/// Parse a [`PypiFile`] from an `<a>` tag.
///
/// Returns `None` if the `<a>` don't doesn't have an `href` attribute.
fn parse_anchor(link: &HTMLTag) -> Result<Option<File>, Error> {
fn parse_anchor(link: &HTMLTag) -> Result<Option<PypiFile>, Error> {
// Extract the href.
let Some(href) = link
.attributes()
@ -212,7 +212,7 @@ impl SimpleHtml {
.map(|upload_time| html_escape::decode_html_entities(upload_time))
.and_then(|upload_time| Timestamp::from_str(&upload_time).ok());
Ok(Some(File {
Ok(Some(PypiFile {
core_metadata,
yanked,
requires_python,
@ -296,7 +296,7 @@ mod tests {
},
),
files: [
File {
PypiFile {
core_metadata: None,
filename: "Jinja2-3.1.2-py3-none-any.whl",
hashes: Hashes {
@ -353,7 +353,7 @@ mod tests {
},
),
files: [
File {
PypiFile {
core_metadata: None,
filename: "Jinja2-3.1.2-py3-none-any.whl",
hashes: Hashes {
@ -413,7 +413,7 @@ mod tests {
},
),
files: [
File {
PypiFile {
core_metadata: None,
filename: "Jinja2-3.1.2-py3-none-any.whl",
hashes: Hashes {
@ -470,7 +470,7 @@ mod tests {
},
),
files: [
File {
PypiFile {
core_metadata: None,
filename: "Jinja2-3.1.2+233fca715f49-py3-none-any.whl",
hashes: Hashes {
@ -527,7 +527,7 @@ mod tests {
},
),
files: [
File {
PypiFile {
core_metadata: None,
filename: "Jinja2-3.1.2-py3-none-any.whl",
hashes: Hashes {
@ -584,7 +584,7 @@ mod tests {
},
),
files: [
File {
PypiFile {
core_metadata: None,
filename: "torchtext-0.17.0+cpu-cp39-cp39-win_amd64.whl",
hashes: Hashes {
@ -639,7 +639,7 @@ mod tests {
},
),
files: [
File {
PypiFile {
core_metadata: None,
filename: "Jinja2-3.1.2-py3-none-any.whl",
hashes: Hashes {
@ -770,7 +770,7 @@ mod tests {
},
),
files: [
File {
PypiFile {
core_metadata: None,
filename: "Jinja2-3.1.2-py3-none-any.whl",
hashes: Hashes {
@ -825,7 +825,7 @@ mod tests {
},
),
files: [
File {
PypiFile {
core_metadata: None,
filename: "Jinja2-3.1.2-py3-none-any.whl",
hashes: Hashes {
@ -881,7 +881,7 @@ mod tests {
},
),
files: [
File {
PypiFile {
core_metadata: None,
filename: "Jinja2-3.1.2-py3-none-any.whl",
hashes: Hashes {
@ -938,7 +938,7 @@ mod tests {
},
),
files: [
File {
PypiFile {
core_metadata: None,
filename: "Jinja2-3.1.2-py3-none-any.whl",
hashes: Hashes {
@ -1012,7 +1012,7 @@ mod tests {
},
),
files: [
File {
PypiFile {
core_metadata: None,
filename: "jaxlib-0.1.52+cuda100-cp36-none-manylinux2010_x86_64.whl",
hashes: Hashes {
@ -1028,7 +1028,7 @@ mod tests {
url: "https://storage.googleapis.com/jax-releases/cuda100/jaxlib-0.1.52+cuda100-cp36-none-manylinux2010_x86_64.whl",
yanked: None,
},
File {
PypiFile {
core_metadata: None,
filename: "jaxlib-0.1.52+cuda100-cp37-none-manylinux2010_x86_64.whl",
hashes: Hashes {
@ -1094,7 +1094,7 @@ mod tests {
},
),
files: [
File {
PypiFile {
core_metadata: None,
filename: "Flask-0.1.tar.gz",
hashes: Hashes {
@ -1112,7 +1112,7 @@ mod tests {
url: "0.1/Flask-0.1.tar.gz",
yanked: None,
},
File {
PypiFile {
core_metadata: None,
filename: "Flask-0.10.1.tar.gz",
hashes: Hashes {
@ -1130,7 +1130,7 @@ mod tests {
url: "0.10.1/Flask-0.10.1.tar.gz",
yanked: None,
},
File {
PypiFile {
core_metadata: None,
filename: "flask-3.0.1.tar.gz",
hashes: Hashes {
@ -1197,7 +1197,7 @@ mod tests {
},
),
files: [
File {
PypiFile {
core_metadata: None,
filename: "Jinja2-3.1.2-py3-none-any.whl",
hashes: Hashes {
@ -1270,7 +1270,7 @@ mod tests {
},
),
files: [
File {
PypiFile {
core_metadata: Some(
Bool(
true,
@ -1290,7 +1290,7 @@ mod tests {
url: "/whl/Jinja2-3.1.2-py3-none-any.whl",
yanked: None,
},
File {
PypiFile {
core_metadata: Some(
Bool(
true,
@ -1310,7 +1310,7 @@ mod tests {
url: "/whl/Jinja2-3.1.3-py3-none-any.whl",
yanked: None,
},
File {
PypiFile {
core_metadata: Some(
Bool(
false,
@ -1330,7 +1330,7 @@ mod tests {
url: "/whl/Jinja2-3.1.4-py3-none-any.whl",
yanked: None,
},
File {
PypiFile {
core_metadata: Some(
Bool(
false,
@ -1350,7 +1350,7 @@ mod tests {
url: "/whl/Jinja2-3.1.5-py3-none-any.whl",
yanked: None,
},
File {
PypiFile {
core_metadata: Some(
Bool(
true,

View file

@ -29,7 +29,7 @@ use uv_normalize::PackageName;
use uv_pep440::Version;
use uv_pep508::MarkerEnvironment;
use uv_platform_tags::Platform;
use uv_pypi_types::{ResolutionMetadata, SimpleJson};
use uv_pypi_types::{PypiSimpleDetail, ResolutionMetadata};
use uv_redacted::DisplaySafeUrl;
use uv_small_str::SmallString;
use uv_torch::TorchStrategy;
@ -630,10 +630,10 @@ impl RegistryClient {
.bytes()
.await
.map_err(|err| ErrorKind::from_reqwest(url.clone(), err))?;
let data: SimpleJson = serde_json::from_slice(bytes.as_ref())
let data: PypiSimpleDetail = serde_json::from_slice(bytes.as_ref())
.map_err(|err| Error::from_json_err(err, url.clone()))?;
SimpleMetadata::from_files(data.files, package_name, &url)
SimpleMetadata::from_pypi_files(data.files, package_name, &url)
}
MediaType::Html => {
let text = response
@ -1136,7 +1136,11 @@ impl SimpleMetadata {
self.0.iter()
}
fn from_files(files: Vec<uv_pypi_types::File>, package_name: &PackageName, base: &Url) -> Self {
fn from_pypi_files(
files: Vec<uv_pypi_types::PypiFile>,
package_name: &PackageName,
base: &Url,
) -> Self {
let mut map: BTreeMap<Version, VersionFiles> = BTreeMap::default();
// Convert to a reference-counted string.
@ -1188,7 +1192,7 @@ impl SimpleMetadata {
let SimpleHtml { base, files } =
SimpleHtml::parse(text, url).map_err(|err| Error::from_html_err(err, url.clone()))?;
Ok(Self::from_files(files, package_name, base.as_url()))
Ok(Self::from_pypi_files(files, package_name, base.as_url()))
}
}
@ -1261,7 +1265,7 @@ mod tests {
use url::Url;
use uv_normalize::PackageName;
use uv_pypi_types::SimpleJson;
use uv_pypi_types::PypiSimpleDetail;
use uv_redacted::DisplaySafeUrl;
use crate::{SimpleMetadata, SimpleMetadatum, html::SimpleHtml};
@ -1480,9 +1484,9 @@ mod tests {
]
}
"#;
let data: SimpleJson = serde_json::from_str(response).unwrap();
let data: PypiSimpleDetail = serde_json::from_str(response).unwrap();
let base = DisplaySafeUrl::parse("https://pypi.org/simple/pyflyby/").unwrap();
let simple_metadata = SimpleMetadata::from_files(
let simple_metadata = SimpleMetadata::from_pypi_files(
data.files,
&PackageName::from_str("pyflyby").unwrap(),
&base,

View file

@ -11,7 +11,7 @@ use uv_pypi_types::{CoreMetadata, HashDigests, Yanked};
use uv_redacted::DisplaySafeUrl;
use uv_small_str::SmallString;
/// Error converting [`uv_pypi_types::File`] to [`distribution_type::File`].
/// Error converting [`uv_pypi_types::PypiFile`] to [`distribution_type::File`].
#[derive(Debug, thiserror::Error)]
pub enum FileConversionError {
#[error("Failed to parse `requires-python`: `{0}`")]
@ -20,7 +20,7 @@ pub enum FileConversionError {
Url(String, #[source] url::ParseError),
}
/// Internal analog to [`uv_pypi_types::File`].
/// Internal analog to [`uv_pypi_types::PypiFile`].
#[derive(Debug, Clone, PartialEq, Eq, Hash, rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)]
#[rkyv(derive(Debug))]
pub struct File {
@ -41,7 +41,7 @@ pub struct File {
impl File {
/// `TryFrom` instead of `From` to filter out files with invalid requires python version specifiers
pub fn try_from(
file: uv_pypi_types::File,
file: uv_pypi_types::PypiFile,
base: &SmallString,
) -> Result<Self, FileConversionError> {
Ok(Self {

View file

@ -9,18 +9,19 @@ use uv_small_str::SmallString;
use crate::lenient_requirement::LenientVersionSpecifiers;
/// A collection of "files" from `PyPI`'s JSON API for a single package.
/// A collection of "files" from `PyPI`'s JSON API for a single package, as served by the
/// `vnd.pypi.simple.v1` media type.
#[derive(Debug, Clone, Deserialize)]
pub struct SimpleJson {
/// The list of [`File`]s available for download sorted by filename.
pub struct PypiSimpleDetail {
/// The list of [`PypiFile`]s available for download sorted by filename.
#[serde(deserialize_with = "sorted_simple_json_files")]
pub files: Vec<File>,
pub files: Vec<PypiFile>,
}
/// Deserializes a sequence of "simple" files from `PyPI` and ensures that they
/// are sorted in a stable order.
fn sorted_simple_json_files<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<File>, D::Error> {
let mut files = <Vec<File>>::deserialize(d)?;
fn sorted_simple_json_files<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<PypiFile>, D::Error> {
let mut files = <Vec<PypiFile>>::deserialize(d)?;
// While it has not been positively observed, we sort the files
// to ensure we have a defined ordering. Otherwise, if we rely on
// the API to provide a stable ordering and doesn't, it can lead
@ -33,11 +34,12 @@ fn sorted_simple_json_files<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<File>
Ok(files)
}
/// A single (remote) file belonging to a package, either a wheel or a source distribution.
/// A single (remote) file belonging to a package, either a wheel or a source distribution, as
/// served by the `vnd.pypi.simple.v1` media type.
///
/// <https://peps.python.org/pep-0691/#project-detail>
#[derive(Debug, Clone)]
pub struct File {
pub struct PypiFile {
pub core_metadata: Option<CoreMetadata>,
pub filename: SmallString,
pub hashes: Hashes,
@ -48,7 +50,7 @@ pub struct File {
pub yanked: Option<Box<Yanked>>,
}
impl<'de> Deserialize<'de> for File {
impl<'de> Deserialize<'de> for PypiFile {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
@ -56,7 +58,7 @@ impl<'de> Deserialize<'de> for File {
struct FileVisitor;
impl<'de> serde::de::Visitor<'de> for FileVisitor {
type Value = File;
type Value = PypiFile;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a map containing file metadata")
@ -103,7 +105,7 @@ impl<'de> Deserialize<'de> for File {
}
}
Ok(File {
Ok(PypiFile {
core_metadata,
filename: filename
.ok_or_else(|| serde::de::Error::missing_field("filename"))?,