Move parsing http retries to EnvironmentOptions (#16284)

## Summary
- Move  parsing `UV_HTTP_RETRIES` to `EnvironmentOptions`

Relates https://github.com/astral-sh/uv/issues/14720

## Test Plan

- Tests with existing tests
This commit is contained in:
Andrei Berenda 2025-10-21 13:14:37 +04:00 committed by GitHub
parent 29cec24d5c
commit 51e8da2d1c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 123 additions and 91 deletions

2
Cargo.lock generated
View File

@ -5291,7 +5291,6 @@ dependencies = [
"predicates", "predicates",
"regex", "regex",
"reqwest", "reqwest",
"reqwest-retry",
"rkyv", "rkyv",
"rustc-hash", "rustc-hash",
"self-replace", "self-replace",
@ -6605,6 +6604,7 @@ dependencies = [
"tracing", "tracing",
"url", "url",
"uv-cache-info", "uv-cache-info",
"uv-client",
"uv-configuration", "uv-configuration",
"uv-dirs", "uv-dirs",
"uv-distribution-types", "uv-distribution-types",

View File

@ -44,7 +44,6 @@ use crate::middleware::OfflineMiddleware;
use crate::tls::read_identity; use crate::tls::read_identity;
use crate::{Connectivity, WrappedReqwestError}; use crate::{Connectivity, WrappedReqwestError};
/// 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.
@ -154,11 +153,13 @@ impl BaseClientBuilder<'_> {
allow_insecure_host: Vec<TrustedHost>, allow_insecure_host: Vec<TrustedHost>,
preview: Preview, preview: Preview,
timeout: Duration, timeout: Duration,
retries: u32,
) -> Self { ) -> Self {
Self { Self {
preview, preview,
allow_insecure_host, allow_insecure_host,
native_tls, native_tls,
retries,
connectivity, connectivity,
timeout, timeout,
..Self::default() ..Self::default()
@ -202,15 +203,6 @@ impl<'a> BaseClientBuilder<'a> {
self self
} }
/// 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.
pub fn retries_from_env(mut self) -> Result<Self, RetryParsingError> {
self.retries = retries_from_env()?;
Ok(self)
}
#[must_use] #[must_use]
pub fn native_tls(mut self, native_tls: bool) -> Self { pub fn native_tls(mut self, native_tls: bool) -> Self {
self.native_tls = native_tls; self.native_tls = native_tls;
@ -292,7 +284,7 @@ impl<'a> BaseClientBuilder<'a> {
} }
/// Create a [`RetryPolicy`] for the client. /// Create a [`RetryPolicy`] for the client.
fn retry_policy(&self) -> ExponentialBackoff { pub fn retry_policy(&self) -> ExponentialBackoff {
let mut builder = ExponentialBackoff::builder(); let mut builder = ExponentialBackoff::builder();
if env::var_os(EnvVars::UV_TEST_NO_HTTP_RETRY_DELAY).is_some() { if env::var_os(EnvVars::UV_TEST_NO_HTTP_RETRY_DELAY).is_some() {
builder = builder.retry_bounds(Duration::from_millis(0), Duration::from_millis(0)); builder = builder.retry_bounds(Duration::from_millis(0), Duration::from_millis(0));
@ -1093,19 +1085,6 @@ pub enum RetryParsingError {
ParseInt(#[from] ParseIntError), 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::*;

View File

@ -1,7 +1,7 @@
pub use base_client::{ pub use base_client::{
AuthIntegration, BaseClient, BaseClientBuilder, DEFAULT_RETRIES, ExtraMiddleware, AuthIntegration, BaseClient, BaseClientBuilder, DEFAULT_RETRIES, ExtraMiddleware,
RedirectClientWithMiddleware, RequestBuilder, RetryParsingError, UvRetryableStrategy, RedirectClientWithMiddleware, RequestBuilder, RetryParsingError, UvRetryableStrategy,
is_transient_network_error, retries_from_env, is_transient_network_error,
}; };
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};

View File

@ -27,7 +27,7 @@ use uv_auth::{Credentials, PyxTokenStore};
use uv_cache::{Cache, Refresh}; use uv_cache::{Cache, Refresh};
use uv_client::{ use uv_client::{
BaseClient, MetadataFormat, OwnedArchive, RegistryClientBuilder, RequestBuilder, BaseClient, MetadataFormat, OwnedArchive, RegistryClientBuilder, RequestBuilder,
RetryParsingError, UvRetryableStrategy, retries_from_env, RetryParsingError, UvRetryableStrategy,
}; };
use uv_configuration::{KeyringProviderType, TrustedPublishing}; use uv_configuration::{KeyringProviderType, TrustedPublishing};
use uv_distribution_filename::{DistFilename, SourceDistExtension, SourceDistFilename}; use uv_distribution_filename::{DistFilename, SourceDistExtension, SourceDistFilename};
@ -382,6 +382,7 @@ pub async fn upload(
filename: &DistFilename, filename: &DistFilename,
registry: &DisplaySafeUrl, registry: &DisplaySafeUrl,
client: &BaseClient, client: &BaseClient,
retry_policy: ExponentialBackoff,
credentials: &Credentials, credentials: &Credentials,
check_url_client: Option<&CheckUrlClient<'_>>, check_url_client: Option<&CheckUrlClient<'_>>,
download_concurrency: &Semaphore, download_concurrency: &Semaphore,
@ -389,8 +390,6 @@ pub async fn upload(
) -> Result<bool, PublishError> { ) -> Result<bool, PublishError> {
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.
let retry_policy = ExponentialBackoff::builder().build_with_max_retries(retries_from_env()?);
loop { loop {
let (request, idx) = build_upload_request( let (request, idx) = build_upload_request(
file, file,

View File

@ -5,11 +5,10 @@ use std::str::FromStr;
use indexmap::IndexMap; use indexmap::IndexMap;
use ref_cast::RefCast; use ref_cast::RefCast;
use reqwest_retry::policies::ExponentialBackoff;
use tracing::{debug, info}; use tracing::{debug, info};
use uv_cache::Cache; use uv_cache::Cache;
use uv_client::{BaseClientBuilder, retries_from_env}; use uv_client::BaseClientBuilder;
use uv_pep440::{Prerelease, Version}; use uv_pep440::{Prerelease, Version};
use uv_platform::{Arch, Libc, Os, Platform}; use uv_platform::{Arch, Libc, Os, Platform};
use uv_preview::Preview; use uv_preview::Preview;
@ -231,8 +230,7 @@ impl PythonInstallation {
// Python downloads are performing their own retries to catch stream errors, disable the // Python downloads are performing their own retries to catch stream errors, disable the
// default retries to avoid the middleware from performing uncontrolled retries. // default retries to avoid the middleware from performing uncontrolled retries.
let retry_policy = let retry_policy = client_builder.retry_policy();
ExponentialBackoff::builder().build_with_max_retries(retries_from_env()?);
let client = client_builder.clone().retries(0).build(); let client = client_builder.clone().retries(0).build();
info!("Fetching requested Python..."); info!("Fetching requested Python...");

View File

@ -17,6 +17,7 @@ workspace = true
[dependencies] [dependencies]
uv-cache-info = { workspace = true, features = ["schemars"] } uv-cache-info = { workspace = true, features = ["schemars"] }
uv-client = { workspace = true }
uv-configuration = { workspace = true, features = ["schemars", "clap"] } uv-configuration = { workspace = true, features = ["schemars", "clap"] }
uv-dirs = { workspace = true } uv-dirs = { workspace = true }
uv-distribution-types = { workspace = true, features = ["schemars"] } uv-distribution-types = { workspace = true, features = ["schemars"] }

View File

@ -585,6 +585,7 @@ pub struct EnvironmentOptions {
pub install_mirrors: PythonInstallMirrors, pub install_mirrors: PythonInstallMirrors,
pub log_context: Option<bool>, pub log_context: Option<bool>,
pub http_timeout: Duration, pub http_timeout: Duration,
pub http_retries: u32,
pub upload_http_timeout: Duration, pub upload_http_timeout: Duration,
pub concurrency: Concurrency, pub concurrency: Concurrency,
#[cfg(feature = "tracing-durations-export")] #[cfg(feature = "tracing-durations-export")]
@ -596,9 +597,11 @@ impl EnvironmentOptions {
pub fn new() -> Result<Self, Error> { pub fn new() -> Result<Self, Error> {
// Timeout options, matching https://doc.rust-lang.org/nightly/cargo/reference/config.html#httptimeout // Timeout options, matching https://doc.rust-lang.org/nightly/cargo/reference/config.html#httptimeout
// `UV_REQUEST_TIMEOUT` is provided for backwards compatibility with v0.1.6 // `UV_REQUEST_TIMEOUT` is provided for backwards compatibility with v0.1.6
let http_timeout = parse_u64_environment_variable(EnvVars::UV_HTTP_TIMEOUT)? let http_timeout = parse_integer_environment_variable(EnvVars::UV_HTTP_TIMEOUT)?
.or(parse_u64_environment_variable(EnvVars::UV_REQUEST_TIMEOUT)?) .or(parse_integer_environment_variable(
.or(parse_u64_environment_variable(EnvVars::HTTP_TIMEOUT)?) EnvVars::UV_REQUEST_TIMEOUT,
)?)
.or(parse_integer_environment_variable(EnvVars::HTTP_TIMEOUT)?)
.map(Duration::from_secs); .map(Duration::from_secs);
Ok(Self { Ok(Self {
@ -610,13 +613,9 @@ impl EnvironmentOptions {
EnvVars::UV_PYTHON_INSTALL_REGISTRY, EnvVars::UV_PYTHON_INSTALL_REGISTRY,
)?, )?,
concurrency: Concurrency { concurrency: Concurrency {
downloads: parse_non_zero_usize_environment_variable( downloads: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_DOWNLOADS)?,
EnvVars::UV_CONCURRENT_DOWNLOADS, builds: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_BUILDS)?,
)?, installs: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_INSTALLS)?,
builds: parse_non_zero_usize_environment_variable(EnvVars::UV_CONCURRENT_BUILDS)?,
installs: parse_non_zero_usize_environment_variable(
EnvVars::UV_CONCURRENT_INSTALLS,
)?,
}, },
install_mirrors: PythonInstallMirrors { install_mirrors: PythonInstallMirrors {
python_install_mirror: parse_string_environment_variable( python_install_mirror: parse_string_environment_variable(
@ -630,11 +629,15 @@ impl EnvironmentOptions {
)?, )?,
}, },
log_context: parse_boolish_environment_variable(EnvVars::UV_LOG_CONTEXT)?, log_context: parse_boolish_environment_variable(EnvVars::UV_LOG_CONTEXT)?,
upload_http_timeout: parse_u64_environment_variable(EnvVars::UV_UPLOAD_HTTP_TIMEOUT)? upload_http_timeout: parse_integer_environment_variable(
.map(Duration::from_secs) EnvVars::UV_UPLOAD_HTTP_TIMEOUT,
.or(http_timeout) )?
.unwrap_or(Duration::from_secs(15 * 60)), .map(Duration::from_secs)
.or(http_timeout)
.unwrap_or(Duration::from_secs(15 * 60)),
http_timeout: http_timeout.unwrap_or(Duration::from_secs(30)), http_timeout: http_timeout.unwrap_or(Duration::from_secs(30)),
http_retries: parse_integer_environment_variable(EnvVars::UV_HTTP_RETRIES)?
.unwrap_or(uv_client::DEFAULT_RETRIES),
#[cfg(feature = "tracing-durations-export")] #[cfg(feature = "tracing-durations-export")]
tracing_durations_file: parse_path_environment_variable( tracing_durations_file: parse_path_environment_variable(
EnvVars::TRACING_DURATIONS_FILE, EnvVars::TRACING_DURATIONS_FILE,
@ -716,10 +719,7 @@ fn parse_string_environment_variable(name: &'static str) -> Result<Option<String
} }
} }
fn parse_integer_environment_variable<T>( fn parse_integer_environment_variable<T>(name: &'static str) -> Result<Option<T>, Error>
name: &'static str,
err_msg: &'static str,
) -> Result<Option<T>, Error>
where where
T: std::str::FromStr + Copy, T: std::str::FromStr + Copy,
<T as std::str::FromStr>::Err: std::fmt::Display, <T as std::str::FromStr>::Err: std::fmt::Display,
@ -732,7 +732,7 @@ where
std::env::VarError::NotUnicode(err) => Err(Error::InvalidEnvironmentVariable { std::env::VarError::NotUnicode(err) => Err(Error::InvalidEnvironmentVariable {
name: name.to_string(), name: name.to_string(),
value: err.to_string_lossy().to_string(), value: err.to_string_lossy().to_string(),
err: err_msg.to_string(), err: "expected a valid UTF-8 string".to_string(),
}), }),
}; };
} }
@ -743,26 +743,14 @@ where
match value.parse::<T>() { match value.parse::<T>() {
Ok(v) => Ok(Some(v)), Ok(v) => Ok(Some(v)),
Err(_) => Err(Error::InvalidEnvironmentVariable { Err(err) => Err(Error::InvalidEnvironmentVariable {
name: name.to_string(), name: name.to_string(),
value, value,
err: err_msg.to_string(), err: err.to_string(),
}), }),
} }
} }
/// Parse a integer environment variable.
fn parse_u64_environment_variable(name: &'static str) -> Result<Option<u64>, Error> {
parse_integer_environment_variable(name, "expected an integer")
}
/// Parse a non-zero usize environment variable.
fn parse_non_zero_usize_environment_variable(
name: &'static str,
) -> Result<Option<NonZeroUsize>, Error> {
parse_integer_environment_variable(name, "expected a non-zero positive integer")
}
#[cfg(feature = "tracing-durations-export")] #[cfg(feature = "tracing-durations-export")]
/// Parse a path environment variable. /// Parse a path environment variable.
fn parse_path_environment_variable(name: &'static str) -> Option<PathBuf> { fn parse_path_environment_variable(name: &'static str) -> Option<PathBuf> {

View File

@ -91,7 +91,6 @@ owo-colors = { workspace = true }
petgraph = { workspace = true } petgraph = { workspace = true }
regex = { workspace = true } regex = { workspace = true }
reqwest = { workspace = true } reqwest = { workspace = true }
reqwest-retry = { workspace = true }
rkyv = { workspace = true } rkyv = { workspace = true }
rustc-hash = { workspace = true } rustc-hash = { workspace = true }
serde = { workspace = true } serde = { workspace = true }

View File

@ -44,6 +44,7 @@ pub(crate) async fn login(
network_settings.allow_insecure_host.clone(), network_settings.allow_insecure_host.clone(),
preview, preview,
network_settings.timeout, network_settings.timeout,
network_settings.retries,
) )
.auth_integration(AuthIntegration::NoAuthMiddleware) .auth_integration(AuthIntegration::NoAuthMiddleware)
.build(); .build();

View File

@ -104,6 +104,7 @@ async fn pyx_logout(
network_settings.allow_insecure_host.clone(), network_settings.allow_insecure_host.clone(),
preview, preview,
network_settings.timeout, network_settings.timeout,
network_settings.retries,
) )
.build(); .build();

View File

@ -33,6 +33,7 @@ pub(crate) async fn token(
network_settings.allow_insecure_host.clone(), network_settings.allow_insecure_host.clone(),
preview, preview,
network_settings.timeout, network_settings.timeout,
network_settings.retries,
) )
.auth_integration(AuthIntegration::NoAuthMiddleware) .auth_integration(AuthIntegration::NoAuthMiddleware)
.build(); .build();

View File

@ -2,12 +2,11 @@ use std::path::Path;
use std::str::FromStr; use std::str::FromStr;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use reqwest_retry::policies::ExponentialBackoff;
use tokio::process::Command; use tokio::process::Command;
use uv_bin_install::{Binary, bin_install}; use uv_bin_install::{Binary, bin_install};
use uv_cache::Cache; use uv_cache::Cache;
use uv_client::{BaseClientBuilder, retries_from_env}; use uv_client::BaseClientBuilder;
use uv_pep440::Version; use uv_pep440::Version;
use uv_preview::{Preview, PreviewFeatures}; use uv_preview::{Preview, PreviewFeatures};
use uv_warnings::warn_user; use uv_warnings::warn_user;
@ -64,9 +63,9 @@ pub(crate) async fn format(
// Parse version if provided // Parse version if provided
let version = version.as_deref().map(Version::from_str).transpose()?; let version = version.as_deref().map(Version::from_str).transpose()?;
let retry_policy = client_builder.retry_policy();
// Python downloads are performing their own retries to catch stream errors, disable the // Python downloads are performing their own retries to catch stream errors, disable the
// default retries to avoid the middleware from performing uncontrolled retries. // default retries to avoid the middleware from performing uncontrolled retries.
let retry_policy = ExponentialBackoff::builder().build_with_max_retries(retries_from_env()?);
let client = client_builder.retries(0).build(); let client = client_builder.retries(0).build();
// Get the path to Ruff, downloading it if necessary // Get the path to Ruff, downloading it if necessary

View File

@ -129,6 +129,7 @@ pub(crate) async fn publish(
.auth_integration(AuthIntegration::NoAuthMiddleware) .auth_integration(AuthIntegration::NoAuthMiddleware)
.wrap_existing(&upload_client); .wrap_existing(&upload_client);
let retry_policy = client_builder.retry_policy();
// We're only checking a single URL and one at a time, so 1 permit is sufficient // We're only checking a single URL and one at a time, so 1 permit is sufficient
let download_concurrency = Arc::new(Semaphore::new(1)); let download_concurrency = Arc::new(Semaphore::new(1));
@ -222,6 +223,7 @@ pub(crate) async fn publish(
&filename, &filename,
&publish_url, &publish_url,
&upload_client, &upload_client,
retry_policy,
&credentials, &credentials,
check_url_client.as_ref(), check_url_client.as_ref(),
&download_concurrency, &download_concurrency,

View File

@ -11,11 +11,10 @@ use futures::stream::FuturesUnordered;
use indexmap::IndexSet; use indexmap::IndexSet;
use itertools::{Either, Itertools}; use itertools::{Either, Itertools};
use owo_colors::{AnsiColors, OwoColorize}; use owo_colors::{AnsiColors, OwoColorize};
use reqwest_retry::policies::ExponentialBackoff;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use tracing::{debug, trace}; use tracing::{debug, trace};
use uv_client::{BaseClientBuilder, retries_from_env}; use uv_client::BaseClientBuilder;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_platform::{Arch, Libc}; use uv_platform::{Arch, Libc};
use uv_preview::{Preview, PreviewFeatures}; use uv_preview::{Preview, PreviewFeatures};
@ -398,9 +397,9 @@ pub(crate) async fn install(
.unique_by(|download| download.key()) .unique_by(|download| download.key())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let retry_policy = client_builder.retry_policy();
// Python downloads are performing their own retries to catch stream errors, disable the // Python downloads are performing their own retries to catch stream errors, disable the
// default retries to avoid the middleware from performing uncontrolled retries. // default retries to avoid the middleware from performing uncontrolled retries.
let retry_policy = ExponentialBackoff::builder().build_with_max_retries(retries_from_env()?);
let client = client_builder.retries(0).build(); let client = client_builder.retries(0).build();
let reporter = PythonDownloadReporter::new(printer, downloads.len() as u64); let reporter = PythonDownloadReporter::new(printer, downloads.len() as u64);

View File

@ -183,9 +183,9 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
settings.network_settings.native_tls, settings.network_settings.native_tls,
settings.network_settings.allow_insecure_host, settings.network_settings.allow_insecure_host,
settings.preview, settings.preview,
environment.http_timeout, settings.network_settings.timeout,
) settings.network_settings.retries,
.retries_from_env()?; );
Some( Some(
RunCommand::from_args(command, client_builder, *module, *script, *gui_script) RunCommand::from_args(command, client_builder, *module, *script, *gui_script)
.await?, .await?,
@ -456,9 +456,9 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
globals.network_settings.native_tls, globals.network_settings.native_tls,
globals.network_settings.allow_insecure_host.clone(), globals.network_settings.allow_insecure_host.clone(),
globals.preview, globals.preview,
environment.http_timeout, globals.network_settings.timeout,
) globals.network_settings.retries,
.retries_from_env()?; );
match *cli.command { match *cli.command {
Commands::Auth(AuthNamespace { Commands::Auth(AuthNamespace {

View File

@ -184,6 +184,7 @@ pub(crate) struct NetworkSettings {
pub(crate) native_tls: bool, pub(crate) native_tls: bool,
pub(crate) allow_insecure_host: Vec<TrustedHost>, pub(crate) allow_insecure_host: Vec<TrustedHost>,
pub(crate) timeout: Duration, pub(crate) timeout: Duration,
pub(crate) retries: u32,
} }
impl NetworkSettings { impl NetworkSettings {
@ -200,7 +201,6 @@ impl NetworkSettings {
} else { } else {
Connectivity::Online Connectivity::Online
}; };
let timeout = environment.http_timeout;
let native_tls = flag(args.native_tls, args.no_native_tls, "native-tls") let native_tls = flag(args.native_tls, args.no_native_tls, "native-tls")
.combine(workspace.and_then(|workspace| workspace.globals.native_tls)) .combine(workspace.and_then(|workspace| workspace.globals.native_tls))
.unwrap_or(false); .unwrap_or(false);
@ -225,7 +225,8 @@ impl NetworkSettings {
connectivity, connectivity,
native_tls, native_tls,
allow_insecure_host, allow_insecure_host,
timeout, timeout: environment.http_timeout,
retries: environment.http_retries,
} }
} }
} }

View File

@ -323,8 +323,21 @@ async fn install_http_retries() {
----- stdout ----- ----- stdout -----
----- stderr ----- ----- stderr -----
error: Failed to parse `UV_HTTP_RETRIES` error: Failed to parse environment variable `UV_HTTP_RETRIES` with invalid value `foo`: invalid digit found in string
Caused by: invalid digit found in string "
);
uv_snapshot!(context.filters(), context.pip_install()
.arg("anyio")
.arg("--index")
.arg(server.uri())
.env(EnvVars::UV_HTTP_RETRIES, "-1"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: Failed to parse environment variable `UV_HTTP_RETRIES` with invalid value `-1`: invalid digit found in string
" "
); );
@ -338,8 +351,7 @@ async fn install_http_retries() {
----- stdout ----- ----- stdout -----
----- stderr ----- ----- stderr -----
error: Failed to parse `UV_HTTP_RETRIES` error: Failed to parse environment variable `UV_HTTP_RETRIES` with invalid value `999999999999`: number too large to fit in target type
Caused by: number too large to fit in target type
" "
); );

View File

@ -67,6 +67,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -267,6 +268,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -468,6 +470,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -701,6 +704,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -903,6 +907,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -1081,6 +1086,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -1308,6 +1314,7 @@ fn resolve_index_url() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -1543,6 +1550,7 @@ fn resolve_index_url() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -1836,6 +1844,7 @@ fn resolve_find_links() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -2060,6 +2069,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -2243,6 +2253,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -2476,6 +2487,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -2732,6 +2744,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -2905,6 +2918,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -3078,6 +3092,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -3253,6 +3268,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -3447,6 +3463,7 @@ fn resolve_tool() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -3633,6 +3650,7 @@ fn resolve_poetry_toml() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -3840,6 +3858,7 @@ fn resolve_both() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -4086,6 +4105,7 @@ fn resolve_both_special_fields() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -4411,6 +4431,7 @@ fn resolve_config_file() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -4711,6 +4732,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -4887,6 +4909,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -5082,6 +5105,7 @@ fn allow_insecure_host() -> anyhow::Result<()> {
}, },
], ],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -5269,6 +5293,7 @@ fn index_priority() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -5504,6 +5529,7 @@ fn index_priority() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -5745,6 +5771,7 @@ fn index_priority() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -5981,6 +6008,7 @@ fn index_priority() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -6224,6 +6252,7 @@ fn index_priority() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -6460,6 +6489,7 @@ fn index_priority() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -6709,6 +6739,7 @@ fn verify_hashes() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -6875,6 +6906,7 @@ fn verify_hashes() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -7039,6 +7071,7 @@ fn verify_hashes() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -7205,6 +7238,7 @@ fn verify_hashes() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -7369,6 +7403,7 @@ fn verify_hashes() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -7534,6 +7569,7 @@ fn verify_hashes() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -7714,6 +7750,7 @@ fn preview_features() {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -7827,6 +7864,7 @@ fn preview_features() {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -7940,6 +7978,7 @@ fn preview_features() {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -8053,6 +8092,7 @@ fn preview_features() {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -8166,6 +8206,7 @@ fn preview_features() {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -8281,6 +8322,7 @@ fn preview_features() {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -8415,6 +8457,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -8589,6 +8632,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -8786,6 +8830,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -8958,6 +9003,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -9124,6 +9170,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -9291,6 +9338,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -9523,6 +9571,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -9641,6 +9690,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -9782,6 +9832,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -9898,6 +9949,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -10004,6 +10056,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -10111,6 +10164,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -10282,6 +10336,7 @@ fn build_isolation_override() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,
@ -10451,6 +10506,7 @@ fn build_isolation_override() -> anyhow::Result<()> {
native_tls: false, native_tls: false,
allow_insecure_host: [], allow_insecure_host: [],
timeout: [TIME], timeout: [TIME],
retries: 3,
}, },
concurrency: Concurrency { concurrency: Concurrency {
downloads: 50, downloads: 50,

View File

@ -885,8 +885,6 @@ fn seed_older_python_version() {
#[test] #[test]
fn create_venv_with_invalid_http_timeout() { fn create_venv_with_invalid_http_timeout() {
let context = TestContext::new_with_versions(&["3.12"]).with_http_timeout("not_a_number"); let context = TestContext::new_with_versions(&["3.12"]).with_http_timeout("not_a_number");
// Create a virtual environment at `.venv`.
uv_snapshot!(context.filters(), context.venv() uv_snapshot!(context.filters(), context.venv()
.arg(context.venv.as_os_str()) .arg(context.venv.as_os_str())
.arg("--python") .arg("--python")
@ -896,15 +894,13 @@ fn create_venv_with_invalid_http_timeout() {
----- stdout ----- ----- stdout -----
----- stderr ----- ----- stderr -----
error: Failed to parse environment variable `UV_HTTP_TIMEOUT` with invalid value `not_a_number`: expected an integer error: Failed to parse environment variable `UV_HTTP_TIMEOUT` with invalid value `not_a_number`: invalid digit found in string
"###); "###);
} }
#[test] #[test]
fn create_venv_with_invalid_concurrent_installs() { fn create_venv_with_invalid_concurrent_installs() {
let context = TestContext::new_with_versions(&["3.12"]).with_concurrent_installs("0"); let context = TestContext::new_with_versions(&["3.12"]).with_concurrent_installs("0");
// Create a virtual environment at `.venv`.
uv_snapshot!(context.filters(), context.venv() uv_snapshot!(context.filters(), context.venv()
.arg(context.venv.as_os_str()) .arg(context.venv.as_os_str())
.arg("--python") .arg("--python")
@ -914,7 +910,7 @@ fn create_venv_with_invalid_concurrent_installs() {
----- stdout ----- ----- stdout -----
----- stderr ----- ----- stderr -----
error: Failed to parse environment variable `UV_CONCURRENT_INSTALLS` with invalid value `0`: expected a non-zero positive integer error: Failed to parse environment variable `UV_CONCURRENT_INSTALLS` with invalid value `0`: number would be zero for non-zero type
"###); "###);
} }