diff --git a/crates/uv-client/src/registry_client.rs b/crates/uv-client/src/registry_client.rs index 7f6ff4696..40127e044 100644 --- a/crates/uv-client/src/registry_client.rs +++ b/crates/uv-client/src/registry_client.rs @@ -850,9 +850,10 @@ impl MediaType { } } -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] pub enum Connectivity { /// Allow access to the network. + #[default] Online, /// Do not allow access to the network. diff --git a/crates/uv/src/cli.rs b/crates/uv/src/cli.rs index 2f47172fe..8f1d3fa0b 100644 --- a/crates/uv/src/cli.rs +++ b/crates/uv/src/cli.rs @@ -1840,6 +1840,13 @@ pub(crate) struct RunArgs { #[arg(long)] pub(crate) with: Vec, + /// Run offline, i.e., without accessing the network. + #[arg(global = true, long, overrides_with("no_offline"))] + pub(crate) offline: bool, + + #[arg(long, overrides_with("offline"), hide = true)] + pub(crate) no_offline: bool, + /// The Python interpreter to use to build the run environment. /// /// By default, `uv` uses the virtual environment in the current working directory or any parent @@ -1954,4 +1961,11 @@ pub(crate) struct ToolRunArgs { group = "discovery" )] pub(crate) python: Option, + + /// Run offline, i.e., without accessing the network. + #[arg(global = true, long, overrides_with("no_offline"))] + pub(crate) offline: bool, + + #[arg(long, overrides_with("offline"), hide = true)] + pub(crate) no_offline: bool, } diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs index de6c1f326..8817a946e 100644 --- a/crates/uv/src/commands/project/mod.rs +++ b/crates/uv/src/commands/project/mod.rs @@ -11,7 +11,7 @@ use platform_tags::Tags; use pypi_types::Yanked; use tracing::debug; use uv_cache::Cache; -use uv_client::{BaseClientBuilder, RegistryClient, RegistryClientBuilder}; +use uv_client::{BaseClientBuilder, Connectivity, RegistryClient, RegistryClientBuilder}; use uv_configuration::{ Concurrency, ConfigSettings, Constraints, NoBinary, NoBuild, Overrides, PreviewMode, Reinstall, SetupPyStrategy, @@ -468,11 +468,12 @@ pub(crate) async fn update_environment( venv: PythonEnvironment, requirements: &[RequirementsSource], preview: PreviewMode, + connectivity: Connectivity, cache: &Cache, printer: Printer, ) -> Result { // TODO(zanieb): Support client configuration - let client_builder = BaseClientBuilder::default(); + let client_builder = BaseClientBuilder::default().connectivity(connectivity); // Read all requirements from the provided sources. // TODO(zanieb): Consider allowing constraints and extras @@ -524,6 +525,7 @@ pub(crate) async fn update_environment( // Initialize the registry client. // TODO(zanieb): Support client options e.g. offline, tls, etc. let client = RegistryClientBuilder::new(cache.clone()) + .connectivity(connectivity) .markers(markers) .platform(venv.interpreter().platform()) .build(); diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs index b23c70808..5aa9904d8 100644 --- a/crates/uv/src/commands/project/run.rs +++ b/crates/uv/src/commands/project/run.rs @@ -8,6 +8,7 @@ use tokio::process::Command; use tracing::debug; use uv_cache::Cache; +use uv_client::Connectivity; use uv_configuration::PreviewMode; use uv_interpreter::{PythonEnvironment, SystemPython}; use uv_requirements::{ProjectWorkspace, RequirementsSource}; @@ -25,6 +26,7 @@ pub(crate) async fn run( python: Option, isolated: bool, preview: PreviewMode, + connectivity: Connectivity, cache: &Cache, printer: Printer, ) -> Result { @@ -60,8 +62,15 @@ pub(crate) async fn run( // Install the project requirements. Some( - project::update_environment(venv, &project.requirements(), preview, cache, printer) - .await?, + project::update_environment( + venv, + &project.requirements(), + preview, + connectivity, + cache, + printer, + ) + .await?, ) }; @@ -101,7 +110,10 @@ pub(crate) async fn run( )?; // Install the ephemeral requirements. - Some(project::update_environment(venv, &requirements, preview, cache, printer).await?) + Some( + project::update_environment(venv, &requirements, preview, connectivity, cache, printer) + .await?, + ) }; // Construct the command diff --git a/crates/uv/src/commands/tool/run.rs b/crates/uv/src/commands/tool/run.rs index 7b82f6d2c..7e7b63e47 100644 --- a/crates/uv/src/commands/tool/run.rs +++ b/crates/uv/src/commands/tool/run.rs @@ -8,6 +8,7 @@ use tokio::process::Command; use tracing::debug; use uv_cache::Cache; +use uv_client::Connectivity; use uv_configuration::PreviewMode; use uv_interpreter::{PythonEnvironment, SystemPython}; use uv_requirements::RequirementsSource; @@ -25,6 +26,7 @@ pub(crate) async fn run( python: Option, _isolated: bool, preview: PreviewMode, + connectivity: Connectivity, cache: &Cache, printer: Printer, ) -> Result { @@ -65,7 +67,7 @@ pub(crate) async fn run( // Install the ephemeral requirements. let ephemeral_env = - Some(update_environment(venv, &requirements, preview, cache, printer).await?); + Some(update_environment(venv, &requirements, preview, connectivity, cache, printer).await?); // TODO(zanieb): Determine the command via the package entry points let command = target; diff --git a/crates/uv/src/main.rs b/crates/uv/src/main.rs index 24b31701a..149bf7820 100644 --- a/crates/uv/src/main.rs +++ b/crates/uv/src/main.rs @@ -12,6 +12,7 @@ use owo_colors::OwoColorize; use tracing::instrument; use uv_cache::Cache; +use uv_client::Connectivity; use uv_requirements::RequirementsSource; use uv_workspace::Combine; @@ -565,6 +566,7 @@ async fn run() -> Result { args.python, globals.isolated, globals.preview, + args.connectivity, &cache, printer, ) @@ -603,12 +605,18 @@ async fn run() -> Result { Commands::Tool(ToolNamespace { command: ToolCommand::Run(args), }) => { + let connectivity = if args.offline { + Connectivity::Offline + } else { + Connectivity::Online + }; commands::run_tool( args.target, args.args, args.python, globals.isolated, globals.preview, + connectivity, &cache, printer, ) diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index cf046df27..ea377ff3d 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -94,6 +94,10 @@ pub(crate) struct RunSettings { pub(crate) args: Vec, pub(crate) with: Vec, pub(crate) python: Option, + + // Shared settings. + // TODO(zanieb): should be moved to a global setting + pub(crate) connectivity: Connectivity, } impl RunSettings { @@ -105,6 +109,8 @@ impl RunSettings { args, with, python, + offline, + no_offline, } = args; Self { @@ -113,6 +119,16 @@ impl RunSettings { args, with, python, + // Shared settings + connectivity: flag(offline, no_offline) + .map(|offline| { + if offline { + Connectivity::Offline + } else { + Connectivity::Online + } + }) + .unwrap_or_default(), } } }