mirror of https://github.com/astral-sh/uv
Move parsing concurrency env variable to EnvironmentOptions (#16223)
## Summary - Move parsing `UV_CONCURRENT_INSTALLS`, `UV_CONCURRENT_BUILDS` and `UV_CONCURRENT_DOWNLOADS` to `EnvironmentOptions` Relates https://github.com/astral-sh/uv/issues/14720 ## Test Plan - Tests with existing tests - Add test with invalid parsing
This commit is contained in:
parent
d536d3f27d
commit
c7d3d549e2
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
@ -565,6 +566,13 @@ pub enum Error {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct Concurrency {
|
||||||
|
pub downloads: Option<NonZeroUsize>,
|
||||||
|
pub builds: Option<NonZeroUsize>,
|
||||||
|
pub installs: Option<NonZeroUsize>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Options loaded from environment variables.
|
/// Options loaded from environment variables.
|
||||||
///
|
///
|
||||||
/// This is currently a subset of all respected environment variables, most are parsed via Clap at
|
/// This is currently a subset of all respected environment variables, most are parsed via Clap at
|
||||||
|
|
@ -578,6 +586,7 @@ pub struct EnvironmentOptions {
|
||||||
pub log_context: Option<bool>,
|
pub log_context: Option<bool>,
|
||||||
pub http_timeout: Duration,
|
pub http_timeout: Duration,
|
||||||
pub upload_http_timeout: Duration,
|
pub upload_http_timeout: Duration,
|
||||||
|
pub concurrency: Concurrency,
|
||||||
#[cfg(feature = "tracing-durations-export")]
|
#[cfg(feature = "tracing-durations-export")]
|
||||||
pub tracing_durations_file: Option<PathBuf>,
|
pub tracing_durations_file: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
@ -587,11 +596,9 @@ 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_u64_environment_variable(EnvVars::UV_HTTP_TIMEOUT)?
|
||||||
.or(parse_integer_environment_variable(
|
.or(parse_u64_environment_variable(EnvVars::UV_REQUEST_TIMEOUT)?)
|
||||||
EnvVars::UV_REQUEST_TIMEOUT,
|
.or(parse_u64_environment_variable(EnvVars::HTTP_TIMEOUT)?)
|
||||||
)?)
|
|
||||||
.or(parse_integer_environment_variable(EnvVars::HTTP_TIMEOUT)?)
|
|
||||||
.map(Duration::from_secs);
|
.map(Duration::from_secs);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
@ -602,6 +609,15 @@ impl EnvironmentOptions {
|
||||||
python_install_registry: parse_boolish_environment_variable(
|
python_install_registry: parse_boolish_environment_variable(
|
||||||
EnvVars::UV_PYTHON_INSTALL_REGISTRY,
|
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,
|
||||||
|
)?,
|
||||||
|
},
|
||||||
install_mirrors: PythonInstallMirrors {
|
install_mirrors: PythonInstallMirrors {
|
||||||
python_install_mirror: parse_string_environment_variable(
|
python_install_mirror: parse_string_environment_variable(
|
||||||
EnvVars::UV_PYTHON_INSTALL_MIRROR,
|
EnvVars::UV_PYTHON_INSTALL_MIRROR,
|
||||||
|
|
@ -614,12 +630,10 @@ 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_integer_environment_variable(
|
upload_http_timeout: parse_u64_environment_variable(EnvVars::UV_UPLOAD_HTTP_TIMEOUT)?
|
||||||
EnvVars::UV_UPLOAD_HTTP_TIMEOUT,
|
.map(Duration::from_secs)
|
||||||
)?
|
.or(http_timeout)
|
||||||
.map(Duration::from_secs)
|
.unwrap_or(Duration::from_secs(15 * 60)),
|
||||||
.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)),
|
||||||
#[cfg(feature = "tracing-durations-export")]
|
#[cfg(feature = "tracing-durations-export")]
|
||||||
tracing_durations_file: parse_path_environment_variable(
|
tracing_durations_file: parse_path_environment_variable(
|
||||||
|
|
@ -702,8 +716,14 @@ fn parse_string_environment_variable(name: &'static str) -> Result<Option<String
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a integer environment variable.
|
fn parse_integer_environment_variable<T>(
|
||||||
fn parse_integer_environment_variable(name: &'static str) -> Result<Option<u64>, Error> {
|
name: &'static str,
|
||||||
|
err_msg: &'static str,
|
||||||
|
) -> Result<Option<T>, Error>
|
||||||
|
where
|
||||||
|
T: std::str::FromStr + Copy,
|
||||||
|
<T as std::str::FromStr>::Err: std::fmt::Display,
|
||||||
|
{
|
||||||
let value = match std::env::var(name) {
|
let value = match std::env::var(name) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -712,7 +732,7 @@ fn parse_integer_environment_variable(name: &'static str) -> Result<Option<u64>,
|
||||||
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: "expected an integer".to_string(),
|
err: err_msg.to_string(),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -720,16 +740,29 @@ fn parse_integer_environment_variable(name: &'static str) -> Result<Option<u64>,
|
||||||
if value.is_empty() {
|
if value.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
match value.parse::<u64>() {
|
|
||||||
|
match value.parse::<T>() {
|
||||||
Ok(v) => Ok(Some(v)),
|
Ok(v) => Ok(Some(v)),
|
||||||
Err(_) => Err(Error::InvalidEnvironmentVariable {
|
Err(_) => Err(Error::InvalidEnvironmentVariable {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
value,
|
value,
|
||||||
err: "expected an integer".to_string(),
|
err: err_msg.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> {
|
||||||
|
|
|
||||||
|
|
@ -117,15 +117,21 @@ impl GlobalSettings {
|
||||||
},
|
},
|
||||||
network_settings,
|
network_settings,
|
||||||
concurrency: Concurrency {
|
concurrency: Concurrency {
|
||||||
downloads: env(env::CONCURRENT_DOWNLOADS)
|
downloads: environment
|
||||||
|
.concurrency
|
||||||
|
.downloads
|
||||||
.combine(workspace.and_then(|workspace| workspace.globals.concurrent_downloads))
|
.combine(workspace.and_then(|workspace| workspace.globals.concurrent_downloads))
|
||||||
.map(NonZeroUsize::get)
|
.map(NonZeroUsize::get)
|
||||||
.unwrap_or(Concurrency::DEFAULT_DOWNLOADS),
|
.unwrap_or(Concurrency::DEFAULT_DOWNLOADS),
|
||||||
builds: env(env::CONCURRENT_BUILDS)
|
builds: environment
|
||||||
|
.concurrency
|
||||||
|
.builds
|
||||||
.combine(workspace.and_then(|workspace| workspace.globals.concurrent_builds))
|
.combine(workspace.and_then(|workspace| workspace.globals.concurrent_builds))
|
||||||
.map(NonZeroUsize::get)
|
.map(NonZeroUsize::get)
|
||||||
.unwrap_or_else(Concurrency::threads),
|
.unwrap_or_else(Concurrency::threads),
|
||||||
installs: env(env::CONCURRENT_INSTALLS)
|
installs: environment
|
||||||
|
.concurrency
|
||||||
|
.installs
|
||||||
.combine(workspace.and_then(|workspace| workspace.globals.concurrent_installs))
|
.combine(workspace.and_then(|workspace| workspace.globals.concurrent_installs))
|
||||||
.map(NonZeroUsize::get)
|
.map(NonZeroUsize::get)
|
||||||
.unwrap_or_else(Concurrency::threads),
|
.unwrap_or_else(Concurrency::threads),
|
||||||
|
|
@ -3747,16 +3753,6 @@ impl AuthLoginSettings {
|
||||||
// Environment variables that are not exposed as CLI arguments.
|
// Environment variables that are not exposed as CLI arguments.
|
||||||
mod env {
|
mod env {
|
||||||
use uv_static::EnvVars;
|
use uv_static::EnvVars;
|
||||||
|
|
||||||
pub(super) const CONCURRENT_DOWNLOADS: (&str, &str) =
|
|
||||||
(EnvVars::UV_CONCURRENT_DOWNLOADS, "a non-zero integer");
|
|
||||||
|
|
||||||
pub(super) const CONCURRENT_BUILDS: (&str, &str) =
|
|
||||||
(EnvVars::UV_CONCURRENT_BUILDS, "a non-zero integer");
|
|
||||||
|
|
||||||
pub(super) const CONCURRENT_INSTALLS: (&str, &str) =
|
|
||||||
(EnvVars::UV_CONCURRENT_INSTALLS, "a non-zero integer");
|
|
||||||
|
|
||||||
pub(super) const UV_PYTHON_DOWNLOADS: (&str, &str) = (
|
pub(super) const UV_PYTHON_DOWNLOADS: (&str, &str) = (
|
||||||
EnvVars::UV_PYTHON_DOWNLOADS,
|
EnvVars::UV_PYTHON_DOWNLOADS,
|
||||||
"one of 'auto', 'true', 'manual', 'never', or 'false'",
|
"one of 'auto', 'true', 'manual', 'never', or 'false'",
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,15 @@ impl TestContext {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the "concurrent installs" for all commands in this context.
|
||||||
|
pub fn with_concurrent_installs(mut self, concurrent_installs: &str) -> Self {
|
||||||
|
self.extra_env.push((
|
||||||
|
EnvVars::UV_CONCURRENT_INSTALLS.into(),
|
||||||
|
concurrent_installs.into(),
|
||||||
|
));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Add extra standard filtering for messages like "Resolved 10 packages" which
|
/// Add extra standard filtering for messages like "Resolved 10 packages" which
|
||||||
/// can differ between platforms.
|
/// can differ between platforms.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -900,6 +900,24 @@ fn create_venv_with_invalid_http_timeout() {
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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")
|
||||||
|
.arg("3.12"), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Failed to parse environment variable `UV_CONCURRENT_INSTALLS` with invalid value `0`: expected a non-zero positive integer
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_venv_unknown_python_minor() {
|
fn create_venv_unknown_python_minor() {
|
||||||
let context = TestContext::new_with_versions(&["3.12"]).with_filtered_python_sources();
|
let context = TestContext::new_with_versions(&["3.12"]).with_filtered_python_sources();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue