Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion crates/uv-client/src/base_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::sync::Arc;
use std::time::Duration;
use std::{env, io, iter};

use anyhow::Context;
use anyhow::anyhow;
use http::{
HeaderMap, HeaderName, HeaderValue, Method, StatusCode,
Expand Down Expand Up @@ -166,6 +167,25 @@ impl<'a> BaseClientBuilder<'a> {
self
}

/// 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(self) -> anyhow::Result<Self> {
// 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(self.retries(
value
.to_string_lossy()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can probably be to_str since if it's non-UTF-8 it won't parse, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I need to write an error case for when that's returns None, it seemed easier to just let it hit the parse error.

.as_ref()
.parse::<u32>()
.context("Failed to parse `UV_HTTP_RETRIES`")?,
))
} else {
Ok(self)
}
}

#[must_use]
pub fn native_tls(mut self, native_tls: bool) -> Self {
self.native_tls = native_tls;
Expand Down Expand Up @@ -238,7 +258,11 @@ impl<'a> BaseClientBuilder<'a> {

/// Create a [`RetryPolicy`] for the client.
fn retry_policy(&self) -> ExponentialBackoff {
ExponentialBackoff::builder().build_with_max_retries(self.retries)
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));
}
Comment on lines +262 to +264
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need this so a test that we consume all the retries isn't slow.

builder.build_with_max_retries(self.retries)
}

pub fn build(&self) -> BaseClient {
Expand Down
5 changes: 5 additions & 0 deletions crates/uv-client/src/registry_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ impl<'a> RegistryClientBuilder<'a> {
self
}

pub fn retries_from_env(mut self) -> anyhow::Result<Self> {
self.base_client_builder = self.base_client_builder.retries_from_env()?;
Ok(self)
}

#[must_use]
pub fn native_tls(mut self, native_tls: bool) -> Self {
self.base_client_builder = self.base_client_builder.native_tls(native_tls);
Expand Down
3 changes: 3 additions & 0 deletions crates/uv-requirements/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ pub enum Error {
#[error(transparent)]
WheelFilename(#[from] uv_distribution_filename::WheelFilenameError),

#[error("Failed to construct HTTP client")]
ClientError(#[source] anyhow::Error),

#[error(transparent)]
Io(#[from] std::io::Error),
}
Expand Down
6 changes: 6 additions & 0 deletions crates/uv-static/src/env_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,9 @@ impl EnvVars {
/// Timeout (in seconds) for HTTP requests. (default: 30 s)
pub const UV_HTTP_TIMEOUT: &'static str = "UV_HTTP_TIMEOUT";

/// The number of retries for HTTP requests. (default: 3)
pub const UV_HTTP_RETRIES: &'static str = "UV_HTTP_RETRIES";

/// Timeout (in seconds) for HTTP requests. Equivalent to `UV_HTTP_TIMEOUT`.
pub const UV_REQUEST_TIMEOUT: &'static str = "UV_REQUEST_TIMEOUT";

Expand Down Expand Up @@ -659,6 +662,9 @@ impl EnvVars {
#[attr_hidden]
pub const UV_TEST_VENDOR_LINKS_URL: &'static str = "UV_TEST_VENDOR_LINKS_URL";

/// Used to disable delay for HTTP retries in tests.
pub const UV_TEST_NO_HTTP_RETRY_DELAY: &'static str = "UV_TEST_NO_HTTP_RETRY_DELAY";

/// Used to set an index url for tests.
#[attr_hidden]
pub const UV_TEST_INDEX_URL: &'static str = "UV_TEST_INDEX_URL";
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/build_frontend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ async fn build_impl(
} = settings;

let client_builder = BaseClientBuilder::default()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone());
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/pip/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ pub(crate) async fn pip_compile(
}

let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.keyring(keyring_provider)
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/pip/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ pub(crate) async fn pip_install(
let start = std::time::Instant::now();

let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.keyring(keyring_provider)
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/pip/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ pub(crate) async fn pip_list(
let capabilities = IndexCapabilities::default();

let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.keyring(keyring_provider)
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/pip/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ pub(crate) async fn pip_sync(
preview: PreviewMode,
) -> Result<ExitStatus> {
let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.keyring(keyring_provider)
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/pip/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ pub(crate) async fn pip_tree(
let capabilities = IndexCapabilities::default();

let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.keyring(keyring_provider)
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/pip/uninstall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub(crate) async fn pip_uninstall(
let start = std::time::Instant::now();

let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.keyring(keyring_provider)
Expand Down
2 changes: 2 additions & 0 deletions crates/uv/src/commands/project/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ pub(crate) async fn add(
}

let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone());
Expand Down Expand Up @@ -329,6 +330,7 @@ pub(crate) async fn add(
.ok();

let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.keyring(settings.resolver.keyring_provider)
Expand Down
2 changes: 2 additions & 0 deletions crates/uv/src/commands/project/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ async fn init_script(
warn_user_once!("`--package` is a no-op for Python scripts, which are standalone");
}
let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone());
Expand Down Expand Up @@ -348,6 +349,7 @@ async fn init_project(

let reporter = PythonDownloadReporter::single(printer);
let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone());
Expand Down
2 changes: 2 additions & 0 deletions crates/uv/src/commands/project/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ pub(crate) async fn lock(
let script = match script {
Some(ScriptPath::Path(path)) => {
let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone());
Expand Down Expand Up @@ -588,6 +589,7 @@ async fn do_lock(

// Initialize the client.
let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.keyring(*keyring_provider)
Expand Down
7 changes: 7 additions & 0 deletions crates/uv/src/commands/project/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,7 @@ impl ScriptInterpreter {
}

let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone());
Expand Down Expand Up @@ -946,6 +947,7 @@ impl ProjectInterpreter {
}

let client_builder = BaseClientBuilder::default()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone());
Expand Down Expand Up @@ -1656,6 +1658,8 @@ pub(crate) async fn resolve_names(
} = settings;

let client_builder = BaseClientBuilder::new()
.retries_from_env()
.map_err(uv_requirements::Error::ClientError)?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.keyring(*keyring_provider)
Expand Down Expand Up @@ -1813,6 +1817,7 @@ pub(crate) async fn resolve_environment(
} = spec.requirements;

let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.keyring(*keyring_provider)
Expand Down Expand Up @@ -1984,6 +1989,7 @@ pub(crate) async fn sync_environment(
} = settings;

let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.keyring(keyring_provider)
Expand Down Expand Up @@ -2147,6 +2153,7 @@ pub(crate) async fn update_environment(
} = settings;

let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.keyring(*keyring_provider)
Expand Down
4 changes: 4 additions & 0 deletions crates/uv/src/commands/project/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,7 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
// If we're isolating the environment, use an ephemeral virtual environment as the
// base environment for the project.
let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone());
Expand Down Expand Up @@ -857,6 +858,7 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl

let interpreter = {
let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone());
Expand Down Expand Up @@ -927,6 +929,7 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
None
} else {
let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone());
Expand Down Expand Up @@ -1524,6 +1527,7 @@ impl RunCommand {
.tempfile()?;

let client = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone())
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/project/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,7 @@ pub(super) async fn do_sync(
} = settings;

let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.keyring(keyring_provider)
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/project/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ pub(crate) async fn tree(
let client = RegistryClientBuilder::new(
cache.clone().with_refresh(Refresh::All(Timestamp::now())),
)
.retries_from_env()?
.native_tls(network_settings.native_tls)
.connectivity(network_settings.connectivity)
.allow_insecure_host(network_settings.allow_insecure_host.clone())
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ pub(crate) async fn publish(
false,
);
let registry_client_builder = RegistryClientBuilder::new(cache.clone())
.retries_from_env()?
.native_tls(network_settings.native_tls)
.connectivity(network_settings.connectivity)
.allow_insecure_host(network_settings.allow_insecure_host.clone())
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/python/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ pub(crate) async fn install(

// Download and unpack the Python versions concurrently
let client = uv_client::BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone())
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/python/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ pub(crate) async fn pin(
}

let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone());
Expand Down
2 changes: 2 additions & 0 deletions crates/uv/src/commands/tool/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub(crate) async fn install(
preview: PreviewMode,
) -> Result<ExitStatus> {
let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone());
Expand Down Expand Up @@ -97,6 +98,7 @@ pub(crate) async fn install(
let workspace_cache = WorkspaceCache::default();

let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone());
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/tool/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,7 @@ async fn get_or_create_environment(
preview: PreviewMode,
) -> Result<(ToolRequirement, PythonEnvironment), ProjectError> {
let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone());
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/tool/upgrade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ pub(crate) async fn upgrade(

let reporter = PythonDownloadReporter::single(printer);
let client_builder = BaseClientBuilder::new()
.retries_from_env()?
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone());
Expand Down
3 changes: 3 additions & 0 deletions crates/uv/src/commands/venv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ async fn venv_impl(
.unwrap_or(PathBuf::from(".venv")),
);

// TODO(zanieb): We don't use [`BaseClientBuilder::retries_from_env`] here because it's a pain
// to map into a miette diagnostic. We should just remove miette diagnostics here, we're not
// using them elsewhere.
let client_builder = BaseClientBuilder::default()
Comment on lines +196 to 199
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave up here, it looked annoying and we only need the client for fetching seed packages.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing miette in #14546

.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
Expand Down
Loading
Loading