diff --git a/Cargo.lock b/Cargo.lock index 9c57eef85..0d20151ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4583,6 +4583,7 @@ dependencies = [ "uv-pep440", "uv-pep508", "uv-pypi-types", + "uv-version", "uv-warnings", "version-ranges", "walkdir", @@ -4841,12 +4842,15 @@ dependencies = [ "futures", "itertools 0.13.0", "rustc-hash", + "tokio", "tracing", + "uv-build-backend", "uv-build-frontend", "uv-cache", "uv-client", "uv-configuration", "uv-distribution", + "uv-distribution-filename", "uv-distribution-types", "uv-git", "uv-install-wheel", @@ -4855,6 +4859,7 @@ dependencies = [ "uv-python", "uv-resolver", "uv-types", + "uv-version", ] [[package]] @@ -5590,6 +5595,7 @@ dependencies = [ "url", "uv-cache", "uv-configuration", + "uv-distribution-filename", "uv-distribution-types", "uv-git", "uv-normalize", diff --git a/crates/uv-bench/benches/uv.rs b/crates/uv-bench/benches/uv.rs index c7c71ceb2..204c341e3 100644 --- a/crates/uv-bench/benches/uv.rs +++ b/crates/uv-bench/benches/uv.rs @@ -87,7 +87,7 @@ mod resolver { use uv_client::RegistryClient; use uv_configuration::{ BuildOptions, Concurrency, ConfigSettings, Constraints, IndexStrategy, LowerBound, - SourceStrategy, + PreviewMode, SourceStrategy, }; use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution::DistributionDatabase; @@ -190,6 +190,7 @@ mod resolver { LowerBound::default(), sources, concurrency, + PreviewMode::Enabled, ); let markers = if universal { diff --git a/crates/uv-build-backend/Cargo.toml b/crates/uv-build-backend/Cargo.toml index d2e605702..2c8a6c76b 100644 --- a/crates/uv-build-backend/Cargo.toml +++ b/crates/uv-build-backend/Cargo.toml @@ -20,6 +20,7 @@ uv-normalize = { workspace = true } uv-pep440 = { workspace = true } uv-pep508 = { workspace = true } uv-pypi-types = { workspace = true } +uv-version = { workspace = true } uv-warnings = { workspace = true } csv = { workspace = true } diff --git a/crates/uv-build-backend/src/lib.rs b/crates/uv-build-backend/src/lib.rs index 20d63f290..c1f08455e 100644 --- a/crates/uv-build-backend/src/lib.rs +++ b/crates/uv-build-backend/src/lib.rs @@ -2,7 +2,7 @@ mod metadata; mod source_dist; mod wheel; -pub use metadata::PyProjectToml; +pub use metadata::{check_direct_build, PyProjectToml}; pub use source_dist::{build_source_dist, list_source_dist}; pub use wheel::{build_editable, build_wheel, list_wheel, metadata}; diff --git a/crates/uv-build-backend/src/metadata.rs b/crates/uv-build-backend/src/metadata.rs index 54a9f62d3..3ee691ce3 100644 --- a/crates/uv-build-backend/src/metadata.rs +++ b/crates/uv-build-backend/src/metadata.rs @@ -3,6 +3,7 @@ use itertools::Itertools; use serde::Deserialize; use std::collections::{BTreeMap, Bound}; use std::ffi::OsStr; +use std::fmt::Display; use std::path::{Path, PathBuf}; use std::str::FromStr; use tracing::{debug, trace}; @@ -50,6 +51,41 @@ pub enum ValidationError { InvalidSpdx(String, #[source] spdx::error::ParseError), } +/// Check if the build backend is matching the currently running uv version. +pub fn check_direct_build(source_tree: &Path, name: impl Display) -> bool { + let pyproject_toml: PyProjectToml = + match fs_err::read_to_string(source_tree.join("pyproject.toml")) + .map_err(|err| err.to_string()) + .and_then(|pyproject_toml| { + toml::from_str(&pyproject_toml).map_err(|err| err.to_string()) + }) { + Ok(pyproject_toml) => pyproject_toml, + Err(err) => { + debug!( + "Not using uv build backend direct build of {name}, no pyproject.toml: {err}" + ); + return false; + } + }; + match pyproject_toml + .check_build_system(uv_version::version()) + .as_slice() + { + // No warnings -> match + [] => true, + // Any warning -> no match + [first, others @ ..] => { + debug!( + "Not using uv build backend direct build of {name}, pyproject.toml does not match: {first}" + ); + for other in others { + trace!("Further uv build backend direct build of {name} mismatch: {other}"); + } + false + } + } +} + /// A `pyproject.toml` as specified in PEP 517. #[derive(Deserialize, Debug, Clone)] #[serde( diff --git a/crates/uv-build-frontend/src/lib.rs b/crates/uv-build-frontend/src/lib.rs index 57817a9a8..05b1b2cfc 100644 --- a/crates/uv-build-frontend/src/lib.rs +++ b/crates/uv-build-frontend/src/lib.rs @@ -251,7 +251,7 @@ impl SourceBuild { interpreter: &Interpreter, build_context: &impl BuildContext, source_build_context: SourceBuildContext, - version_id: Option, + version_id: Option<&str>, locations: &IndexLocations, source_strategy: SourceStrategy, config_settings: ConfigSettings, @@ -376,7 +376,7 @@ impl SourceBuild { build_context, package_name.as_ref(), package_version.as_ref(), - version_id.as_deref(), + version_id, locations, source_strategy, build_kind, @@ -401,7 +401,7 @@ impl SourceBuild { metadata_directory: None, package_name, package_version, - version_id, + version_id: version_id.map(ToString::to_string), environment_variables, modified_path, runner, diff --git a/crates/uv-dispatch/Cargo.toml b/crates/uv-dispatch/Cargo.toml index b61a7534b..3750ab24a 100644 --- a/crates/uv-dispatch/Cargo.toml +++ b/crates/uv-dispatch/Cargo.toml @@ -17,11 +17,13 @@ doctest = false workspace = true [dependencies] +uv-build-backend = { workspace = true } uv-build-frontend = { workspace = true } uv-cache = { workspace = true } uv-client = { workspace = true } uv-configuration = { workspace = true } uv-distribution = { workspace = true } +uv-distribution-filename = { workspace = true } uv-distribution-types = { workspace = true } uv-git = { workspace = true } uv-install-wheel = { workspace = true } @@ -30,9 +32,11 @@ uv-pypi-types = { workspace = true } uv-python = { workspace = true } uv-resolver = { workspace = true } uv-types = { workspace = true } +uv-version = { workspace = true } anyhow = { workspace = true } futures = { workspace = true } itertools = { workspace = true } rustc-hash = { workspace = true } +tokio = { workspace = true } tracing = { workspace = true } diff --git a/crates/uv-dispatch/src/lib.rs b/crates/uv-dispatch/src/lib.rs index 24c9619a1..6c1981b0f 100644 --- a/crates/uv-dispatch/src/lib.rs +++ b/crates/uv-dispatch/src/lib.rs @@ -9,17 +9,18 @@ use anyhow::{anyhow, Context, Result}; use futures::FutureExt; use itertools::Itertools; use rustc_hash::FxHashMap; -use tracing::{debug, instrument}; - +use tracing::{debug, instrument, trace}; +use uv_build_backend::check_direct_build; use uv_build_frontend::{SourceBuild, SourceBuildContext}; use uv_cache::Cache; use uv_client::RegistryClient; use uv_configuration::{ - BuildKind, BuildOptions, ConfigSettings, Constraints, IndexStrategy, LowerBound, Reinstall, - SourceStrategy, + BuildKind, BuildOptions, ConfigSettings, Constraints, IndexStrategy, LowerBound, PreviewMode, + Reinstall, SourceStrategy, }; use uv_configuration::{BuildOutput, Concurrency}; use uv_distribution::DistributionDatabase; +use uv_distribution_filename::DistFilename; use uv_distribution_types::{ CachedDist, DependencyMetadata, IndexCapabilities, IndexLocations, Name, Resolution, SourceDist, VersionOrUrlRef, @@ -57,6 +58,7 @@ pub struct BuildDispatch<'a> { bounds: LowerBound, sources: SourceStrategy, concurrency: Concurrency, + preview: PreviewMode, } impl<'a> BuildDispatch<'a> { @@ -79,6 +81,7 @@ impl<'a> BuildDispatch<'a> { bounds: LowerBound, sources: SourceStrategy, concurrency: Concurrency, + preview: PreviewMode, ) -> Self { Self { client, @@ -101,6 +104,7 @@ impl<'a> BuildDispatch<'a> { bounds, sources, concurrency, + preview, } } @@ -340,7 +344,7 @@ impl<'a> BuildContext for BuildDispatch<'a> { source: &'data Path, subdirectory: Option<&'data Path>, install_path: &'data Path, - version_id: Option, + version_id: Option<&'data str>, dist: Option<&'data SourceDist>, sources: SourceStrategy, build_kind: BuildKind, @@ -394,6 +398,73 @@ impl<'a> BuildContext for BuildDispatch<'a> { .await?; Ok(builder) } + + async fn direct_build<'data>( + &'data self, + source: &'data Path, + subdirectory: Option<&'data Path>, + output_dir: &'data Path, + build_kind: BuildKind, + version_id: Option<&'data str>, + ) -> Result> { + // Direct builds are a preview feature with the uv build backend. + if self.preview.is_disabled() { + trace!("Preview is disabled, not checking for direct build"); + return Ok(None); + } + + let source_tree = if let Some(subdir) = subdirectory { + source.join(subdir) + } else { + source.to_path_buf() + }; + + // Only perform the direct build if the backend is uv in a compatible version. + let source_tree_str = source_tree.display().to_string(); + let identifier = version_id.unwrap_or_else(|| &source_tree_str); + if !check_direct_build(&source_tree, identifier) { + trace!("Requirements for direct build not matched: {identifier}"); + return Ok(None); + } + + debug!("Performing direct build for {identifier}"); + + let output_dir = output_dir.to_path_buf(); + let filename = tokio::task::spawn_blocking(move || -> Result<_> { + let filename = match build_kind { + BuildKind::Wheel => { + let wheel = uv_build_backend::build_wheel( + &source_tree, + &output_dir, + None, + uv_version::version(), + )?; + DistFilename::WheelFilename(wheel) + } + BuildKind::Sdist => { + let source_dist = uv_build_backend::build_source_dist( + &source_tree, + &output_dir, + uv_version::version(), + )?; + DistFilename::SourceDistFilename(source_dist) + } + BuildKind::Editable => { + let wheel = uv_build_backend::build_editable( + &source_tree, + &output_dir, + None, + uv_version::version(), + )?; + DistFilename::WheelFilename(wheel) + } + }; + Ok(filename) + }) + .await??; + + Ok(Some(filename)) + } } /// Shared state used during resolution and installation. diff --git a/crates/uv-distribution/src/source/mod.rs b/crates/uv-distribution/src/source/mod.rs index 54cac0b7f..8df51913f 100644 --- a/crates/uv-distribution/src/source/mod.rs +++ b/crates/uv-distribution/src/source/mod.rs @@ -1803,27 +1803,47 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { fs::create_dir_all(&cache_shard) .await .map_err(Error::CacheWrite)?; - let disk_filename = self + // Try a direct build if that isn't disabled and the uv build backend is used. + let disk_filename = if let Some(name) = self .build_context - .setup_build( + .direct_build( source_root, subdirectory, - source_root, - Some(source.to_string()), - source.as_dist(), - source_strategy, + temp_dir.path(), if source.is_editable() { BuildKind::Editable } else { BuildKind::Wheel }, - BuildOutput::Debug, + Some(&source.to_string()), ) .await .map_err(Error::Build)? - .wheel(temp_dir.path()) - .await - .map_err(Error::Build)?; + { + // In the uv build backend, the normalized filename and the disk filename are the same. + name.to_string() + } else { + self.build_context + .setup_build( + source_root, + subdirectory, + source_root, + Some(&source.to_string()), + source.as_dist(), + source_strategy, + if source.is_editable() { + BuildKind::Editable + } else { + BuildKind::Wheel + }, + BuildOutput::Debug, + ) + .await + .map_err(Error::Build)? + .wheel(temp_dir.path()) + .await + .map_err(Error::Build)? + }; // Read the metadata from the wheel. let filename = WheelFilename::from_str(&disk_filename)?; @@ -1884,7 +1904,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { source_root, subdirectory, source_root, - Some(source.to_string()), + Some(&source.to_string()), source.as_dist(), source_strategy, if source.is_editable() { diff --git a/crates/uv-types/Cargo.toml b/crates/uv-types/Cargo.toml index 57ac4571a..192e50eb6 100644 --- a/crates/uv-types/Cargo.toml +++ b/crates/uv-types/Cargo.toml @@ -18,6 +18,7 @@ workspace = true [dependencies] uv-cache = { workspace = true } uv-configuration = { workspace = true } +uv-distribution-filename = { workspace = true } uv-distribution-types = { workspace = true } uv-git = { workspace = true } uv-normalize = { workspace = true } diff --git a/crates/uv-types/src/traits.rs b/crates/uv-types/src/traits.rs index b39e26138..68c17258a 100644 --- a/crates/uv-types/src/traits.rs +++ b/crates/uv-types/src/traits.rs @@ -1,5 +1,6 @@ use std::future::Future; use std::path::{Path, PathBuf}; +use uv_distribution_filename::DistFilename; use anyhow::Result; @@ -115,12 +116,27 @@ pub trait BuildContext { source: &'a Path, subdirectory: Option<&'a Path>, install_path: &'a Path, - version_id: Option, + version_id: Option<&'a str>, dist: Option<&'a SourceDist>, sources: SourceStrategy, build_kind: BuildKind, build_output: BuildOutput, ) -> impl Future> + 'a; + + /// Build by calling directly into the uv build backend without PEP 517, if possible. + /// + /// Checks if the source tree uses uv as build backend. If not, it returns `Ok(None)`, otherwise + /// it builds and returns the name of the built file. + /// + /// `version_id` is for error reporting only. + fn direct_build<'a>( + &'a self, + source: &'a Path, + subdirectory: Option<&'a Path>, + output_dir: &'a Path, + build_kind: BuildKind, + version_id: Option<&'a str>, + ) -> impl Future>> + 'a; } /// A wrapper for `uv_build::SourceBuild` to avoid cyclical crate dependencies. @@ -140,7 +156,9 @@ pub trait SourceBuildTrait { /// /// For PEP 517 builds, this calls `build_wheel`. /// - /// Returns the filename of the built wheel inside the given `wheel_dir`. + /// Returns the filename of the built wheel inside the given `wheel_dir`. The filename is a + /// string and not a `WheelFilename` because the on disk filename might not be normalized in the + /// same way as uv would. fn wheel<'a>(&'a self, wheel_dir: &'a Path) -> impl Future> + 'a; } diff --git a/crates/uv/src/commands/build_frontend.rs b/crates/uv/src/commands/build_frontend.rs index 42c67bd9f..2fa5f1d5c 100644 --- a/crates/uv/src/commands/build_frontend.rs +++ b/crates/uv/src/commands/build_frontend.rs @@ -8,7 +8,7 @@ use std::{fmt, io}; use anyhow::{Context, Result}; use owo_colors::OwoColorize; use thiserror::Error; -use tracing::{debug, instrument, trace}; +use tracing::instrument; use crate::commands::pip::operations; use crate::commands::project::find_requires_python; @@ -17,7 +17,7 @@ use crate::commands::ExitStatus; use crate::printer::Printer; use crate::settings::{ResolverSettings, ResolverSettingsRef}; use uv_auth::store_credentials; -use uv_build_backend::PyProjectToml; +use uv_build_backend::check_direct_build; use uv_cache::{Cache, CacheBucket}; use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ @@ -190,7 +190,7 @@ async fn build_impl( preview: PreviewMode, ) -> Result { if list && preview.is_disabled() { - // We need the fast path for list and that is preview only. + // We need the direct build for list and that is preview only. writeln!( printer.stderr(), "The `--list` option is only available in preview mode; add the `--preview` flag to use `--list`" @@ -576,6 +576,7 @@ async fn build_package( LowerBound::Allow, sources, concurrency, + preview, ); prepare_output_directory(&output_dir).await?; @@ -590,14 +591,17 @@ async fn build_package( return Err(Error::ListForcePep517); } - if !check_fast_path(source.path()) { + if !check_direct_build(source.path(), source.path().user_display()) { // TODO(konsti): Provide more context on what mismatched return Err(Error::ListNonUv); } BuildAction::List - } else if preview.is_enabled() && !force_pep517 && check_fast_path(source.path()) { - BuildAction::FastPath + } else if preview.is_enabled() + && !force_pep517 + && check_direct_build(source.path(), source.path().user_display()) + { + BuildAction::DirectBuild } else { BuildAction::Pep517 }; @@ -816,7 +820,7 @@ enum BuildAction { /// Only list the files that would be included, don't actually build. List, /// Build by calling directly into the build backend. - FastPath, + DirectBuild, /// Build through the PEP 517 hooks. Pep517, } @@ -826,14 +830,14 @@ impl BuildAction { fn force_build(self) -> Self { match self { // List is only available for the uv build backend - Self::List => Self::FastPath, - Self::FastPath => Self::FastPath, + Self::List => Self::DirectBuild, + Self::DirectBuild => Self::DirectBuild, Self::Pep517 => Self::Pep517, } } } -/// Build a source distribution, either through PEP 517 or through the fast path. +/// Build a source distribution, either through PEP 517 or through a direct build. #[instrument(skip_all)] async fn build_sdist( source_tree: &Path, @@ -864,7 +868,7 @@ async fn build_sdist( file_list, } } - BuildAction::FastPath => { + BuildAction::DirectBuild => { writeln!( printer.stderr(), "{}", @@ -911,7 +915,7 @@ async fn build_sdist( source_tree, subdirectory, source.path(), - version_id.map(ToString::to_string), + version_id, dist, sources, BuildKind::Sdist, @@ -932,7 +936,7 @@ async fn build_sdist( Ok(build_result) } -/// Build a wheel, either through PEP 517 or through the fast path. +/// Build a wheel, either through PEP 517 or through a direct build. #[instrument(skip_all)] async fn build_wheel( source_tree: &Path, @@ -964,7 +968,7 @@ async fn build_wheel( file_list, } } - BuildAction::FastPath => { + BuildAction::DirectBuild => { writeln!( printer.stderr(), "{}", @@ -1008,7 +1012,7 @@ async fn build_wheel( source_tree, subdirectory, source.path(), - version_id.map(ToString::to_string), + version_id, dist, sources, BuildKind::Wheel, @@ -1246,33 +1250,3 @@ impl BuildPlan { }) } } - -/// Check if the build backend is matching the currently running uv version. -fn check_fast_path(source_tree: &Path) -> bool { - let pyproject_toml: PyProjectToml = - match fs_err::read_to_string(source_tree.join("pyproject.toml")) - .map_err(anyhow::Error::from) - .and_then(|pyproject_toml| Ok(toml::from_str(&pyproject_toml)?)) - { - Ok(pyproject_toml) => pyproject_toml, - Err(err) => { - debug!("Not using uv build backend fast path, no pyproject.toml: {err}"); - return false; - } - }; - match pyproject_toml - .check_build_system(uv_version::version()) - .as_slice() - { - // No warnings -> match - [] => true, - // Any warning -> no match - [first, others @ ..] => { - debug!("Not using uv build backend fast path, pyproject.toml does not match: {first}"); - for other in others { - trace!("Further uv build backend fast path mismatch: {other}"); - } - false - } - } -} diff --git a/crates/uv/src/commands/pip/compile.rs b/crates/uv/src/commands/pip/compile.rs index 1cfb34208..65b437be0 100644 --- a/crates/uv/src/commands/pip/compile.rs +++ b/crates/uv/src/commands/pip/compile.rs @@ -11,7 +11,7 @@ use uv_cache::Cache; use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ BuildOptions, Concurrency, ConfigSettings, Constraints, ExtrasSpecification, IndexStrategy, - LowerBound, NoBinary, NoBuild, Reinstall, SourceStrategy, TrustedHost, Upgrade, + LowerBound, NoBinary, NoBuild, PreviewMode, Reinstall, SourceStrategy, TrustedHost, Upgrade, }; use uv_configuration::{KeyringProviderType, TargetTriple}; use uv_dispatch::{BuildDispatch, SharedState}; @@ -96,6 +96,7 @@ pub(crate) async fn pip_compile( quiet: bool, cache: Cache, printer: Printer, + preview: PreviewMode, ) -> Result { // If the user requests `extras` but does not provide a valid source (e.g., a `pyproject.toml`), // return an error. @@ -354,6 +355,7 @@ pub(crate) async fn pip_compile( LowerBound::Warn, sources, concurrency, + preview, ); let options = OptionsBuilder::new() diff --git a/crates/uv/src/commands/pip/install.rs b/crates/uv/src/commands/pip/install.rs index f4ee781fd..e156bf81d 100644 --- a/crates/uv/src/commands/pip/install.rs +++ b/crates/uv/src/commands/pip/install.rs @@ -8,7 +8,7 @@ use uv_cache::Cache; use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ BuildOptions, Concurrency, ConfigSettings, Constraints, ExtrasSpecification, HashCheckingMode, - IndexStrategy, LowerBound, Reinstall, SourceStrategy, TrustedHost, Upgrade, + IndexStrategy, LowerBound, PreviewMode, Reinstall, SourceStrategy, TrustedHost, Upgrade, }; use uv_configuration::{KeyringProviderType, TargetTriple}; use uv_dispatch::{BuildDispatch, SharedState}; @@ -85,6 +85,7 @@ pub(crate) async fn pip_install( cache: Cache, dry_run: bool, printer: Printer, + preview: PreviewMode, ) -> anyhow::Result { let start = std::time::Instant::now(); @@ -383,6 +384,7 @@ pub(crate) async fn pip_install( LowerBound::Warn, sources, concurrency, + preview, ); let options = OptionsBuilder::new() diff --git a/crates/uv/src/commands/pip/sync.rs b/crates/uv/src/commands/pip/sync.rs index 74b0389b0..21f932277 100644 --- a/crates/uv/src/commands/pip/sync.rs +++ b/crates/uv/src/commands/pip/sync.rs @@ -8,7 +8,7 @@ use uv_cache::Cache; use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ BuildOptions, Concurrency, ConfigSettings, Constraints, ExtrasSpecification, HashCheckingMode, - IndexStrategy, LowerBound, Reinstall, SourceStrategy, TrustedHost, Upgrade, + IndexStrategy, LowerBound, PreviewMode, Reinstall, SourceStrategy, TrustedHost, Upgrade, }; use uv_configuration::{KeyringProviderType, TargetTriple}; use uv_dispatch::{BuildDispatch, SharedState}; @@ -74,6 +74,7 @@ pub(crate) async fn pip_sync( cache: Cache, dry_run: bool, printer: Printer, + preview: PreviewMode, ) -> Result { let client_builder = BaseClientBuilder::new() .connectivity(connectivity) @@ -326,6 +327,7 @@ pub(crate) async fn pip_sync( LowerBound::Warn, sources, concurrency, + preview, ); // Determine the set of installed packages. diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs index 8143df356..c0bb80796 100644 --- a/crates/uv/src/commands/project/add.rs +++ b/crates/uv/src/commands/project/add.rs @@ -15,8 +15,8 @@ use uv_cache_key::RepositoryUrl; use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ Concurrency, Constraints, DevGroupsManifest, DevGroupsSpecification, DevMode, EditableMode, - ExtrasSpecification, GroupsSpecification, InstallOptions, LowerBound, SourceStrategy, - TrustedHost, + ExtrasSpecification, GroupsSpecification, InstallOptions, LowerBound, PreviewMode, + SourceStrategy, TrustedHost, }; use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution::DistributionDatabase; @@ -85,6 +85,7 @@ pub(crate) async fn add( no_config: bool, cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result { for source in &requirements { match source { @@ -340,6 +341,7 @@ pub(crate) async fn add( bounds, sources, concurrency, + preview, ); // Resolve any unnamed requirements. @@ -681,6 +683,7 @@ pub(crate) async fn add( allow_insecure_host, cache, printer, + preview, ) .await { @@ -724,6 +727,7 @@ async fn lock_and_sync( allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result<(), ProjectError> { let mode = if locked { LockMode::Locked(environment.interpreter()) @@ -744,6 +748,7 @@ async fn lock_and_sync( allow_insecure_host, cache, printer, + preview, ) .await? .into_lock(); @@ -862,6 +867,7 @@ async fn lock_and_sync( allow_insecure_host, cache, printer, + preview, ) .await? .into_lock(); @@ -928,6 +934,7 @@ async fn lock_and_sync( allow_insecure_host, cache, printer, + preview, ) .await?; diff --git a/crates/uv/src/commands/project/environment.rs b/crates/uv/src/commands/project/environment.rs index 91f8f99b9..e4dd28ec2 100644 --- a/crates/uv/src/commands/project/environment.rs +++ b/crates/uv/src/commands/project/environment.rs @@ -9,7 +9,7 @@ use crate::settings::ResolverInstallerSettings; use uv_cache::{Cache, CacheBucket}; use uv_cache_key::{cache_digest, hash_digest}; use uv_client::Connectivity; -use uv_configuration::{Concurrency, TrustedHost}; +use uv_configuration::{Concurrency, PreviewMode, TrustedHost}; use uv_dispatch::SharedState; use uv_distribution_types::{Name, Resolution}; use uv_python::{Interpreter, PythonEnvironment}; @@ -41,6 +41,7 @@ impl CachedEnvironment { allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result { // When caching, always use the base interpreter, rather than that of the virtual // environment. @@ -72,6 +73,7 @@ impl CachedEnvironment { allow_insecure_host, cache, printer, + preview, ) .await?, ); @@ -125,6 +127,7 @@ impl CachedEnvironment { allow_insecure_host, cache, printer, + preview, ) .await?; diff --git a/crates/uv/src/commands/project/export.rs b/crates/uv/src/commands/project/export.rs index 5f8ac0263..4368af06c 100644 --- a/crates/uv/src/commands/project/export.rs +++ b/crates/uv/src/commands/project/export.rs @@ -10,7 +10,7 @@ use uv_cache::Cache; use uv_client::Connectivity; use uv_configuration::{ Concurrency, DevGroupsSpecification, EditableMode, ExportFormat, ExtrasSpecification, - InstallOptions, LowerBound, TrustedHost, + InstallOptions, LowerBound, PreviewMode, TrustedHost, }; use uv_dispatch::SharedState; use uv_normalize::PackageName; @@ -58,6 +58,7 @@ pub(crate) async fn export( quiet: bool, cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result { // Identify the project. let project = if frozen { @@ -145,6 +146,7 @@ pub(crate) async fn export( allow_insecure_host, cache, printer, + preview, ) .await { diff --git a/crates/uv/src/commands/project/lock.rs b/crates/uv/src/commands/project/lock.rs index a19446006..f7afd49b8 100644 --- a/crates/uv/src/commands/project/lock.rs +++ b/crates/uv/src/commands/project/lock.rs @@ -11,8 +11,8 @@ use tracing::debug; use uv_cache::Cache; use uv_client::{Connectivity, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ - Concurrency, Constraints, ExtrasSpecification, LowerBound, Reinstall, SourceStrategy, - TrustedHost, Upgrade, + Concurrency, Constraints, ExtrasSpecification, LowerBound, PreviewMode, Reinstall, + SourceStrategy, TrustedHost, Upgrade, }; use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution::DistributionDatabase; @@ -89,6 +89,7 @@ pub(crate) async fn lock( no_config: bool, cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> anyhow::Result { // Find the project requirements. let workspace = Workspace::discover(project_dir, &DiscoveryOptions::default()).await?; @@ -142,6 +143,7 @@ pub(crate) async fn lock( allow_insecure_host, cache, printer, + preview, ) .await { @@ -201,6 +203,7 @@ pub(super) async fn do_safe_lock( allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result { match mode { LockMode::Frozen => { @@ -231,6 +234,7 @@ pub(super) async fn do_safe_lock( allow_insecure_host, cache, printer, + preview, ) .await?; @@ -270,6 +274,7 @@ pub(super) async fn do_safe_lock( allow_insecure_host, cache, printer, + preview, ) .await?; @@ -300,6 +305,7 @@ async fn do_lock( allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result { let start = std::time::Instant::now(); @@ -502,6 +508,7 @@ async fn do_lock( bounds, sources, concurrency, + preview, ); let database = DistributionDatabase::new(&client, &build_dispatch, concurrency.downloads); diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs index dd5f2be77..99ef9e6aa 100644 --- a/crates/uv/src/commands/project/mod.rs +++ b/crates/uv/src/commands/project/mod.rs @@ -9,7 +9,7 @@ use uv_cache::Cache; use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ Concurrency, Constraints, DevGroupsManifest, DevGroupsSpecification, ExtrasSpecification, - GroupsSpecification, LowerBound, Reinstall, TrustedHost, Upgrade, + GroupsSpecification, LowerBound, PreviewMode, Reinstall, TrustedHost, Upgrade, }; use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution::DistributionDatabase; @@ -896,6 +896,7 @@ pub(crate) async fn resolve_names( allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result, uv_requirements::Error> { // Partition the requirements into named and unnamed requirements. let (mut requirements, unnamed): (Vec<_>, Vec<_>) = @@ -991,6 +992,7 @@ pub(crate) async fn resolve_names( LowerBound::Allow, *sources, concurrency, + preview, ); // Resolve the unnamed requirements. @@ -1045,6 +1047,7 @@ pub(crate) async fn resolve_environment<'a>( allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result { warn_on_requirements_txt_setting(&spec.requirements, settings); @@ -1172,6 +1175,7 @@ pub(crate) async fn resolve_environment<'a>( LowerBound::Allow, sources, concurrency, + preview, ); // Resolve the requirements. @@ -1218,6 +1222,7 @@ pub(crate) async fn sync_environment( allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result { let InstallerSettingsRef { index_locations, @@ -1305,6 +1310,7 @@ pub(crate) async fn sync_environment( LowerBound::Allow, sources, concurrency, + preview, ); // Sync the environment. @@ -1370,6 +1376,7 @@ pub(crate) async fn update_environment( allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result { warn_on_requirements_txt_setting(&spec, settings.as_ref().into()); @@ -1510,6 +1517,7 @@ pub(crate) async fn update_environment( LowerBound::Allow, *sources, concurrency, + preview, ); // Resolve the requirements. diff --git a/crates/uv/src/commands/project/remove.rs b/crates/uv/src/commands/project/remove.rs index ae1bf0e77..3aae268c7 100644 --- a/crates/uv/src/commands/project/remove.rs +++ b/crates/uv/src/commands/project/remove.rs @@ -8,7 +8,7 @@ use uv_cache::Cache; use uv_client::Connectivity; use uv_configuration::{ Concurrency, DevGroupsManifest, EditableMode, ExtrasSpecification, InstallOptions, LowerBound, - TrustedHost, + PreviewMode, TrustedHost, }; use uv_dispatch::SharedState; use uv_fs::Simplified; @@ -54,6 +54,7 @@ pub(crate) async fn remove( no_config: bool, cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result { let target = if let Some(script) = script { // If we found a PEP 723 script and the user provided a project-only setting, warn. @@ -231,6 +232,7 @@ pub(crate) async fn remove( allow_insecure_host, cache, printer, + preview, ) .await { @@ -285,6 +287,7 @@ pub(crate) async fn remove( allow_insecure_host, cache, printer, + preview, ) .await { diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs index 16028c13e..9ce55a3c7 100644 --- a/crates/uv/src/commands/project/run.rs +++ b/crates/uv/src/commands/project/run.rs @@ -18,7 +18,7 @@ use uv_cli::ExternalCommand; use uv_client::{BaseClientBuilder, Connectivity}; use uv_configuration::{ Concurrency, DevGroupsSpecification, EditableMode, ExtrasSpecification, GroupsSpecification, - InstallOptions, LowerBound, SourceStrategy, TrustedHost, + InstallOptions, LowerBound, PreviewMode, SourceStrategy, TrustedHost, }; use uv_dispatch::SharedState; use uv_distribution::LoweredRequirement; @@ -86,6 +86,7 @@ pub(crate) async fn run( printer: Printer, env_file: Vec, no_env_file: bool, + preview: PreviewMode, ) -> anyhow::Result { // These cases seem quite complex because (in theory) they should change the "current package". // Let's ban them entirely for now. @@ -341,6 +342,7 @@ pub(crate) async fn run( allow_insecure_host, cache, printer, + preview, ) .await; @@ -662,6 +664,7 @@ pub(crate) async fn run( allow_insecure_host, cache, printer, + preview, ) .await { @@ -742,6 +745,7 @@ pub(crate) async fn run( allow_insecure_host, cache, printer, + preview, ) .await { @@ -893,6 +897,7 @@ pub(crate) async fn run( allow_insecure_host, cache, printer, + preview, ) .await; diff --git a/crates/uv/src/commands/project/sync.rs b/crates/uv/src/commands/project/sync.rs index 6d9c3a71c..f1e385dc7 100644 --- a/crates/uv/src/commands/project/sync.rs +++ b/crates/uv/src/commands/project/sync.rs @@ -10,7 +10,7 @@ use uv_cache::Cache; use uv_client::{Connectivity, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ Concurrency, Constraints, DevGroupsManifest, DevGroupsSpecification, EditableMode, - ExtrasSpecification, HashCheckingMode, InstallOptions, LowerBound, TrustedHost, + ExtrasSpecification, HashCheckingMode, InstallOptions, LowerBound, PreviewMode, TrustedHost, }; use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution_types::{ @@ -67,6 +67,7 @@ pub(crate) async fn sync( no_config: bool, cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result { // Identify the project. let project = if frozen { @@ -156,6 +157,7 @@ pub(crate) async fn sync( allow_insecure_host, cache, printer, + preview, ) .await { @@ -231,6 +233,7 @@ pub(crate) async fn sync( allow_insecure_host, cache, printer, + preview, ) .await { @@ -265,6 +268,7 @@ pub(super) async fn do_sync( allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result<(), ProjectError> { // Use isolated state for universal resolution. When resolving, we don't enforce that the // prioritized distributions match the current platform. So if we lock here, then try to @@ -425,6 +429,7 @@ pub(super) async fn do_sync( bounds, sources, concurrency, + preview, ); let site_packages = SitePackages::from_environment(venv)?; diff --git a/crates/uv/src/commands/project/tree.rs b/crates/uv/src/commands/project/tree.rs index dfc674004..77bffa57a 100644 --- a/crates/uv/src/commands/project/tree.rs +++ b/crates/uv/src/commands/project/tree.rs @@ -8,7 +8,7 @@ use uv_cache::{Cache, Refresh}; use uv_cache_info::Timestamp; use uv_client::{Connectivity, RegistryClientBuilder}; use uv_configuration::{ - Concurrency, DevGroupsSpecification, LowerBound, TargetTriple, TrustedHost, + Concurrency, DevGroupsSpecification, LowerBound, PreviewMode, TargetTriple, TrustedHost, }; use uv_dispatch::SharedState; use uv_distribution_types::IndexCapabilities; @@ -58,6 +58,7 @@ pub(crate) async fn tree( no_config: bool, cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result { // Find the project requirements. let workspace = Workspace::discover(project_dir, &DiscoveryOptions::default()).await?; @@ -121,6 +122,7 @@ pub(crate) async fn tree( allow_insecure_host, cache, printer, + preview, ) .await { diff --git a/crates/uv/src/commands/tool/install.rs b/crates/uv/src/commands/tool/install.rs index f51854fa7..f36d9a19b 100644 --- a/crates/uv/src/commands/tool/install.rs +++ b/crates/uv/src/commands/tool/install.rs @@ -8,7 +8,7 @@ use tracing::{debug, trace}; use uv_cache::{Cache, Refresh}; use uv_cache_info::Timestamp; use uv_client::{BaseClientBuilder, Connectivity}; -use uv_configuration::{Concurrency, Reinstall, TrustedHost, Upgrade}; +use uv_configuration::{Concurrency, PreviewMode, Reinstall, TrustedHost, Upgrade}; use uv_dispatch::SharedState; use uv_distribution_types::{NameRequirementSpecification, UnresolvedRequirementSpecification}; use uv_normalize::PackageName; @@ -61,6 +61,7 @@ pub(crate) async fn install( allow_insecure_host: &[TrustedHost], cache: Cache, printer: Printer, + preview: PreviewMode, ) -> Result { let client_builder = BaseClientBuilder::new() .connectivity(connectivity) @@ -128,6 +129,7 @@ pub(crate) async fn install( allow_insecure_host, &cache, printer, + preview, ) .await? .pop() @@ -202,6 +204,7 @@ pub(crate) async fn install( allow_insecure_host, &cache, printer, + preview, ) .await? .pop() @@ -266,6 +269,7 @@ pub(crate) async fn install( allow_insecure_host, &cache, printer, + preview, ) .await?, ); @@ -291,6 +295,7 @@ pub(crate) async fn install( allow_insecure_host, &cache, printer, + preview, ) .await?; @@ -422,6 +427,7 @@ pub(crate) async fn install( allow_insecure_host, &cache, printer, + preview, ) .await { @@ -456,6 +462,7 @@ pub(crate) async fn install( allow_insecure_host, &cache, printer, + preview, ) .await { @@ -490,6 +497,7 @@ pub(crate) async fn install( allow_insecure_host, &cache, printer, + preview, ) .await .inspect_err(|_| { diff --git a/crates/uv/src/commands/tool/run.rs b/crates/uv/src/commands/tool/run.rs index c3797bee9..8d97bc104 100644 --- a/crates/uv/src/commands/tool/run.rs +++ b/crates/uv/src/commands/tool/run.rs @@ -14,7 +14,7 @@ use uv_cache::{Cache, Refresh}; use uv_cache_info::Timestamp; use uv_cli::ExternalCommand; use uv_client::{BaseClientBuilder, Connectivity}; -use uv_configuration::{Concurrency, TrustedHost}; +use uv_configuration::{Concurrency, PreviewMode, TrustedHost}; use uv_dispatch::SharedState; use uv_distribution_types::{Name, UnresolvedRequirementSpecification}; use uv_installer::{SatisfiesResult, SitePackages}; @@ -84,6 +84,7 @@ pub(crate) async fn run( allow_insecure_host: &[TrustedHost], cache: Cache, printer: Printer, + preview: PreviewMode, ) -> anyhow::Result { let Some(command) = command else { // When a command isn't provided, we'll show a brief help including available tools @@ -128,6 +129,7 @@ pub(crate) async fn run( allow_insecure_host, &cache, printer, + preview, ) .await; @@ -446,6 +448,7 @@ async fn get_or_create_environment( allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result<(Requirement, PythonEnvironment), ProjectError> { let client_builder = BaseClientBuilder::new() .connectivity(connectivity) @@ -529,6 +532,7 @@ async fn get_or_create_environment( allow_insecure_host, cache, printer, + preview, ) .await? .pop() @@ -560,6 +564,7 @@ async fn get_or_create_environment( allow_insecure_host, cache, printer, + preview, ) .await?, ); @@ -638,6 +643,7 @@ async fn get_or_create_environment( allow_insecure_host, cache, printer, + preview, ) .await?; diff --git a/crates/uv/src/commands/tool/upgrade.rs b/crates/uv/src/commands/tool/upgrade.rs index f35489864..088803976 100644 --- a/crates/uv/src/commands/tool/upgrade.rs +++ b/crates/uv/src/commands/tool/upgrade.rs @@ -7,7 +7,7 @@ use tracing::debug; use uv_cache::Cache; use uv_client::{BaseClientBuilder, Connectivity}; -use uv_configuration::{Concurrency, TrustedHost}; +use uv_configuration::{Concurrency, PreviewMode, TrustedHost}; use uv_dispatch::SharedState; use uv_fs::CWD; use uv_normalize::PackageName; @@ -48,6 +48,7 @@ pub(crate) async fn upgrade( allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, + preview: PreviewMode, ) -> Result { let installed_tools = InstalledTools::from_settings()?.init()?; let _lock = installed_tools.lock().await?; @@ -129,6 +130,7 @@ pub(crate) async fn upgrade( concurrency, native_tls, allow_insecure_host, + preview, ) .await; @@ -219,6 +221,7 @@ async fn upgrade_tool( concurrency: Concurrency, native_tls: bool, allow_insecure_host: &[TrustedHost], + preview: PreviewMode, ) -> Result { // Ensure the tool is installed. let existing_tool_receipt = match installed_tools.get_tool_receipt(name) { @@ -301,6 +304,7 @@ async fn upgrade_tool( allow_insecure_host, cache, printer, + preview, ) .await?; @@ -319,6 +323,7 @@ async fn upgrade_tool( allow_insecure_host, cache, printer, + preview, ) .await?; @@ -344,6 +349,7 @@ async fn upgrade_tool( allow_insecure_host, cache, printer, + preview, ) .await?; diff --git a/crates/uv/src/commands/venv.rs b/crates/uv/src/commands/venv.rs index c38de43ea..f9f6b1698 100644 --- a/crates/uv/src/commands/venv.rs +++ b/crates/uv/src/commands/venv.rs @@ -13,7 +13,7 @@ use uv_cache::Cache; use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ BuildOptions, Concurrency, ConfigSettings, Constraints, IndexStrategy, KeyringProviderType, - LowerBound, NoBinary, NoBuild, SourceStrategy, TrustedHost, + LowerBound, NoBinary, NoBuild, PreviewMode, SourceStrategy, TrustedHost, }; use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution_types::{DependencyMetadata, Index, IndexLocations}; @@ -65,6 +65,7 @@ pub(crate) async fn venv( cache: &Cache, printer: Printer, relocatable: bool, + preview: PreviewMode, ) -> Result { match venv_impl( project_dir, @@ -92,6 +93,7 @@ pub(crate) async fn venv( cache, printer, relocatable, + preview, ) .await { @@ -150,6 +152,7 @@ async fn venv_impl( cache: &Cache, printer: Printer, relocatable: bool, + preview: PreviewMode, ) -> miette::Result { let project = if no_project { None @@ -333,6 +336,7 @@ async fn venv_impl( LowerBound::Allow, sources, concurrency, + preview, ); // Resolve the seed packages. diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index 20216bc18..ff7f60465 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -374,6 +374,7 @@ async fn run(mut cli: Cli) -> Result { globals.quiet, cache, printer, + globals.preview, ) .await } @@ -445,6 +446,7 @@ async fn run(mut cli: Cli) -> Result { cache, args.dry_run, printer, + globals.preview, ) .await } @@ -535,6 +537,7 @@ async fn run(mut cli: Cli) -> Result { cache, args.dry_run, printer, + globals.preview, ) .await } @@ -807,6 +810,7 @@ async fn run(mut cli: Cli) -> Result { &cache, printer, args.relocatable, + globals.preview, ) .await } @@ -927,6 +931,7 @@ async fn run(mut cli: Cli) -> Result { &globals.allow_insecure_host, cache, printer, + globals.preview, ) .await } @@ -991,6 +996,7 @@ async fn run(mut cli: Cli) -> Result { &globals.allow_insecure_host, cache, printer, + globals.preview, )) .await } @@ -1037,6 +1043,7 @@ async fn run(mut cli: Cli) -> Result { &globals.allow_insecure_host, &cache, printer, + globals.preview, )) .await } @@ -1375,6 +1382,7 @@ async fn run_project( printer, args.env_file, args.no_env_file, + globals.preview, )) .await } @@ -1414,6 +1422,7 @@ async fn run_project( no_config, &cache, printer, + globals.preview, ) .await } @@ -1445,6 +1454,7 @@ async fn run_project( no_config, &cache, printer, + globals.preview, ) .await } @@ -1500,6 +1510,7 @@ async fn run_project( no_config, &cache, printer, + globals.preview, )) .await } @@ -1544,6 +1555,7 @@ async fn run_project( no_config, &cache, printer, + globals.preview, )) .await } @@ -1581,6 +1593,7 @@ async fn run_project( no_config, &cache, printer, + globals.preview, ) .await } @@ -1620,6 +1633,7 @@ async fn run_project( globals.quiet, &cache, printer, + globals.preview, ) .await }