Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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: 11 additions & 15 deletions crates/uv-client/src/base_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ pub struct BaseClientBuilder<'a> {
platform: Option<&'a Platform>,
auth_integration: AuthIntegration,
indexes: Indexes,
timeout: Option<Duration>,
default_timeout: Duration,
extra_middleware: Option<ExtraMiddleware>,
proxies: Vec<Proxy>,
Expand Down Expand Up @@ -137,6 +138,7 @@ impl Default for BaseClientBuilder<'_> {
platform: None,
auth_integration: AuthIntegration::default(),
indexes: Indexes::new(),
timeout: None,
default_timeout: Duration::from_secs(30),
extra_middleware: None,
proxies: vec![],
Expand All @@ -153,12 +155,14 @@ impl BaseClientBuilder<'_> {
native_tls: bool,
allow_insecure_host: Vec<TrustedHost>,
preview: Preview,
timeout: Option<Duration>,
) -> Self {
Self {
preview,
allow_insecure_host,
native_tls,
connectivity,
timeout,
..Self::default()
}
}
Expand Down Expand Up @@ -245,6 +249,12 @@ impl<'a> BaseClientBuilder<'a> {
self
}

#[must_use]
pub fn timeout(mut self, timeout: Option<Duration>) -> Self {
self.timeout = timeout;
self
}

#[must_use]
pub fn default_timeout(mut self, default_timeout: Duration) -> Self {
self.default_timeout = default_timeout;
Expand Down Expand Up @@ -299,21 +309,7 @@ impl<'a> BaseClientBuilder<'a> {
}

pub fn build(&self) -> BaseClient {
// 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 timeout = env::var(EnvVars::UV_HTTP_TIMEOUT)
.or_else(|_| env::var(EnvVars::UV_REQUEST_TIMEOUT))
.or_else(|_| env::var(EnvVars::HTTP_TIMEOUT))
.and_then(|value| {
value.parse::<u64>()
.map(Duration::from_secs)
.or_else(|_| {
// On parse error, warn and use the default timeout
warn_user_once!("Ignoring invalid value from environment for `UV_HTTP_TIMEOUT`. Expected an integer number of seconds, got \"{value}\".");
Ok(self.default_timeout)
})
})
.unwrap_or(self.default_timeout);
let timeout = self.timeout.unwrap_or(self.default_timeout);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should probably remove self.default_timeout entirely and unwrap to that during settings resolution.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I did not do this because we use 15min default for upload_client
is it ok to override timeout at all at upload_client?

        // Set a very high timeout for uploads, connections are often 10x slower on upload than
        // download. 15 min is taken from the time a trusted publishing token is valid.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I deleted default_timeout
but it will be quite easy to revert it if it is better to have default timeout

debug!("Using request timeout of {}s", timeout.as_secs());

// Use the custom client if provided, otherwise create a new one
Expand Down
13 changes: 7 additions & 6 deletions crates/uv-dev/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
use std::env;

use anyhow::Result;
use clap::Parser;
use tracing::instrument;

use crate::clear_compile::ClearCompileArgs;
use crate::compile::CompileArgs;
use crate::generate_all::Args as GenerateAllArgs;
Expand All @@ -16,6 +12,10 @@ use crate::generate_sysconfig_mappings::Args as GenerateSysconfigMetadataArgs;
use crate::render_benchmarks::RenderBenchmarksArgs;
use crate::validate_zip::ValidateZipArgs;
use crate::wheel_metadata::WheelMetadataArgs;
use anyhow::Result;
use clap::Parser;
use tracing::instrument;
use uv_settings::EnvironmentOptions;

mod clear_compile;
mod compile;
Expand Down Expand Up @@ -61,9 +61,10 @@ enum Cli {
#[instrument] // Anchor span to check for overhead
pub async fn run() -> Result<()> {
let cli = Cli::parse();
let environment = EnvironmentOptions::new()?;
match cli {
Cli::WheelMetadata(args) => wheel_metadata::wheel_metadata(args).await?,
Cli::ValidateZip(args) => validate_zip::validate_zip(args).await?,
Cli::WheelMetadata(args) => wheel_metadata::wheel_metadata(args, environment).await?,
Cli::ValidateZip(args) => validate_zip::validate_zip(args, environment).await?,
Cli::Compile(args) => compile::compile(args).await?,
Cli::ClearCompile(args) => clear_compile::clear_compile(&args)?,
Cli::GenerateAll(args) => generate_all::main(&args).await?,
Expand Down
12 changes: 10 additions & 2 deletions crates/uv-dev/src/validate_zip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use uv_cache::{Cache, CacheArgs};
use uv_client::{BaseClientBuilder, RegistryClientBuilder};
use uv_pep508::VerbatimUrl;
use uv_pypi_types::ParsedUrl;
use uv_settings::EnvironmentOptions;

#[derive(Parser)]
pub(crate) struct ValidateZipArgs {
Expand All @@ -17,9 +18,16 @@ pub(crate) struct ValidateZipArgs {
cache_args: CacheArgs,
}

pub(crate) async fn validate_zip(args: ValidateZipArgs) -> Result<()> {
pub(crate) async fn validate_zip(
args: ValidateZipArgs,
environment: EnvironmentOptions,
) -> Result<()> {
let cache = Cache::try_from(args.cache_args)?.init()?;
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
let client = RegistryClientBuilder::new(
BaseClientBuilder::default().timeout(environment.http_timeout),
cache,
)
.build();

let ParsedUrl::Archive(archive) = ParsedUrl::try_from(args.url.to_url())? else {
bail!("Only archive URLs are supported");
Expand Down
12 changes: 10 additions & 2 deletions crates/uv-dev/src/wheel_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use uv_distribution_filename::WheelFilename;
use uv_distribution_types::{BuiltDist, DirectUrlBuiltDist, IndexCapabilities, RemoteSource};
use uv_pep508::VerbatimUrl;
use uv_pypi_types::ParsedUrl;
use uv_settings::EnvironmentOptions;

#[derive(Parser)]
pub(crate) struct WheelMetadataArgs {
Expand All @@ -18,9 +19,16 @@ pub(crate) struct WheelMetadataArgs {
cache_args: CacheArgs,
}

pub(crate) async fn wheel_metadata(args: WheelMetadataArgs) -> Result<()> {
pub(crate) async fn wheel_metadata(
args: WheelMetadataArgs,
environment: EnvironmentOptions,
) -> Result<()> {
let cache = Cache::try_from(args.cache_args)?.init()?;
let client = RegistryClientBuilder::new(BaseClientBuilder::default(), cache).build();
let client = RegistryClientBuilder::new(
BaseClientBuilder::default().timeout(environment.http_timeout),
cache,
)
.build();
let capabilities = IndexCapabilities::default();

let filename = WheelFilename::from_str(&args.url.filename()?)?;
Expand Down
36 changes: 34 additions & 2 deletions crates/uv-settings/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::ops::Deref;
use std::path::{Path, PathBuf};

use std::time::Duration;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This import should be in the group above.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think we cannot group path and time

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There should be whitespace between the std imports and the uv imports

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added

use uv_dirs::{system_config_file, user_config_dir};
use uv_fs::Simplified;
use uv_static::EnvVars;
Expand Down Expand Up @@ -551,7 +551,8 @@ pub enum Error {
#[error("Failed to parse: `{}`", _0.user_display())]
UvToml(PathBuf, #[source] Box<toml::de::Error>),

#[error("Failed to parse: `{}`. The `{}` field is not allowed in a `uv.toml` file. `{}` is only applicable in the context of a project, and should be placed in a `pyproject.toml` file instead.", _0.user_display(), _1, _1)]
#[error("Failed to parse: `{}`. The `{}` field is not allowed in a `uv.toml` file. `{}` is only applicable in the context of a project, and should be placed in a `pyproject.toml` file instead.", _0.user_display(), _1, _1
)]
PyprojectOnlyField(PathBuf, &'static str),

#[error("Failed to parse environment variable `{name}` with invalid value `{value}`: {err}")]
Expand All @@ -572,6 +573,7 @@ pub struct EnvironmentOptions {
pub python_install_registry: Option<bool>,
pub install_mirrors: PythonInstallMirrors,
pub log_context: Option<bool>,
pub http_timeout: Option<Duration>,
}

impl EnvironmentOptions {
Expand All @@ -594,6 +596,14 @@ impl EnvironmentOptions {
)?,
},
log_context: parse_boolish_environment_variable(EnvVars::UV_LOG_CONTEXT)?,
// 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
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),
})
}
}
Expand Down Expand Up @@ -670,3 +680,25 @@ fn parse_string_environment_variable(name: &'static str) -> Result<Option<String
},
}
}

/// Parse a integer environment variable.
fn parse_integer_environment_variable(name: &'static str) -> Result<Option<u64>, Error> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think as written, this will return an error on an empty string instead of None?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed
And right now return None
I have thought that it is better to return None only for String

match std::env::var(name) {
Ok(v) => match v.parse::<u64>() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would probably not nest these matches, I'd do an early return if the environment variable is not present or not unicode.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

refactored

Ok(v) => Ok(Some(v)),
Err(err) => Err(Error::InvalidEnvironmentVariable {
name: name.to_string(),
value: err.to_string(),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Doesn't this return a https://doc.rust-lang.org/std/num/struct.ParseIntError.html we should use as the error message not a value?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes, fixed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added test for this

err: "expected an integer".to_string(),
}),
},
Err(e) => match e {
std::env::VarError::NotPresent => Ok(None),
std::env::VarError::NotUnicode(err) => Err(Error::InvalidEnvironmentVariable {
name: name.to_string(),
value: err.to_string_lossy().to_string(),
err: "expected an integer".to_string(),
}),
},
}
}
15 changes: 9 additions & 6 deletions crates/uv/src/commands/auth/login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@ pub(crate) async fn login(
bail!("Cannot specify a password when logging in to pyx");
}

let client = BaseClientBuilder::default()
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone())
.auth_integration(AuthIntegration::NoAuthMiddleware)
.build();
let client = BaseClientBuilder::new(
network_settings.connectivity,
network_settings.native_tls,
network_settings.allow_insecure_host.clone(),
preview,
Comment thread
andrey-berenda marked this conversation as resolved.
network_settings.timeout,
)
.auth_integration(AuthIntegration::NoAuthMiddleware)
.build();

let access_token = pyx_login_with_browser(&pyx_store, &client, &printer).await?;
let jwt = PyxJwt::decode(&access_token)?;
Expand Down
13 changes: 8 additions & 5 deletions crates/uv/src/commands/auth/logout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,14 @@ async fn pyx_logout(
printer: Printer,
) -> Result<ExitStatus> {
// Initialize the client.
let client = BaseClientBuilder::default()
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone())
.build();
let client = BaseClientBuilder::new(
network_settings.connectivity,
network_settings.native_tls,
network_settings.allow_insecure_host.clone(),
Preview::default(),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should be using the user-provided Preview value here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

added

network_settings.timeout,
)
.build();

// Retrieve the token store.
let Some(tokens) = store.read().await? else {
Expand Down
16 changes: 9 additions & 7 deletions crates/uv/src/commands/auth/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@ pub(crate) async fn token(
if username.is_some() {
bail!("Cannot specify a username when logging in to pyx");
}

let client = BaseClientBuilder::default()
.connectivity(network_settings.connectivity)
.native_tls(network_settings.native_tls)
.allow_insecure_host(network_settings.allow_insecure_host.clone())
.auth_integration(AuthIntegration::NoAuthMiddleware)
.build();
let client = BaseClientBuilder::new(
network_settings.connectivity,
network_settings.native_tls,
network_settings.allow_insecure_host.clone(),
preview,
Comment thread
andrey-berenda marked this conversation as resolved.
network_settings.timeout,
)
.auth_integration(AuthIntegration::NoAuthMiddleware)
.build();

pyx_refresh(&pyx_store, &client, printer).await?;
return Ok(ExitStatus::Success);
Expand Down
23 changes: 18 additions & 5 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
.map(Cow::Owned)
.unwrap_or_else(|| Cow::Borrowed(&*CWD));

// Load environment variables not handled by Clap
let environment = EnvironmentOptions::new()?;

// The `--isolated` argument is deprecated on preview APIs, and warns on non-preview APIs.
let deprecated_isolated = if cli.top_level.global_args.isolated {
match &*cli.command {
Expand Down Expand Up @@ -169,12 +172,17 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
..
}) = &mut **command
{
let settings = GlobalSettings::resolve(&cli.top_level.global_args, filesystem.as_ref());
let settings = GlobalSettings::resolve(
&cli.top_level.global_args,
filesystem.as_ref(),
&environment,
);
let client_builder = BaseClientBuilder::new(
settings.network_settings.connectivity,
settings.network_settings.native_tls,
settings.network_settings.allow_insecure_host,
settings.preview,
environment.http_timeout,
)
.retries_from_env()?;
Some(
Expand Down Expand Up @@ -305,11 +313,12 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
.map(FilesystemOptions::from)
.combine(filesystem);

// Load environment variables not handled by Clap
let environment = EnvironmentOptions::new()?;

// Resolve the global settings.
let globals = GlobalSettings::resolve(&cli.top_level.global_args, filesystem.as_ref());
let globals = GlobalSettings::resolve(
&cli.top_level.global_args,
filesystem.as_ref(),
&environment,
);

// Resolve the cache settings.
let cache_settings = CacheSettings::resolve(*cli.top_level.cache_args, filesystem.as_ref());
Expand Down Expand Up @@ -441,6 +450,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
globals.network_settings.native_tls,
globals.network_settings.allow_insecure_host.clone(),
globals.preview,
environment.http_timeout,
)
.retries_from_env()?;

Expand All @@ -453,6 +463,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
args,
&cli.top_level.global_args,
filesystem.as_ref(),
&environment,
);
show_settings!(args);

Expand All @@ -475,6 +486,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
args,
&cli.top_level.global_args,
filesystem.as_ref(),
&environment,
);
show_settings!(args);

Expand All @@ -495,6 +507,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
args,
&cli.top_level.global_args,
filesystem.as_ref(),
&environment,
);
show_settings!(args);

Expand Down
Loading
Loading