This commit is contained in:
Anoop Rehman 2025-12-16 11:27:07 +01:00 committed by GitHub
commit 1c85e9552f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 37 additions and 9 deletions

View File

@ -1233,7 +1233,7 @@ impl RegistryClient {
std::io::Error::new( std::io::Error::new(
std::io::ErrorKind::TimedOut, std::io::ErrorKind::TimedOut,
format!( format!(
"Failed to download distribution due to network timeout. Try increasing UV_HTTP_TIMEOUT (current value: {}s).", "Failed to download distribution due to network timeout ({}s).\nTry increasing UV_HTTP_TIMEOUT to a larger integer value (in seconds), e.g., UV_HTTP_TIMEOUT=60",
self.timeout().as_secs() self.timeout().as_secs()
), ),
) )

View File

@ -93,7 +93,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
io::Error::new( io::Error::new(
io::ErrorKind::TimedOut, io::ErrorKind::TimedOut,
format!( format!(
"Failed to download distribution due to network timeout. Try increasing UV_HTTP_TIMEOUT (current value: {}s).", "Failed to download distribution due to network timeout ({}s).\nTry increasing UV_HTTP_TIMEOUT to a larger integer value (in seconds), e.g., UV_HTTP_TIMEOUT=60",
self.client.unmanaged.timeout().as_secs() self.client.unmanaged.timeout().as_secs()
), ),
) )

View File

@ -608,11 +608,15 @@ 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_integer_environment_variable(EnvVars::UV_HTTP_TIMEOUT)? let http_timeout = parse_integer_environment_variable(EnvVars::UV_HTTP_TIMEOUT, true)?
.or(parse_integer_environment_variable( .or(parse_integer_environment_variable(
EnvVars::UV_REQUEST_TIMEOUT, EnvVars::UV_REQUEST_TIMEOUT,
true,
)?)
.or(parse_integer_environment_variable(
EnvVars::HTTP_TIMEOUT,
true,
)?) )?)
.or(parse_integer_environment_variable(EnvVars::HTTP_TIMEOUT)?)
.map(Duration::from_secs); .map(Duration::from_secs);
Ok(Self { Ok(Self {
@ -625,9 +629,15 @@ impl EnvironmentOptions {
EnvVars::UV_PYTHON_INSTALL_REGISTRY, EnvVars::UV_PYTHON_INSTALL_REGISTRY,
)?, )?,
concurrency: Concurrency { concurrency: Concurrency {
downloads: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_DOWNLOADS)?, downloads: parse_integer_environment_variable(
builds: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_BUILDS)?, EnvVars::UV_CONCURRENT_DOWNLOADS,
installs: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_INSTALLS)?, false,
)?,
builds: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_BUILDS, false)?,
installs: parse_integer_environment_variable(
EnvVars::UV_CONCURRENT_INSTALLS,
false,
)?,
}, },
install_mirrors: PythonInstallMirrors { install_mirrors: PythonInstallMirrors {
python_install_mirror: parse_string_environment_variable( python_install_mirror: parse_string_environment_variable(
@ -644,12 +654,13 @@ impl EnvironmentOptions {
lfs: parse_boolish_environment_variable(EnvVars::UV_GIT_LFS)?, lfs: parse_boolish_environment_variable(EnvVars::UV_GIT_LFS)?,
upload_http_timeout: parse_integer_environment_variable( upload_http_timeout: parse_integer_environment_variable(
EnvVars::UV_UPLOAD_HTTP_TIMEOUT, EnvVars::UV_UPLOAD_HTTP_TIMEOUT,
true,
)? )?
.map(Duration::from_secs) .map(Duration::from_secs)
.or(http_timeout) .or(http_timeout)
.unwrap_or(Duration::from_secs(15 * 60)), .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)? http_retries: parse_integer_environment_variable(EnvVars::UV_HTTP_RETRIES, false)?
.unwrap_or(uv_client::DEFAULT_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(
@ -732,7 +743,15 @@ fn parse_string_environment_variable(name: &'static str) -> Result<Option<String
} }
} }
fn parse_integer_environment_variable<T>(name: &'static str) -> Result<Option<T>, Error> /// Parse an integer environment variable.
///
/// If `is_duration` is `true`, this function will check if the value ends with "s" and provide
/// a helpful error message, as duration environment variables expect an integer value in seconds
/// (not a string like "60s").
fn parse_integer_environment_variable<T>(
name: &'static str,
is_duration: bool,
) -> 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,
@ -754,6 +773,15 @@ where
return Ok(None); return Ok(None);
} }
// For duration variables, check if the value ends with "s" and provide a helpful error message
if is_duration && value.trim_end().ends_with('s') {
return Err(Error::InvalidEnvironmentVariable {
name: name.to_string(),
value: value.clone(),
err: "expected an integer value in seconds (e.g., 30), not a string with units (e.g., 30s)".to_string(),
});
}
match value.parse::<T>() { match value.parse::<T>() {
Ok(v) => Ok(Some(v)), Ok(v) => Ok(Some(v)),
Err(err) => Err(Error::InvalidEnvironmentVariable { Err(err) => Err(Error::InvalidEnvironmentVariable {