Use a dedicated type for form metadata (#14175)
Some checks are pending
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / smoke test | linux (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / mkdocs (push) Waiting to run
CI / build binary | linux libc (push) Blocked by required conditions
CI / build binary | linux musl (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows x86_64 (push) Blocked by required conditions
CI / build binary | windows aarch64 (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / ecosystem test | pydantic/pydantic-core (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / smoke test | macos (push) Blocked by required conditions
CI / smoke test | windows x86_64 (push) Blocked by required conditions
CI / smoke test | windows aarch64 (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | deadsnakes python3.9 on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | pyodide on ubuntu (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | free-threaded python on github actions (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | registries (push) Blocked by required conditions
CI / integration test | uv_build (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions

This commit is contained in:
Charlie Marsh 2025-06-20 20:33:29 -04:00 committed by GitHub
parent e59835d50c
commit 0fef253c4b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -390,7 +390,7 @@ pub async fn upload(
download_concurrency: &Semaphore,
reporter: Arc<impl Reporter>,
) -> Result<bool, PublishError> {
let form_metadata = form_metadata(file, filename)
let form_metadata = FormMetadata::read_from_file(file, filename)
.await
.map_err(|err| PublishError::PublishPrepare(file.to_path_buf(), Box::new(err)))?;
@ -644,13 +644,17 @@ async fn metadata(file: &Path, filename: &DistFilename) -> Result<Metadata23, Pu
Ok(Metadata23::parse(&contents)?)
}
/// Collect the non-file fields for the multipart request from the package METADATA.
///
/// Reference implementation: <https://github.com/pypi/warehouse/blob/d2c36d992cf9168e0518201d998b2707a3ef1e72/warehouse/forklift/legacy.py#L1376-L1430>
async fn form_metadata(
#[derive(Debug, Clone)]
struct FormMetadata(Vec<(&'static str, String)>);
impl FormMetadata {
/// Collect the non-file fields for the multipart request from the package METADATA.
///
/// Reference implementation: <https://github.com/pypi/warehouse/blob/d2c36d992cf9168e0518201d998b2707a3ef1e72/warehouse/forklift/legacy.py#L1376-L1430>
async fn read_from_file(
file: &Path,
filename: &DistFilename,
) -> Result<Vec<(&'static str, String)>, PublishPrepareError> {
) -> Result<Self, PublishPrepareError> {
let hash_hex = hash_file(file, Hasher::from(HashAlgorithm::Sha256)).await?;
let Metadata23 {
@ -745,7 +749,13 @@ async fn form_metadata(
add_vec("requires_dist", requires_dist);
add_vec("requires_external", requires_external);
Ok(form_metadata)
Ok(Self(form_metadata))
}
/// Returns an iterator over the metadata fields.
fn iter(&self) -> std::slice::Iter<'_, (&'static str, String)> {
self.0.iter()
}
}
/// Build the upload request.
@ -758,11 +768,11 @@ async fn build_request<'a>(
registry: &DisplaySafeUrl,
client: &'a BaseClient,
credentials: &Credentials,
form_metadata: &[(&'static str, String)],
form_metadata: &FormMetadata,
reporter: Arc<impl Reporter>,
) -> Result<(RequestBuilder<'a>, usize), PublishPrepareError> {
let mut form = reqwest::multipart::Form::new();
for (key, value) in form_metadata {
for (key, value) in form_metadata.iter() {
form = form.text(*key, value.clone());
}
@ -888,16 +898,19 @@ async fn handle_response(registry: &Url, response: Response) -> Result<(), Publi
#[cfg(test)]
mod tests {
use crate::{Reporter, build_request, form_metadata};
use insta::{assert_debug_snapshot, assert_snapshot};
use itertools::Itertools;
use std::path::PathBuf;
use std::sync::Arc;
use insta::{assert_debug_snapshot, assert_snapshot};
use itertools::Itertools;
use uv_auth::Credentials;
use uv_client::BaseClientBuilder;
use uv_distribution_filename::DistFilename;
use uv_redacted::DisplaySafeUrl;
use crate::{FormMetadata, Reporter, build_request};
struct DummyReporter;
impl Reporter for DummyReporter {
@ -916,7 +929,9 @@ mod tests {
let file = PathBuf::from("../../scripts/links/").join(raw_filename);
let filename = DistFilename::try_from_normalized_filename(raw_filename).unwrap();
let form_metadata = form_metadata(&file, &filename).await.unwrap();
let form_metadata = FormMetadata::read_from_file(&file, &filename)
.await
.unwrap();
let formatted_metadata = form_metadata
.iter()
@ -1028,7 +1043,9 @@ mod tests {
let file = PathBuf::from("../../scripts/links/").join(raw_filename);
let filename = DistFilename::try_from_normalized_filename(raw_filename).unwrap();
let form_metadata = form_metadata(&file, &filename).await.unwrap();
let form_metadata = FormMetadata::read_from_file(&file, &filename)
.await
.unwrap();
let formatted_metadata = form_metadata
.iter()