diff --git a/Cargo.lock b/Cargo.lock index 84b8b93ca..11bcb924a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5291,7 +5291,6 @@ dependencies = [ "predicates", "regex", "reqwest", - "reqwest-retry", "rkyv", "rustc-hash", "self-replace", @@ -6605,6 +6604,7 @@ dependencies = [ "tracing", "url", "uv-cache-info", + "uv-client", "uv-configuration", "uv-dirs", "uv-distribution-types", diff --git a/crates/uv-client/src/base_client.rs b/crates/uv-client/src/base_client.rs index bf662e295..0c897957d 100644 --- a/crates/uv-client/src/base_client.rs +++ b/crates/uv-client/src/base_client.rs @@ -44,7 +44,6 @@ use crate::middleware::OfflineMiddleware; use crate::tls::read_identity; use crate::{Connectivity, WrappedReqwestError}; -/// Do not use this value directly outside tests, use [`retries_from_env`] instead. pub const DEFAULT_RETRIES: u32 = 3; /// Maximum number of redirects to follow before giving up. @@ -154,11 +153,13 @@ impl BaseClientBuilder<'_> { allow_insecure_host: Vec, preview: Preview, timeout: Duration, + retries: u32, ) -> Self { Self { preview, allow_insecure_host, native_tls, + retries, connectivity, timeout, ..Self::default() @@ -202,15 +203,6 @@ impl<'a> BaseClientBuilder<'a> { 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.retries = retries_from_env()?; - Ok(self) - } - #[must_use] pub fn native_tls(mut self, native_tls: bool) -> Self { self.native_tls = native_tls; @@ -292,7 +284,7 @@ impl<'a> BaseClientBuilder<'a> { } /// Create a [`RetryPolicy`] for the client. - fn retry_policy(&self) -> ExponentialBackoff { + pub fn retry_policy(&self) -> ExponentialBackoff { let mut builder = ExponentialBackoff::builder(); 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)); @@ -1093,19 +1085,6 @@ pub enum RetryParsingError { 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 { - // 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::()?) - } else { - Ok(DEFAULT_RETRIES) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/uv-client/src/lib.rs b/crates/uv-client/src/lib.rs index 44e333efc..a82f1b381 100644 --- a/crates/uv-client/src/lib.rs +++ b/crates/uv-client/src/lib.rs @@ -1,7 +1,7 @@ pub use base_client::{ AuthIntegration, BaseClient, BaseClientBuilder, DEFAULT_RETRIES, ExtraMiddleware, 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 error::{Error, ErrorKind, WrappedReqwestError}; diff --git a/crates/uv-publish/src/lib.rs b/crates/uv-publish/src/lib.rs index ea2e1c095..65c2ca602 100644 --- a/crates/uv-publish/src/lib.rs +++ b/crates/uv-publish/src/lib.rs @@ -27,7 +27,7 @@ use uv_auth::{Credentials, PyxTokenStore}; use uv_cache::{Cache, Refresh}; use uv_client::{ BaseClient, MetadataFormat, OwnedArchive, RegistryClientBuilder, RequestBuilder, - RetryParsingError, UvRetryableStrategy, retries_from_env, + RetryParsingError, UvRetryableStrategy, }; use uv_configuration::{KeyringProviderType, TrustedPublishing}; use uv_distribution_filename::{DistFilename, SourceDistExtension, SourceDistFilename}; @@ -382,6 +382,7 @@ pub async fn upload( filename: &DistFilename, registry: &DisplaySafeUrl, client: &BaseClient, + retry_policy: ExponentialBackoff, credentials: &Credentials, check_url_client: Option<&CheckUrlClient<'_>>, download_concurrency: &Semaphore, @@ -389,8 +390,6 @@ pub async fn upload( ) -> Result { let mut n_past_retries = 0; 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 { let (request, idx) = build_upload_request( file, diff --git a/crates/uv-python/src/installation.rs b/crates/uv-python/src/installation.rs index 6097f9618..6422d16f6 100644 --- a/crates/uv-python/src/installation.rs +++ b/crates/uv-python/src/installation.rs @@ -5,11 +5,10 @@ use std::str::FromStr; use indexmap::IndexMap; use ref_cast::RefCast; -use reqwest_retry::policies::ExponentialBackoff; use tracing::{debug, info}; use uv_cache::Cache; -use uv_client::{BaseClientBuilder, retries_from_env}; +use uv_client::BaseClientBuilder; use uv_pep440::{Prerelease, Version}; use uv_platform::{Arch, Libc, Os, Platform}; use uv_preview::Preview; @@ -231,8 +230,7 @@ impl PythonInstallation { // Python downloads are performing their own retries to catch stream errors, disable the // default retries to avoid the middleware from performing uncontrolled retries. - let retry_policy = - ExponentialBackoff::builder().build_with_max_retries(retries_from_env()?); + let retry_policy = client_builder.retry_policy(); let client = client_builder.clone().retries(0).build(); info!("Fetching requested Python..."); diff --git a/crates/uv-settings/Cargo.toml b/crates/uv-settings/Cargo.toml index 0bb003750..fe98506af 100644 --- a/crates/uv-settings/Cargo.toml +++ b/crates/uv-settings/Cargo.toml @@ -17,6 +17,7 @@ workspace = true [dependencies] uv-cache-info = { workspace = true, features = ["schemars"] } +uv-client = { workspace = true } uv-configuration = { workspace = true, features = ["schemars", "clap"] } uv-dirs = { workspace = true } uv-distribution-types = { workspace = true, features = ["schemars"] } diff --git a/crates/uv-settings/src/lib.rs b/crates/uv-settings/src/lib.rs index a8937f0da..c9fcc16b7 100644 --- a/crates/uv-settings/src/lib.rs +++ b/crates/uv-settings/src/lib.rs @@ -585,6 +585,7 @@ pub struct EnvironmentOptions { pub install_mirrors: PythonInstallMirrors, pub log_context: Option, pub http_timeout: Duration, + pub http_retries: u32, pub upload_http_timeout: Duration, pub concurrency: Concurrency, #[cfg(feature = "tracing-durations-export")] @@ -596,9 +597,11 @@ impl EnvironmentOptions { pub fn new() -> Result { // 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 - let http_timeout = parse_u64_environment_variable(EnvVars::UV_HTTP_TIMEOUT)? - .or(parse_u64_environment_variable(EnvVars::UV_REQUEST_TIMEOUT)?) - .or(parse_u64_environment_variable(EnvVars::HTTP_TIMEOUT)?) + let http_timeout = parse_integer_environment_variable(EnvVars::UV_HTTP_TIMEOUT)? + .or(parse_integer_environment_variable( + EnvVars::UV_REQUEST_TIMEOUT, + )?) + .or(parse_integer_environment_variable(EnvVars::HTTP_TIMEOUT)?) .map(Duration::from_secs); Ok(Self { @@ -610,13 +613,9 @@ impl EnvironmentOptions { EnvVars::UV_PYTHON_INSTALL_REGISTRY, )?, concurrency: Concurrency { - downloads: parse_non_zero_usize_environment_variable( - EnvVars::UV_CONCURRENT_DOWNLOADS, - )?, - builds: parse_non_zero_usize_environment_variable(EnvVars::UV_CONCURRENT_BUILDS)?, - installs: parse_non_zero_usize_environment_variable( - EnvVars::UV_CONCURRENT_INSTALLS, - )?, + downloads: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_DOWNLOADS)?, + builds: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_BUILDS)?, + installs: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_INSTALLS)?, }, install_mirrors: PythonInstallMirrors { python_install_mirror: parse_string_environment_variable( @@ -630,11 +629,15 @@ impl EnvironmentOptions { )?, }, log_context: parse_boolish_environment_variable(EnvVars::UV_LOG_CONTEXT)?, - upload_http_timeout: parse_u64_environment_variable(EnvVars::UV_UPLOAD_HTTP_TIMEOUT)? - .map(Duration::from_secs) - .or(http_timeout) - .unwrap_or(Duration::from_secs(15 * 60)), + upload_http_timeout: parse_integer_environment_variable( + EnvVars::UV_UPLOAD_HTTP_TIMEOUT, + )? + .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_retries: parse_integer_environment_variable(EnvVars::UV_HTTP_RETRIES)? + .unwrap_or(uv_client::DEFAULT_RETRIES), #[cfg(feature = "tracing-durations-export")] tracing_durations_file: parse_path_environment_variable( EnvVars::TRACING_DURATIONS_FILE, @@ -716,10 +719,7 @@ fn parse_string_environment_variable(name: &'static str) -> Result( - name: &'static str, - err_msg: &'static str, -) -> Result, Error> +fn parse_integer_environment_variable(name: &'static str) -> Result, Error> where T: std::str::FromStr + Copy, ::Err: std::fmt::Display, @@ -732,7 +732,7 @@ where std::env::VarError::NotUnicode(err) => Err(Error::InvalidEnvironmentVariable { name: name.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::() { Ok(v) => Ok(Some(v)), - Err(_) => Err(Error::InvalidEnvironmentVariable { + Err(err) => Err(Error::InvalidEnvironmentVariable { name: name.to_string(), value, - err: err_msg.to_string(), + err: err.to_string(), }), } } -/// Parse a integer environment variable. -fn parse_u64_environment_variable(name: &'static str) -> Result, 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, Error> { - parse_integer_environment_variable(name, "expected a non-zero positive integer") -} - #[cfg(feature = "tracing-durations-export")] /// Parse a path environment variable. fn parse_path_environment_variable(name: &'static str) -> Option { diff --git a/crates/uv/Cargo.toml b/crates/uv/Cargo.toml index bdf7a1ae5..c8a5ef7eb 100644 --- a/crates/uv/Cargo.toml +++ b/crates/uv/Cargo.toml @@ -91,7 +91,6 @@ owo-colors = { workspace = true } petgraph = { workspace = true } regex = { workspace = true } reqwest = { workspace = true } -reqwest-retry = { workspace = true } rkyv = { workspace = true } rustc-hash = { workspace = true } serde = { workspace = true } diff --git a/crates/uv/src/commands/auth/login.rs b/crates/uv/src/commands/auth/login.rs index 57039b9fc..c0d937e7c 100644 --- a/crates/uv/src/commands/auth/login.rs +++ b/crates/uv/src/commands/auth/login.rs @@ -44,6 +44,7 @@ pub(crate) async fn login( network_settings.allow_insecure_host.clone(), preview, network_settings.timeout, + network_settings.retries, ) .auth_integration(AuthIntegration::NoAuthMiddleware) .build(); diff --git a/crates/uv/src/commands/auth/logout.rs b/crates/uv/src/commands/auth/logout.rs index 74582b8c8..da1ed9336 100644 --- a/crates/uv/src/commands/auth/logout.rs +++ b/crates/uv/src/commands/auth/logout.rs @@ -104,6 +104,7 @@ async fn pyx_logout( network_settings.allow_insecure_host.clone(), preview, network_settings.timeout, + network_settings.retries, ) .build(); diff --git a/crates/uv/src/commands/auth/token.rs b/crates/uv/src/commands/auth/token.rs index b28652df4..792f05305 100644 --- a/crates/uv/src/commands/auth/token.rs +++ b/crates/uv/src/commands/auth/token.rs @@ -33,6 +33,7 @@ pub(crate) async fn token( network_settings.allow_insecure_host.clone(), preview, network_settings.timeout, + network_settings.retries, ) .auth_integration(AuthIntegration::NoAuthMiddleware) .build(); diff --git a/crates/uv/src/commands/project/format.rs b/crates/uv/src/commands/project/format.rs index f63c74d0e..312b3531f 100644 --- a/crates/uv/src/commands/project/format.rs +++ b/crates/uv/src/commands/project/format.rs @@ -2,12 +2,11 @@ use std::path::Path; use std::str::FromStr; use anyhow::{Context, Result}; -use reqwest_retry::policies::ExponentialBackoff; use tokio::process::Command; use uv_bin_install::{Binary, bin_install}; use uv_cache::Cache; -use uv_client::{BaseClientBuilder, retries_from_env}; +use uv_client::BaseClientBuilder; use uv_pep440::Version; use uv_preview::{Preview, PreviewFeatures}; use uv_warnings::warn_user; @@ -64,9 +63,9 @@ pub(crate) async fn format( // Parse version if provided 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 // 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(); // Get the path to Ruff, downloading it if necessary diff --git a/crates/uv/src/commands/publish.rs b/crates/uv/src/commands/publish.rs index 2e5facb90..923cf8bcb 100644 --- a/crates/uv/src/commands/publish.rs +++ b/crates/uv/src/commands/publish.rs @@ -129,6 +129,7 @@ pub(crate) async fn publish( .auth_integration(AuthIntegration::NoAuthMiddleware) .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 let download_concurrency = Arc::new(Semaphore::new(1)); @@ -222,6 +223,7 @@ pub(crate) async fn publish( &filename, &publish_url, &upload_client, + retry_policy, &credentials, check_url_client.as_ref(), &download_concurrency, diff --git a/crates/uv/src/commands/python/install.rs b/crates/uv/src/commands/python/install.rs index c59119fb9..208007fdf 100644 --- a/crates/uv/src/commands/python/install.rs +++ b/crates/uv/src/commands/python/install.rs @@ -11,11 +11,10 @@ use futures::stream::FuturesUnordered; use indexmap::IndexSet; use itertools::{Either, Itertools}; use owo_colors::{AnsiColors, OwoColorize}; -use reqwest_retry::policies::ExponentialBackoff; use rustc_hash::{FxHashMap, FxHashSet}; use tracing::{debug, trace}; -use uv_client::{BaseClientBuilder, retries_from_env}; +use uv_client::BaseClientBuilder; use uv_fs::Simplified; use uv_platform::{Arch, Libc}; use uv_preview::{Preview, PreviewFeatures}; @@ -398,9 +397,9 @@ pub(crate) async fn install( .unique_by(|download| download.key()) .collect::>(); + let retry_policy = client_builder.retry_policy(); // Python downloads are performing their own retries to catch stream errors, disable the // 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 reporter = PythonDownloadReporter::new(printer, downloads.len() as u64); diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index b370d6eac..350ea1505 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -183,9 +183,9 @@ async fn run(mut cli: Cli) -> Result { settings.network_settings.native_tls, settings.network_settings.allow_insecure_host, settings.preview, - environment.http_timeout, - ) - .retries_from_env()?; + settings.network_settings.timeout, + settings.network_settings.retries, + ); Some( RunCommand::from_args(command, client_builder, *module, *script, *gui_script) .await?, @@ -456,9 +456,9 @@ async fn run(mut cli: Cli) -> Result { globals.network_settings.native_tls, globals.network_settings.allow_insecure_host.clone(), globals.preview, - environment.http_timeout, - ) - .retries_from_env()?; + globals.network_settings.timeout, + globals.network_settings.retries, + ); match *cli.command { Commands::Auth(AuthNamespace { diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index e8d3470fa..4e2da8b5c 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -184,6 +184,7 @@ pub(crate) struct NetworkSettings { pub(crate) native_tls: bool, pub(crate) allow_insecure_host: Vec, pub(crate) timeout: Duration, + pub(crate) retries: u32, } impl NetworkSettings { @@ -200,7 +201,6 @@ impl NetworkSettings { } else { Connectivity::Online }; - let timeout = environment.http_timeout; let native_tls = flag(args.native_tls, args.no_native_tls, "native-tls") .combine(workspace.and_then(|workspace| workspace.globals.native_tls)) .unwrap_or(false); @@ -225,7 +225,8 @@ impl NetworkSettings { connectivity, native_tls, allow_insecure_host, - timeout, + timeout: environment.http_timeout, + retries: environment.http_retries, } } } diff --git a/crates/uv/tests/it/network.rs b/crates/uv/tests/it/network.rs index f541528bc..991ad27c3 100644 --- a/crates/uv/tests/it/network.rs +++ b/crates/uv/tests/it/network.rs @@ -323,8 +323,21 @@ async fn install_http_retries() { ----- stdout ----- ----- stderr ----- - error: Failed to parse `UV_HTTP_RETRIES` - Caused by: invalid digit found in string + error: Failed to parse environment variable `UV_HTTP_RETRIES` with invalid value `foo`: 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 ----- ----- stderr ----- - error: Failed to parse `UV_HTTP_RETRIES` - Caused by: number too large to fit in target type + error: Failed to parse environment variable `UV_HTTP_RETRIES` with invalid value `999999999999`: number too large to fit in target type " ); diff --git a/crates/uv/tests/it/show_settings.rs b/crates/uv/tests/it/show_settings.rs index e47c6a2e2..e82135f0d 100644 --- a/crates/uv/tests/it/show_settings.rs +++ b/crates/uv/tests/it/show_settings.rs @@ -67,6 +67,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -267,6 +268,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -468,6 +470,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -701,6 +704,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -903,6 +907,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -1081,6 +1086,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -1308,6 +1314,7 @@ fn resolve_index_url() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -1543,6 +1550,7 @@ fn resolve_index_url() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -1836,6 +1844,7 @@ fn resolve_find_links() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -2060,6 +2069,7 @@ fn resolve_top_level() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -2243,6 +2253,7 @@ fn resolve_top_level() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -2476,6 +2487,7 @@ fn resolve_top_level() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -2732,6 +2744,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -2905,6 +2918,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -3078,6 +3092,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -3253,6 +3268,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -3447,6 +3463,7 @@ fn resolve_tool() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -3633,6 +3650,7 @@ fn resolve_poetry_toml() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -3840,6 +3858,7 @@ fn resolve_both() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -4086,6 +4105,7 @@ fn resolve_both_special_fields() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -4411,6 +4431,7 @@ fn resolve_config_file() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -4711,6 +4732,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -4887,6 +4909,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -5082,6 +5105,7 @@ fn allow_insecure_host() -> anyhow::Result<()> { }, ], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -5269,6 +5293,7 @@ fn index_priority() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -5504,6 +5529,7 @@ fn index_priority() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -5745,6 +5771,7 @@ fn index_priority() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -5981,6 +6008,7 @@ fn index_priority() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -6224,6 +6252,7 @@ fn index_priority() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -6460,6 +6489,7 @@ fn index_priority() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -6709,6 +6739,7 @@ fn verify_hashes() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -6875,6 +6906,7 @@ fn verify_hashes() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -7039,6 +7071,7 @@ fn verify_hashes() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -7205,6 +7238,7 @@ fn verify_hashes() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -7369,6 +7403,7 @@ fn verify_hashes() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -7534,6 +7569,7 @@ fn verify_hashes() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -7714,6 +7750,7 @@ fn preview_features() { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -7827,6 +7864,7 @@ fn preview_features() { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -7940,6 +7978,7 @@ fn preview_features() { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -8053,6 +8092,7 @@ fn preview_features() { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -8166,6 +8206,7 @@ fn preview_features() { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -8281,6 +8322,7 @@ fn preview_features() { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -8415,6 +8457,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -8589,6 +8632,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -8786,6 +8830,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -8958,6 +9003,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -9124,6 +9170,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -9291,6 +9338,7 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -9523,6 +9571,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -9641,6 +9690,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -9782,6 +9832,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -9898,6 +9949,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -10004,6 +10056,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -10111,6 +10164,7 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -10282,6 +10336,7 @@ fn build_isolation_override() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, @@ -10451,6 +10506,7 @@ fn build_isolation_override() -> anyhow::Result<()> { native_tls: false, allow_insecure_host: [], timeout: [TIME], + retries: 3, }, concurrency: Concurrency { downloads: 50, diff --git a/crates/uv/tests/it/venv.rs b/crates/uv/tests/it/venv.rs index 55c5b4752..651803aa4 100644 --- a/crates/uv/tests/it/venv.rs +++ b/crates/uv/tests/it/venv.rs @@ -885,8 +885,6 @@ fn seed_older_python_version() { #[test] fn create_venv_with_invalid_http_timeout() { 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() .arg(context.venv.as_os_str()) .arg("--python") @@ -896,15 +894,13 @@ fn create_venv_with_invalid_http_timeout() { ----- stdout ----- ----- 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] fn create_venv_with_invalid_concurrent_installs() { let context = TestContext::new_with_versions(&["3.12"]).with_concurrent_installs("0"); - - // Create a virtual environment at `.venv`. uv_snapshot!(context.filters(), context.venv() .arg(context.venv.as_os_str()) .arg("--python") @@ -914,7 +910,7 @@ fn create_venv_with_invalid_concurrent_installs() { ----- stdout ----- ----- 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 "###); }