mirror of https://github.com/astral-sh/uv
Respect `UV_HTTP_RETRIES` in `uv publish` (#15106)
Previously, publish would always use the default retries, now it respects `UV_HTTP_RETRIES` Some awkward error handling to avoid pulling anyhow into uv-publish.
This commit is contained in:
parent
aa758ae402
commit
59558b13c1
|
|
@ -1,12 +1,12 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
use std::num::ParseIntError;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{env, io, iter};
|
use std::{env, io, iter};
|
||||||
|
|
||||||
use anyhow::Context;
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use http::{
|
use http::{
|
||||||
HeaderMap, HeaderName, HeaderValue, Method, StatusCode,
|
HeaderMap, HeaderName, HeaderValue, Method, StatusCode,
|
||||||
|
|
@ -22,6 +22,7 @@ use reqwest_retry::policies::ExponentialBackoff;
|
||||||
use reqwest_retry::{
|
use reqwest_retry::{
|
||||||
DefaultRetryableStrategy, RetryTransientMiddleware, Retryable, RetryableStrategy,
|
DefaultRetryableStrategy, RetryTransientMiddleware, Retryable, RetryableStrategy,
|
||||||
};
|
};
|
||||||
|
use thiserror::Error;
|
||||||
use tracing::{debug, trace};
|
use tracing::{debug, trace};
|
||||||
use url::ParseError;
|
use url::ParseError;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
@ -42,7 +43,9 @@ use crate::linehaul::LineHaul;
|
||||||
use crate::middleware::OfflineMiddleware;
|
use crate::middleware::OfflineMiddleware;
|
||||||
use crate::tls::read_identity;
|
use crate::tls::read_identity;
|
||||||
|
|
||||||
|
/// Do not use this value directly outside tests, use [`retries_from_env`] instead.
|
||||||
pub const DEFAULT_RETRIES: u32 = 3;
|
pub const DEFAULT_RETRIES: u32 = 3;
|
||||||
|
|
||||||
/// Maximum number of redirects to follow before giving up.
|
/// Maximum number of redirects to follow before giving up.
|
||||||
///
|
///
|
||||||
/// This is the default used by [`reqwest`].
|
/// This is the default used by [`reqwest`].
|
||||||
|
|
@ -169,23 +172,13 @@ impl<'a> BaseClientBuilder<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the retry count from [`EnvVars::UV_HTTP_RETRIES`] if set, otherwise, make no change.
|
/// Read the retry count from [`EnvVars::UV_HTTP_RETRIES`] if set, otherwise use the default
|
||||||
|
/// retries.
|
||||||
///
|
///
|
||||||
/// Errors when [`EnvVars::UV_HTTP_RETRIES`] is not a valid u32.
|
/// Errors when [`EnvVars::UV_HTTP_RETRIES`] is not a valid u32.
|
||||||
pub fn retries_from_env(self) -> anyhow::Result<Self> {
|
pub fn retries_from_env(mut self) -> Result<Self, RetryParsingError> {
|
||||||
// TODO(zanieb): We should probably parse this in another layer, but there's not a natural
|
self.retries = retries_from_env()?;
|
||||||
// fit for it right now
|
Ok(self)
|
||||||
if let Some(value) = env::var_os(EnvVars::UV_HTTP_RETRIES) {
|
|
||||||
Ok(self.retries(
|
|
||||||
value
|
|
||||||
.to_string_lossy()
|
|
||||||
.as_ref()
|
|
||||||
.parse::<u32>()
|
|
||||||
.context("Failed to parse `UV_HTTP_RETRIES`")?,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|
@ -982,6 +975,26 @@ fn find_sources<E: Error + 'static>(orig: &dyn Error) -> impl Iterator<Item = &E
|
||||||
iter::successors(find_source::<E>(orig), |&err| find_source(err))
|
iter::successors(find_source::<E>(orig), |&err| find_source(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(konsti): Remove once we find a native home for `retries_from_env`
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum RetryParsingError {
|
||||||
|
#[error("Failed to parse `UV_HTTP_RETRIES`")]
|
||||||
|
ParseInt(#[from] ParseIntError),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read the retry count from [`EnvVars::UV_HTTP_RETRIES`] if set, otherwise, make no change.
|
||||||
|
///
|
||||||
|
/// Errors when [`EnvVars::UV_HTTP_RETRIES`] is not a valid u32.
|
||||||
|
pub fn retries_from_env() -> Result<u32, RetryParsingError> {
|
||||||
|
// TODO(zanieb): We should probably parse this in another layer, but there's not a natural
|
||||||
|
// fit for it right now
|
||||||
|
if let Some(value) = env::var_os(EnvVars::UV_HTTP_RETRIES) {
|
||||||
|
Ok(value.to_string_lossy().as_ref().parse::<u32>()?)
|
||||||
|
} else {
|
||||||
|
Ok(DEFAULT_RETRIES)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
pub use base_client::{
|
pub use base_client::{
|
||||||
AuthIntegration, BaseClient, BaseClientBuilder, DEFAULT_RETRIES, ExtraMiddleware,
|
AuthIntegration, BaseClient, BaseClientBuilder, DEFAULT_RETRIES, ExtraMiddleware,
|
||||||
RedirectClientWithMiddleware, RequestBuilder, UvRetryableStrategy, is_extended_transient_error,
|
RedirectClientWithMiddleware, RequestBuilder, RetryParsingError, UvRetryableStrategy,
|
||||||
|
is_extended_transient_error, retries_from_env,
|
||||||
};
|
};
|
||||||
pub use cached_client::{CacheControl, CachedClient, CachedClientError, DataWithCachePolicy};
|
pub use cached_client::{CacheControl, CachedClient, CachedClientError, DataWithCachePolicy};
|
||||||
pub use error::{Error, ErrorKind, WrappedReqwestError};
|
pub use error::{Error, ErrorKind, WrappedReqwestError};
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ use url::Url;
|
||||||
use uv_auth::Credentials;
|
use uv_auth::Credentials;
|
||||||
use uv_cache::{Cache, Refresh};
|
use uv_cache::{Cache, Refresh};
|
||||||
use uv_client::{
|
use uv_client::{
|
||||||
BaseClient, DEFAULT_RETRIES, MetadataFormat, OwnedArchive, RegistryClientBuilder,
|
BaseClient, MetadataFormat, OwnedArchive, RegistryClientBuilder, RequestBuilder,
|
||||||
RequestBuilder, UvRetryableStrategy,
|
RetryParsingError, UvRetryableStrategy, retries_from_env,
|
||||||
};
|
};
|
||||||
use uv_configuration::{KeyringProviderType, TrustedPublishing};
|
use uv_configuration::{KeyringProviderType, TrustedPublishing};
|
||||||
use uv_distribution_filename::{DistFilename, SourceDistExtension, SourceDistFilename};
|
use uv_distribution_filename::{DistFilename, SourceDistExtension, SourceDistFilename};
|
||||||
|
|
@ -78,6 +78,8 @@ pub enum PublishError {
|
||||||
},
|
},
|
||||||
#[error("Hash is missing in index for {0}")]
|
#[error("Hash is missing in index for {0}")]
|
||||||
MissingHash(Box<DistFilename>),
|
MissingHash(Box<DistFilename>),
|
||||||
|
#[error(transparent)]
|
||||||
|
RetryParsing(#[from] RetryParsingError),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Failure to get the metadata for a specific file.
|
/// Failure to get the metadata for a specific file.
|
||||||
|
|
@ -397,7 +399,7 @@ pub async fn upload(
|
||||||
let mut n_past_retries = 0;
|
let mut n_past_retries = 0;
|
||||||
let start_time = SystemTime::now();
|
let start_time = SystemTime::now();
|
||||||
// N.B. We cannot use the client policy here because it is set to zero retries
|
// N.B. We cannot use the client policy here because it is set to zero retries
|
||||||
let retry_policy = ExponentialBackoff::builder().build_with_max_retries(DEFAULT_RETRIES);
|
let retry_policy = ExponentialBackoff::builder().build_with_max_retries(retries_from_env()?);
|
||||||
loop {
|
loop {
|
||||||
let (request, idx) = build_request(
|
let (request, idx) = build_request(
|
||||||
file,
|
file,
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,9 @@ pub(crate) enum ProjectError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
RetryParsing(#[from] uv_client::RetryParsingError),
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Anyhow(#[from] anyhow::Error),
|
Anyhow(#[from] anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
@ -1708,7 +1711,7 @@ pub(crate) async fn resolve_names(
|
||||||
|
|
||||||
let client_builder = BaseClientBuilder::new()
|
let client_builder = BaseClientBuilder::new()
|
||||||
.retries_from_env()
|
.retries_from_env()
|
||||||
.map_err(uv_requirements::Error::ClientError)?
|
.map_err(|err| uv_requirements::Error::ClientError(err.into()))?
|
||||||
.connectivity(network_settings.connectivity)
|
.connectivity(network_settings.connectivity)
|
||||||
.native_tls(network_settings.native_tls)
|
.native_tls(network_settings.native_tls)
|
||||||
.keyring(*keyring_provider)
|
.keyring(*keyring_provider)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue