Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Skip existing, second iteration: Check the index before uploading #8531

Merged
merged 5 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4819,6 +4819,22 @@ pub struct PublishArgs {
value_parser = parse_insecure_host,
)]
pub allow_insecure_host: Option<Vec<Maybe<TrustedHost>>>,

/// Check an index URL for existing files to skip duplicate uploads.
///
/// This option allows retrying publishing that failed after only some, but not all files have
/// been uploaded, and handles error due to parallel uploads of the same file.
///
/// Before uploading, the index is checked. If the exact same file already exists in the index,
/// the file will not be uploaded. If an error occurred during the upload, the index is checked
/// again, to handle cases where the identical file was uploaded twice in parallel.
///
/// The exact behavior will vary based on the index. When uploading to PyPI, uploading the same
/// file succeeds even without `--check-url`, while most other indexes error.
///
/// The index must provide one of the supported hashes (SHA-256, SHA-384, or SHA-512).
#[arg(long,env = EnvVars::UV_PUBLISH_CHECK_URL)]
pub check_url: Option<IndexUrl>,
}

/// See [PEP 517](https://peps.python.org/pep-0517/) and
Expand Down
30 changes: 28 additions & 2 deletions crates/uv-client/src/registry_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::time::Duration;
use tracing::{info_span, instrument, trace, warn, Instrument};
use url::Url;

use uv_cache::{Cache, CacheBucket, CacheEntry, WheelCache};
use uv_cache::{Cache, CacheBucket, CacheEntry, Refresh, WheelCache};
use uv_configuration::KeyringProviderType;
use uv_configuration::{IndexStrategy, TrustedHost};
use uv_distribution_filename::{DistFilename, SourceDistFilename, WheelFilename};
Expand All @@ -31,7 +31,7 @@ use crate::cached_client::CacheControl;
use crate::html::SimpleHtml;
use crate::remote_metadata::wheel_metadata_from_remote_zip;
use crate::rkyvutil::OwnedArchive;
use crate::{CachedClient, CachedClientError, Error, ErrorKind};
use crate::{BaseClient, CachedClient, CachedClientError, Error, ErrorKind};

/// A builder for an [`RegistryClient`].
#[derive(Debug, Clone)]
Expand Down Expand Up @@ -143,6 +143,27 @@ impl<'a> RegistryClientBuilder<'a> {
timeout,
}
}

/// Share the underlying client between two different middleware configurations.
pub fn wrap_existing(self, existing: &BaseClient) -> RegistryClient {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid the cost for creating another client.

// Wrap in any relevant middleware and handle connectivity.
let client = self.base_client_builder.wrap_existing(existing);

let timeout = client.timeout();
let connectivity = client.connectivity();

// Wrap in the cache middleware.
let client = CachedClient::new(client);

RegistryClient {
index_urls: self.index_urls,
index_strategy: self.index_strategy,
cache: self.cache,
connectivity,
client,
timeout,
}
}
}

impl<'a> TryFrom<BaseClientBuilder<'a>> for RegistryClientBuilder<'a> {
Expand Down Expand Up @@ -266,6 +287,11 @@ impl RegistryClient {
Ok(results)
}

/// Invalidate the cache after the index changed.
pub fn refresh(&mut self, refresh: Refresh) {
konstin marked this conversation as resolved.
Show resolved Hide resolved
self.cache = self.cache.clone().with_refresh(refresh);
}

/// Fetch the [`SimpleMetadata`] from a single index for a given package.
///
/// The index can either be a PEP 503-compatible remote repository, or a local directory laid
Expand Down
9 changes: 0 additions & 9 deletions crates/uv-extract/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,6 @@ impl Hasher {
Hasher::Sha512(hasher) => hasher.update(data),
}
}

pub fn finalize(self) -> Vec<u8> {
match self {
Hasher::Md5(hasher) => hasher.finalize().to_vec(),
Hasher::Sha256(hasher) => hasher.finalize().to_vec(),
Hasher::Sha384(hasher) => hasher.finalize().to_vec(),
Hasher::Sha512(hasher) => hasher.finalize().to_vec(),
}
}
}

impl From<HashAlgorithm> for Hasher {
Expand Down
4 changes: 3 additions & 1 deletion crates/uv-publish/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ license.workspace = true
doctest = false

[dependencies]
uv-cache = { workspace = true }
uv-client = { workspace = true }
uv-configuration = { workspace = true }
uv-distribution-filename = { workspace = true }
uv-distribution-types = { workspace = true }
uv-extract = { workspace = true }
uv-fs = { workspace = true }
uv-metadata = { workspace = true }
uv-pypi-types = { workspace = true }
Expand All @@ -35,7 +38,6 @@ reqwest-retry = { workspace = true }
rustc-hash = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
sha2 = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
tokio-util = { workspace = true , features = ["io"] }
Expand Down
Loading
Loading