From bfb4bc2aebe1dbff9aeba84d0c96e3ffcbf0ddd1 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Fri, 25 Jul 2025 11:01:57 -0500 Subject: [PATCH] Split preview mode into separate feature flags (#14823) I think this would give us better hygiene than a global flag. It makes it easier for users to opt-in to overlapping features, such as Python upgrades and Python bin installations and to disable warnings for preview mode without opting in to a bunch of other features. In general, I want to reduce the burden for putting something under preview. The `--preview` and `--no-preview` flags are retained as global overrides. A new `--preview-features` option is added which accepts comma separated features or can be passed multiple times, e.g., `--preview-features add-bounds,pylock`. There's a `UV_PREVIEW_FEATURES` environment variable for that option (I'm not sure if we should overload `UV_PREVIEW`, but could be convinced). --- Cargo.lock | 2 + crates/uv-bench/benches/uv.rs | 4 +- crates/uv-build-frontend/src/lib.rs | 4 +- crates/uv-cli/src/lib.rs | 25 +- crates/uv-configuration/Cargo.toml | 2 + crates/uv-configuration/src/preview.rs | 248 ++++- crates/uv-dev/src/compile.rs | 4 +- crates/uv-dispatch/src/lib.rs | 6 +- crates/uv-python/src/discovery.rs | 14 +- crates/uv-python/src/environment.rs | 4 +- crates/uv-python/src/installation.rs | 10 +- crates/uv-python/src/lib.rs | 168 ++-- crates/uv-python/src/managed.rs | 12 +- crates/uv-static/src/env_vars.rs | 3 + crates/uv-tool/src/lib.rs | 4 +- crates/uv-virtualenv/src/lib.rs | 4 +- crates/uv-virtualenv/src/virtualenv.rs | 4 +- crates/uv/src/commands/build_frontend.rs | 8 +- crates/uv/src/commands/pip/check.rs | 4 +- crates/uv/src/commands/pip/compile.rs | 6 +- crates/uv/src/commands/pip/freeze.rs | 4 +- crates/uv/src/commands/pip/install.rs | 11 +- crates/uv/src/commands/pip/list.rs | 4 +- crates/uv/src/commands/pip/show.rs | 4 +- crates/uv/src/commands/pip/sync.rs | 11 +- crates/uv/src/commands/pip/tree.rs | 4 +- crates/uv/src/commands/pip/uninstall.rs | 4 +- crates/uv/src/commands/project/add.rs | 15 +- crates/uv/src/commands/project/environment.rs | 4 +- crates/uv/src/commands/project/export.rs | 4 +- crates/uv/src/commands/project/init.rs | 8 +- crates/uv/src/commands/project/lock.rs | 12 +- crates/uv/src/commands/project/mod.rs | 24 +- crates/uv/src/commands/project/remove.rs | 4 +- crates/uv/src/commands/project/run.rs | 4 +- crates/uv/src/commands/project/sync.rs | 12 +- crates/uv/src/commands/project/tree.rs | 4 +- crates/uv/src/commands/project/version.rs | 8 +- crates/uv/src/commands/python/find.rs | 6 +- crates/uv/src/commands/python/install.rs | 19 +- crates/uv/src/commands/python/list.rs | 4 +- crates/uv/src/commands/python/pin.rs | 6 +- crates/uv/src/commands/python/uninstall.rs | 6 +- crates/uv/src/commands/tool/common.rs | 4 +- crates/uv/src/commands/tool/dir.rs | 4 +- crates/uv/src/commands/tool/install.rs | 4 +- crates/uv/src/commands/tool/run.rs | 6 +- crates/uv/src/commands/tool/upgrade.rs | 6 +- crates/uv/src/commands/venv.rs | 7 +- crates/uv/src/settings.rs | 12 +- crates/uv/tests/it/common/mod.rs | 4 +- crates/uv/tests/it/edit.rs | 4 +- crates/uv/tests/it/python_install.rs | 12 +- crates/uv/tests/it/show_settings.rs | 846 +++++++++++++++++- docs/reference/environment.md | 4 + 55 files changed, 1327 insertions(+), 304 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0bd80e35..b22a79c0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5037,6 +5037,7 @@ name = "uv-configuration" version = "0.0.1" dependencies = [ "anyhow", + "bitflags 2.9.1", "clap", "either", "fs-err 3.1.1", @@ -5061,6 +5062,7 @@ dependencies = [ "uv-pep508", "uv-platform-tags", "uv-static", + "uv-warnings", ] [[package]] diff --git a/crates/uv-bench/benches/uv.rs b/crates/uv-bench/benches/uv.rs index 8380ccd60..eedc0f92f 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, - PackageConfigSettings, PreviewMode, SourceStrategy, + PackageConfigSettings, Preview, SourceStrategy, }; use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution::DistributionDatabase; @@ -194,7 +194,7 @@ mod resolver { sources, workspace_cache, concurrency, - PreviewMode::Enabled, + Preview::default(), ); let markers = if universal { diff --git a/crates/uv-build-frontend/src/lib.rs b/crates/uv-build-frontend/src/lib.rs index e2a128747..b815719ba 100644 --- a/crates/uv-build-frontend/src/lib.rs +++ b/crates/uv-build-frontend/src/lib.rs @@ -28,7 +28,7 @@ use tokio::sync::{Mutex, Semaphore}; use tracing::{Instrument, debug, info_span, instrument, warn}; use uv_cache_key::cache_digest; -use uv_configuration::PreviewMode; +use uv_configuration::Preview; use uv_configuration::{BuildKind, BuildOutput, ConfigSettings, SourceStrategy}; use uv_distribution::BuildRequires; use uv_distribution_types::{IndexLocations, Requirement, Resolution}; @@ -286,7 +286,7 @@ impl SourceBuild { mut environment_variables: FxHashMap, level: BuildOutput, concurrent_builds: usize, - preview: PreviewMode, + preview: Preview, ) -> Result { let temp_dir = build_context.cache().venv_dir()?; diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index b6a41f3e7..1b41050b2 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -11,8 +11,8 @@ use clap::{Args, Parser, Subcommand}; use uv_cache::CacheArgs; use uv_configuration::{ ConfigSettingEntry, ConfigSettingPackageEntry, ExportFormat, IndexStrategy, - KeyringProviderType, PackageNameSpecifier, ProjectBuildBackend, TargetTriple, TrustedHost, - TrustedPublishing, VersionControlSystem, + KeyringProviderType, PackageNameSpecifier, PreviewFeatures, ProjectBuildBackend, TargetTriple, + TrustedHost, TrustedPublishing, VersionControlSystem, }; use uv_distribution_types::{Index, IndexUrl, Origin, PipExtraIndex, PipFindLinks, PipIndex}; use uv_normalize::{ExtraName, GroupName, PackageName, PipGroupName}; @@ -273,7 +273,7 @@ pub struct GlobalArgs { )] pub allow_insecure_host: Option>>, - /// Whether to enable experimental, preview features. + /// Whether to enable all experimental preview features. /// /// Preview features may change without warning. #[arg(global = true, long, hide = true, env = EnvVars::UV_PREVIEW, value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_preview"))] @@ -282,6 +282,25 @@ pub struct GlobalArgs { #[arg(global = true, long, overrides_with("preview"), hide = true)] pub no_preview: bool, + /// Enable experimental preview features. + /// + /// Preview features may change without warning. + /// + /// Use comma-separated values or pass multiple times to enable multiple features. + /// + /// The following features are available: `python-install-default`, `python-upgrade`, + /// `json-output`, `pylock`, `add-bounds`. + #[arg( + global = true, + long = "preview-features", + env = EnvVars::UV_PREVIEW_FEATURES, + value_delimiter = ',', + hide = true, + alias = "preview-feature", + value_enum, + )] + pub preview_features: Vec, + /// Avoid discovering a `pyproject.toml` or `uv.toml` file. /// /// Normally, configuration files are discovered in the current directory, diff --git a/crates/uv-configuration/Cargo.toml b/crates/uv-configuration/Cargo.toml index 1c195ab3d..807340a01 100644 --- a/crates/uv-configuration/Cargo.toml +++ b/crates/uv-configuration/Cargo.toml @@ -27,7 +27,9 @@ uv-pep440 = { workspace = true } uv-pep508 = { workspace = true, features = ["schemars"] } uv-platform-tags = { workspace = true } uv-static = { workspace = true } +uv-warnings = { workspace = true } +bitflags = { workspace = true } clap = { workspace = true, features = ["derive"], optional = true } either = { workspace = true } fs-err = { workspace = true } diff --git a/crates/uv-configuration/src/preview.rs b/crates/uv-configuration/src/preview.rs index 38572589b..c8d67be5b 100644 --- a/crates/uv-configuration/src/preview.rs +++ b/crates/uv-configuration/src/preview.rs @@ -1,37 +1,243 @@ -use std::fmt::{Display, Formatter}; +use std::{ + fmt::{Display, Formatter}, + str::FromStr, +}; + +use thiserror::Error; +use uv_warnings::warn_user_once; + +bitflags::bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] + pub struct PreviewFeatures: u32 { + const PYTHON_INSTALL_DEFAULT = 1 << 0; + const PYTHON_UPGRADE = 1 << 1; + const JSON_OUTPUT = 1 << 2; + const PYLOCK = 1 << 3; + const ADD_BOUNDS = 1 << 4; + } +} + +impl PreviewFeatures { + /// Returns the string representation of a single preview feature flag. + /// + /// Panics if given a combination of flags. + fn flag_as_str(self) -> &'static str { + match self { + Self::PYTHON_INSTALL_DEFAULT => "python-install-default", + Self::PYTHON_UPGRADE => "python-upgrade", + Self::JSON_OUTPUT => "json-output", + Self::PYLOCK => "pylock", + Self::ADD_BOUNDS => "add-bounds", + _ => panic!("`flag_as_str` can only be used for exactly one feature flag"), + } + } +} + +impl Display for PreviewFeatures { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if self.is_empty() { + write!(f, "none") + } else { + let features: Vec<&str> = self.iter().map(PreviewFeatures::flag_as_str).collect(); + write!(f, "{}", features.join(",")) + } + } +} + +#[derive(Debug, Error, Clone)] +pub enum PreviewFeaturesParseError { + #[error("Empty string in preview features: {0}")] + Empty(String), +} + +impl FromStr for PreviewFeatures { + type Err = PreviewFeaturesParseError; + + fn from_str(s: &str) -> Result { + let mut flags = PreviewFeatures::empty(); + + for part in s.split(',') { + let part = part.trim(); + if part.is_empty() { + return Err(PreviewFeaturesParseError::Empty( + "Empty string in preview features".to_string(), + )); + } + + let flag = match part { + "python-install-default" => Self::PYTHON_INSTALL_DEFAULT, + "python-upgrade" => Self::PYTHON_UPGRADE, + "json-output" => Self::JSON_OUTPUT, + "pylock" => Self::PYLOCK, + "add-bounds" => Self::ADD_BOUNDS, + _ => { + warn_user_once!("Unknown preview feature: `{part}`"); + continue; + } + }; + + flags |= flag; + } + + Ok(flags) + } +} #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub enum PreviewMode { - #[default] - Disabled, - Enabled, +pub struct Preview { + flags: PreviewFeatures, } -impl PreviewMode { - pub fn is_enabled(&self) -> bool { - matches!(self, Self::Enabled) +impl Preview { + pub fn new(flags: PreviewFeatures) -> Self { + Self { flags } } - pub fn is_disabled(&self) -> bool { - matches!(self, Self::Disabled) + pub fn all() -> Self { + Self::new(PreviewFeatures::all()) } -} -impl From for PreviewMode { - fn from(version: bool) -> Self { - if version { - PreviewMode::Enabled - } else { - PreviewMode::Disabled + pub fn from_args( + preview: bool, + no_preview: bool, + preview_features: &[PreviewFeatures], + ) -> Self { + if no_preview { + return Self::default(); } + + if preview { + return Self::all(); + } + + let mut flags = PreviewFeatures::empty(); + + for features in preview_features { + flags |= *features; + } + + Self { flags } + } + + pub fn is_enabled(&self, flag: PreviewFeatures) -> bool { + self.flags.contains(flag) } } -impl Display for PreviewMode { +impl Display for Preview { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Self::Disabled => write!(f, "disabled"), - Self::Enabled => write!(f, "enabled"), + if self.flags.is_empty() { + write!(f, "disabled") + } else if self.flags == PreviewFeatures::all() { + write!(f, "enabled") + } else { + write!(f, "{}", self.flags) } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_preview_features_from_str() { + // Test single feature + let features = PreviewFeatures::from_str("python-install-default").unwrap(); + assert_eq!(features, PreviewFeatures::PYTHON_INSTALL_DEFAULT); + + // Test multiple features + let features = PreviewFeatures::from_str("python-upgrade,json-output").unwrap(); + assert!(features.contains(PreviewFeatures::PYTHON_UPGRADE)); + assert!(features.contains(PreviewFeatures::JSON_OUTPUT)); + assert!(!features.contains(PreviewFeatures::PYLOCK)); + + // Test with whitespace + let features = PreviewFeatures::from_str("pylock , add-bounds").unwrap(); + assert!(features.contains(PreviewFeatures::PYLOCK)); + assert!(features.contains(PreviewFeatures::ADD_BOUNDS)); + + // Test empty string error + assert!(PreviewFeatures::from_str("").is_err()); + assert!(PreviewFeatures::from_str("pylock,").is_err()); + assert!(PreviewFeatures::from_str(",pylock").is_err()); + + // Test unknown feature (should be ignored with warning) + let features = PreviewFeatures::from_str("unknown-feature,pylock").unwrap(); + assert!(features.contains(PreviewFeatures::PYLOCK)); + assert_eq!(features.bits().count_ones(), 1); + } + + #[test] + fn test_preview_features_display() { + // Test empty + let features = PreviewFeatures::empty(); + assert_eq!(features.to_string(), "none"); + + // Test single feature + let features = PreviewFeatures::PYTHON_INSTALL_DEFAULT; + assert_eq!(features.to_string(), "python-install-default"); + + // Test multiple features + let features = PreviewFeatures::PYTHON_UPGRADE | PreviewFeatures::JSON_OUTPUT; + assert_eq!(features.to_string(), "python-upgrade,json-output"); + } + + #[test] + fn test_preview_display() { + // Test disabled + let preview = Preview::default(); + assert_eq!(preview.to_string(), "disabled"); + + // Test enabled (all features) + let preview = Preview::all(); + assert_eq!(preview.to_string(), "enabled"); + + // Test specific features + let preview = Preview::new(PreviewFeatures::PYTHON_UPGRADE | PreviewFeatures::PYLOCK); + assert_eq!(preview.to_string(), "python-upgrade,pylock"); + } + + #[test] + fn test_preview_from_args() { + // Test no_preview + let preview = Preview::from_args(true, true, &[]); + assert_eq!(preview.to_string(), "disabled"); + + // Test preview (all features) + let preview = Preview::from_args(true, false, &[]); + assert_eq!(preview.to_string(), "enabled"); + + // Test specific features + let features = vec![ + PreviewFeatures::PYTHON_UPGRADE, + PreviewFeatures::JSON_OUTPUT, + ]; + let preview = Preview::from_args(false, false, &features); + assert!(preview.is_enabled(PreviewFeatures::PYTHON_UPGRADE)); + assert!(preview.is_enabled(PreviewFeatures::JSON_OUTPUT)); + assert!(!preview.is_enabled(PreviewFeatures::PYLOCK)); + } + + #[test] + fn test_as_str_single_flags() { + assert_eq!( + PreviewFeatures::PYTHON_INSTALL_DEFAULT.flag_as_str(), + "python-install-default" + ); + assert_eq!( + PreviewFeatures::PYTHON_UPGRADE.flag_as_str(), + "python-upgrade" + ); + assert_eq!(PreviewFeatures::JSON_OUTPUT.flag_as_str(), "json-output"); + assert_eq!(PreviewFeatures::PYLOCK.flag_as_str(), "pylock"); + assert_eq!(PreviewFeatures::ADD_BOUNDS.flag_as_str(), "add-bounds"); + } + + #[test] + #[should_panic(expected = "`flag_as_str` can only be used for exactly one feature flag")] + fn test_as_str_multiple_flags_panics() { + let features = PreviewFeatures::PYTHON_UPGRADE | PreviewFeatures::JSON_OUTPUT; + let _ = features.flag_as_str(); + } +} diff --git a/crates/uv-dev/src/compile.rs b/crates/uv-dev/src/compile.rs index d2b685b23..0e1392f63 100644 --- a/crates/uv-dev/src/compile.rs +++ b/crates/uv-dev/src/compile.rs @@ -4,7 +4,7 @@ use clap::Parser; use tracing::info; use uv_cache::{Cache, CacheArgs}; -use uv_configuration::{Concurrency, PreviewMode}; +use uv_configuration::{Concurrency, Preview}; use uv_python::{EnvironmentPreference, PythonEnvironment, PythonRequest}; #[derive(Parser)] @@ -26,7 +26,7 @@ pub(crate) async fn compile(args: CompileArgs) -> anyhow::Result<()> { &PythonRequest::default(), EnvironmentPreference::OnlyVirtual, &cache, - PreviewMode::Disabled, + Preview::default(), )? .into_interpreter(); interpreter.sys_executable().to_path_buf() diff --git a/crates/uv-dispatch/src/lib.rs b/crates/uv-dispatch/src/lib.rs index 2e34b583d..aa48fecf7 100644 --- a/crates/uv-dispatch/src/lib.rs +++ b/crates/uv-dispatch/src/lib.rs @@ -18,7 +18,7 @@ use uv_cache::Cache; use uv_client::RegistryClient; use uv_configuration::{ BuildKind, BuildOptions, ConfigSettings, Constraints, IndexStrategy, PackageConfigSettings, - PreviewMode, Reinstall, SourceStrategy, + Preview, Reinstall, SourceStrategy, }; use uv_configuration::{BuildOutput, Concurrency}; use uv_distribution::DistributionDatabase; @@ -99,7 +99,7 @@ pub struct BuildDispatch<'a> { sources: SourceStrategy, workspace_cache: WorkspaceCache, concurrency: Concurrency, - preview: PreviewMode, + preview: Preview, } impl<'a> BuildDispatch<'a> { @@ -123,7 +123,7 @@ impl<'a> BuildDispatch<'a> { sources: SourceStrategy, workspace_cache: WorkspaceCache, concurrency: Concurrency, - preview: PreviewMode, + preview: Preview, ) -> Self { Self { client, diff --git a/crates/uv-python/src/discovery.rs b/crates/uv-python/src/discovery.rs index f10b480e2..466ea4b0f 100644 --- a/crates/uv-python/src/discovery.rs +++ b/crates/uv-python/src/discovery.rs @@ -8,7 +8,7 @@ use std::{env, io, iter}; use std::{path::Path, path::PathBuf, str::FromStr}; use thiserror::Error; use tracing::{debug, instrument, trace}; -use uv_configuration::PreviewMode; +use uv_configuration::Preview; use which::{which, which_all}; use uv_cache::Cache; @@ -335,7 +335,7 @@ fn python_executables_from_installed<'a>( implementation: Option<&'a ImplementationName>, platform: PlatformRequest, preference: PythonPreference, - preview: PreviewMode, + preview: Preview, ) -> Box> + 'a> { let from_managed_installations = iter::once_with(move || { ManagedPythonInstallations::from_settings(None) @@ -485,7 +485,7 @@ fn python_executables<'a>( platform: PlatformRequest, environments: EnvironmentPreference, preference: PythonPreference, - preview: PreviewMode, + preview: Preview, ) -> Box> + 'a> { // Always read from `UV_INTERNAL__PARENT_INTERPRETER` — it could be a system interpreter let from_parent_interpreter = iter::once_with(|| { @@ -705,7 +705,7 @@ fn python_interpreters<'a>( environments: EnvironmentPreference, preference: PythonPreference, cache: &'a Cache, - preview: PreviewMode, + preview: Preview, ) -> impl Iterator> + 'a { python_interpreters_from_executables( // Perform filtering on the discovered executables based on their source. This avoids @@ -1053,7 +1053,7 @@ pub fn find_python_installations<'a>( environments: EnvironmentPreference, preference: PythonPreference, cache: &'a Cache, - preview: PreviewMode, + preview: Preview, ) -> Box> + 'a> { let sources = DiscoveryPreferences { python_preference: preference, @@ -1254,7 +1254,7 @@ pub(crate) fn find_python_installation( environments: EnvironmentPreference, preference: PythonPreference, cache: &Cache, - preview: PreviewMode, + preview: Preview, ) -> Result { let installations = find_python_installations(request, environments, preference, cache, preview); @@ -1353,7 +1353,7 @@ pub(crate) fn find_best_python_installation( environments: EnvironmentPreference, preference: PythonPreference, cache: &Cache, - preview: PreviewMode, + preview: Preview, ) -> Result { debug!("Starting Python discovery for {}", request); diff --git a/crates/uv-python/src/environment.rs b/crates/uv-python/src/environment.rs index 10cec16ad..f82319074 100644 --- a/crates/uv-python/src/environment.rs +++ b/crates/uv-python/src/environment.rs @@ -7,7 +7,7 @@ use owo_colors::OwoColorize; use tracing::debug; use uv_cache::Cache; -use uv_configuration::PreviewMode; +use uv_configuration::Preview; use uv_fs::{LockedFile, Simplified}; use uv_pep440::Version; @@ -153,7 +153,7 @@ impl PythonEnvironment { request: &PythonRequest, preference: EnvironmentPreference, cache: &Cache, - preview: PreviewMode, + preview: Preview, ) -> Result { let installation = match find_python_installation( request, diff --git a/crates/uv-python/src/installation.rs b/crates/uv-python/src/installation.rs index a5dbb55f2..3f5b506a6 100644 --- a/crates/uv-python/src/installation.rs +++ b/crates/uv-python/src/installation.rs @@ -8,7 +8,7 @@ use tracing::{debug, info}; use uv_cache::Cache; use uv_client::BaseClientBuilder; -use uv_configuration::PreviewMode; +use uv_configuration::Preview; use uv_pep440::{Prerelease, Version}; use crate::discovery::{ @@ -58,7 +58,7 @@ impl PythonInstallation { environments: EnvironmentPreference, preference: PythonPreference, cache: &Cache, - preview: PreviewMode, + preview: Preview, ) -> Result { let installation = find_python_installation(request, environments, preference, cache, preview)??; @@ -72,7 +72,7 @@ impl PythonInstallation { environments: EnvironmentPreference, preference: PythonPreference, cache: &Cache, - preview: PreviewMode, + preview: Preview, ) -> Result { Ok(find_best_python_installation( request, @@ -97,7 +97,7 @@ impl PythonInstallation { python_install_mirror: Option<&str>, pypy_install_mirror: Option<&str>, python_downloads_json_url: Option<&str>, - preview: PreviewMode, + preview: Preview, ) -> Result { let request = request.unwrap_or(&PythonRequest::Default); @@ -220,7 +220,7 @@ impl PythonInstallation { reporter: Option<&dyn Reporter>, python_install_mirror: Option<&str>, pypy_install_mirror: Option<&str>, - preview: PreviewMode, + preview: Preview, ) -> Result { let installations = ManagedPythonInstallations::from_settings(None)?.init()?; let installations_dir = installations.root(); diff --git a/crates/uv-python/src/lib.rs b/crates/uv-python/src/lib.rs index 2461f9006..8b8e9c129 100644 --- a/crates/uv-python/src/lib.rs +++ b/crates/uv-python/src/lib.rs @@ -135,7 +135,7 @@ mod tests { use indoc::{formatdoc, indoc}; use temp_env::with_vars; use test_log::test; - use uv_configuration::PreviewMode; + use uv_configuration::Preview; use uv_static::EnvVars; use uv_cache::Cache; @@ -468,7 +468,7 @@ mod tests { EnvironmentPreference::OnlySystem, PythonPreference::default(), &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }); assert!( @@ -483,7 +483,7 @@ mod tests { EnvironmentPreference::OnlySystem, PythonPreference::default(), &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }); assert!( @@ -508,7 +508,7 @@ mod tests { EnvironmentPreference::OnlySystem, PythonPreference::default(), &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }); assert!( @@ -530,7 +530,7 @@ mod tests { EnvironmentPreference::OnlySystem, PythonPreference::default(), &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert!( @@ -592,7 +592,7 @@ mod tests { EnvironmentPreference::OnlySystem, PythonPreference::default(), &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert!( @@ -624,7 +624,7 @@ mod tests { EnvironmentPreference::OnlySystem, PythonPreference::default(), &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }); assert!( @@ -661,7 +661,7 @@ mod tests { EnvironmentPreference::OnlySystem, PythonPreference::default(), &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert!( @@ -693,7 +693,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -715,7 +715,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -741,7 +741,7 @@ mod tests { EnvironmentPreference::OnlySystem, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -767,7 +767,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -790,7 +790,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; @@ -824,7 +824,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; @@ -858,7 +858,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })?; assert!( @@ -880,7 +880,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })?; assert!( @@ -902,7 +902,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; @@ -936,7 +936,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; @@ -973,7 +973,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert!( @@ -1004,7 +1004,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert!( @@ -1039,7 +1039,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1065,7 +1065,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1092,7 +1092,7 @@ mod tests { EnvironmentPreference::OnlyVirtual, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }, )??; @@ -1117,7 +1117,7 @@ mod tests { EnvironmentPreference::OnlyVirtual, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }, )?; @@ -1139,7 +1139,7 @@ mod tests { EnvironmentPreference::OnlySystem, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }, )??; @@ -1162,7 +1162,7 @@ mod tests { EnvironmentPreference::OnlyVirtual, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }, )??; @@ -1195,7 +1195,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }, )??; @@ -1216,7 +1216,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }, )??; @@ -1243,7 +1243,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; @@ -1261,7 +1261,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; @@ -1290,7 +1290,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1328,7 +1328,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }, )??; @@ -1356,7 +1356,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }, )??; @@ -1381,7 +1381,7 @@ mod tests { EnvironmentPreference::ExplicitSystem, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }, )??; @@ -1406,7 +1406,7 @@ mod tests { EnvironmentPreference::OnlySystem, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }, )??; @@ -1431,7 +1431,7 @@ mod tests { EnvironmentPreference::OnlyVirtual, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }, )??; @@ -1469,7 +1469,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }, )??; @@ -1497,7 +1497,7 @@ mod tests { EnvironmentPreference::OnlySystem, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1514,7 +1514,7 @@ mod tests { EnvironmentPreference::OnlySystem, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1531,7 +1531,7 @@ mod tests { EnvironmentPreference::OnlySystem, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })?; assert!( @@ -1553,7 +1553,7 @@ mod tests { EnvironmentPreference::OnlyVirtual, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })?; assert!( @@ -1570,7 +1570,7 @@ mod tests { EnvironmentPreference::OnlySystem, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }, )?; @@ -1592,7 +1592,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1607,7 +1607,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })?; assert!( @@ -1621,7 +1621,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })?; assert!( @@ -1650,7 +1650,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1666,7 +1666,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1696,7 +1696,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1712,7 +1712,7 @@ mod tests { EnvironmentPreference::ExplicitSystem, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1728,7 +1728,7 @@ mod tests { EnvironmentPreference::OnlyVirtual, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1744,7 +1744,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1768,7 +1768,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1783,7 +1783,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1807,7 +1807,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1827,7 +1827,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }, )??; @@ -1856,7 +1856,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1878,7 +1878,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })?; assert!( @@ -1908,7 +1908,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -1924,7 +1924,7 @@ mod tests { EnvironmentPreference::ExplicitSystem, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })?; assert!( @@ -1951,7 +1951,7 @@ mod tests { EnvironmentPreference::ExplicitSystem, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }) .unwrap() @@ -1976,7 +1976,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })?; assert!( @@ -1993,7 +1993,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -2008,7 +2008,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -2034,7 +2034,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -2049,7 +2049,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -2075,7 +2075,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -2102,7 +2102,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -2129,7 +2129,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -2156,7 +2156,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -2183,7 +2183,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -2211,7 +2211,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })?; assert!( @@ -2233,7 +2233,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -2248,7 +2248,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -2274,7 +2274,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -2289,7 +2289,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; assert_eq!( @@ -2327,7 +2327,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }) .unwrap() @@ -2345,7 +2345,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }) .unwrap() @@ -2387,7 +2387,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }) .unwrap() @@ -2405,7 +2405,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }) .unwrap() @@ -2442,7 +2442,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }) .unwrap() @@ -2465,7 +2465,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }) .unwrap() @@ -2488,7 +2488,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) }) .unwrap() @@ -2527,7 +2527,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; @@ -2580,7 +2580,7 @@ mod tests { EnvironmentPreference::Any, PythonPreference::OnlySystem, &context.cache, - PreviewMode::Disabled, + Preview::default(), ) })??; diff --git a/crates/uv-python/src/managed.rs b/crates/uv-python/src/managed.rs index 9ee72adda..d9b96e5ed 100644 --- a/crates/uv-python/src/managed.rs +++ b/crates/uv-python/src/managed.rs @@ -12,7 +12,7 @@ use itertools::Itertools; use same_file::is_same_file; use thiserror::Error; use tracing::{debug, warn}; -use uv_configuration::PreviewMode; +use uv_configuration::{Preview, PreviewFeatures}; #[cfg(windows)] use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT; @@ -519,7 +519,7 @@ impl ManagedPythonInstallation { /// Ensure the environment contains the symlink directory (or junction on Windows) /// pointing to the patch directory for this minor version. - pub fn ensure_minor_version_link(&self, preview: PreviewMode) -> Result<(), Error> { + pub fn ensure_minor_version_link(&self, preview: Preview) -> Result<(), Error> { if let Some(minor_version_link) = PythonMinorVersionLink::from_installation(self, preview) { minor_version_link.create_directory()?; } @@ -531,7 +531,7 @@ impl ManagedPythonInstallation { /// /// Unlike [`ensure_minor_version_link`], will not create a new symlink directory /// if one doesn't already exist, - pub fn update_minor_version_link(&self, preview: PreviewMode) -> Result<(), Error> { + pub fn update_minor_version_link(&self, preview: Preview) -> Result<(), Error> { if let Some(minor_version_link) = PythonMinorVersionLink::from_installation(self, preview) { if !minor_version_link.exists() { return Ok(()); @@ -702,7 +702,7 @@ impl PythonMinorVersionLink { pub fn from_executable( executable: &Path, key: &PythonInstallationKey, - preview: PreviewMode, + preview: Preview, ) -> Option { let implementation = key.implementation(); if !matches!( @@ -755,7 +755,7 @@ impl PythonMinorVersionLink { // If preview mode is disabled, still return a `MinorVersionSymlink` for // existing symlinks, allowing continued operations without the `--preview` // flag after initial symlink directory installation. - if preview.is_disabled() && !minor_version_link.exists() { + if !preview.is_enabled(PreviewFeatures::PYTHON_UPGRADE) && !minor_version_link.exists() { return None; } Some(minor_version_link) @@ -763,7 +763,7 @@ impl PythonMinorVersionLink { pub fn from_installation( installation: &ManagedPythonInstallation, - preview: PreviewMode, + preview: Preview, ) -> Option { PythonMinorVersionLink::from_executable( installation.executable(false).as_path(), diff --git a/crates/uv-static/src/env_vars.rs b/crates/uv-static/src/env_vars.rs index a18bc11a8..b8adf225f 100644 --- a/crates/uv-static/src/env_vars.rs +++ b/crates/uv-static/src/env_vars.rs @@ -225,6 +225,9 @@ impl EnvVars { /// Equivalent to the `--preview` argument. Enables preview mode. pub const UV_PREVIEW: &'static str = "UV_PREVIEW"; + /// Equivalent to the `--preview-features` argument. Enables specific preview features. + pub const UV_PREVIEW_FEATURES: &'static str = "UV_PREVIEW_FEATURES"; + /// Equivalent to the `--token` argument for self update. A GitHub token for authentication. pub const UV_GITHUB_TOKEN: &'static str = "UV_GITHUB_TOKEN"; diff --git a/crates/uv-tool/src/lib.rs b/crates/uv-tool/src/lib.rs index 4afc83bcb..902dbf2d0 100644 --- a/crates/uv-tool/src/lib.rs +++ b/crates/uv-tool/src/lib.rs @@ -1,7 +1,7 @@ use core::fmt; use fs_err as fs; -use uv_configuration::PreviewMode; +use uv_configuration::Preview; use uv_dirs::user_executable_directory; use uv_pep440::Version; use uv_pep508::{InvalidNameError, PackageName}; @@ -258,7 +258,7 @@ impl InstalledTools { &self, name: &PackageName, interpreter: Interpreter, - preview: PreviewMode, + preview: Preview, ) -> Result { let environment_path = self.tool_dir(name); diff --git a/crates/uv-virtualenv/src/lib.rs b/crates/uv-virtualenv/src/lib.rs index bcf1e9f97..7c682627b 100644 --- a/crates/uv-virtualenv/src/lib.rs +++ b/crates/uv-virtualenv/src/lib.rs @@ -3,7 +3,7 @@ use std::path::Path; use thiserror::Error; -use uv_configuration::PreviewMode; +use uv_configuration::Preview; use uv_python::{Interpreter, PythonEnvironment}; pub use virtualenv::{OnExisting, remove_virtualenv}; @@ -56,7 +56,7 @@ pub fn create_venv( relocatable: bool, seed: bool, upgradeable: bool, - preview: PreviewMode, + preview: Preview, ) -> Result { // Create the virtualenv at the given location. let virtualenv = virtualenv::create( diff --git a/crates/uv-virtualenv/src/virtualenv.rs b/crates/uv-virtualenv/src/virtualenv.rs index 5fa77034e..7c65ec1bf 100644 --- a/crates/uv-virtualenv/src/virtualenv.rs +++ b/crates/uv-virtualenv/src/virtualenv.rs @@ -12,7 +12,7 @@ use itertools::Itertools; use owo_colors::OwoColorize; use tracing::{debug, trace}; -use uv_configuration::PreviewMode; +use uv_configuration::Preview; use uv_fs::{CWD, Simplified, cachedir}; use uv_pypi_types::Scheme; use uv_python::managed::{PythonMinorVersionLink, create_link_to_executable}; @@ -59,7 +59,7 @@ pub(crate) fn create( relocatable: bool, seed: bool, upgradeable: bool, - preview: PreviewMode, + preview: Preview, ) -> Result { // Determine the base Python executable; that is, the Python executable that should be // considered the "base" for the virtual environment. diff --git a/crates/uv/src/commands/build_frontend.rs b/crates/uv/src/commands/build_frontend.rs index b3f9e5c89..24cda1cf3 100644 --- a/crates/uv/src/commands/build_frontend.rs +++ b/crates/uv/src/commands/build_frontend.rs @@ -16,7 +16,7 @@ use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ BuildKind, BuildOptions, BuildOutput, Concurrency, ConfigSettings, Constraints, DependencyGroupsWithDefaults, HashCheckingMode, IndexStrategy, KeyringProviderType, - PackageConfigSettings, PreviewMode, SourceStrategy, + PackageConfigSettings, Preview, SourceStrategy, }; use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution_filename::{ @@ -117,7 +117,7 @@ pub(crate) async fn build_frontend( concurrency: Concurrency, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { let build_result = build_impl( project_dir, @@ -185,7 +185,7 @@ async fn build_impl( concurrency: Concurrency, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { // Extract the resolver settings. let ResolverSettings { @@ -437,7 +437,7 @@ async fn build_package( link_mode: LinkMode, config_setting: &ConfigSettings, config_settings_package: &PackageConfigSettings, - preview: PreviewMode, + preview: Preview, ) -> Result, Error> { let output_dir = if let Some(output_dir) = output_dir { Cow::Owned(std::path::absolute(output_dir)?) diff --git a/crates/uv/src/commands/pip/check.rs b/crates/uv/src/commands/pip/check.rs index bfbb20ee6..cf5ab350b 100644 --- a/crates/uv/src/commands/pip/check.rs +++ b/crates/uv/src/commands/pip/check.rs @@ -5,7 +5,7 @@ use anyhow::Result; use owo_colors::OwoColorize; use uv_cache::Cache; -use uv_configuration::PreviewMode; +use uv_configuration::Preview; use uv_distribution_types::{Diagnostic, InstalledDist}; use uv_installer::{SitePackages, SitePackagesDiagnostic}; use uv_python::{EnvironmentPreference, PythonEnvironment, PythonRequest}; @@ -20,7 +20,7 @@ pub(crate) fn pip_check( system: bool, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { let start = Instant::now(); diff --git a/crates/uv/src/commands/pip/compile.rs b/crates/uv/src/commands/pip/compile.rs index b9dda45c8..8c512a2e9 100644 --- a/crates/uv/src/commands/pip/compile.rs +++ b/crates/uv/src/commands/pip/compile.rs @@ -14,8 +14,8 @@ use uv_cache::Cache; use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ BuildOptions, Concurrency, ConfigSettings, Constraints, ExportFormat, ExtrasSpecification, - IndexStrategy, NoBinary, NoBuild, PackageConfigSettings, PreviewMode, Reinstall, - SourceStrategy, Upgrade, + IndexStrategy, NoBinary, NoBuild, PackageConfigSettings, Preview, Reinstall, SourceStrategy, + Upgrade, }; use uv_configuration::{KeyringProviderType, TargetTriple}; use uv_dispatch::{BuildDispatch, SharedState}; @@ -110,7 +110,7 @@ pub(crate) async fn pip_compile( quiet: bool, cache: Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { // If the user provides a `pyproject.toml` or other TOML file as the output file, raise an // error. diff --git a/crates/uv/src/commands/pip/freeze.rs b/crates/uv/src/commands/pip/freeze.rs index 8c8491d45..6f663e03b 100644 --- a/crates/uv/src/commands/pip/freeze.rs +++ b/crates/uv/src/commands/pip/freeze.rs @@ -6,7 +6,7 @@ use itertools::Itertools; use owo_colors::OwoColorize; use uv_cache::Cache; -use uv_configuration::PreviewMode; +use uv_configuration::Preview; use uv_distribution_types::{Diagnostic, InstalledDist, Name}; use uv_installer::SitePackages; use uv_python::{EnvironmentPreference, PythonEnvironment, PythonRequest}; @@ -24,7 +24,7 @@ pub(crate) fn pip_freeze( paths: Option>, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { // Detect the current Python interpreter. let environment = PythonEnvironment::find( diff --git a/crates/uv/src/commands/pip/install.rs b/crates/uv/src/commands/pip/install.rs index cb1229d72..d3917db11 100644 --- a/crates/uv/src/commands/pip/install.rs +++ b/crates/uv/src/commands/pip/install.rs @@ -10,8 +10,8 @@ use uv_cache::Cache; use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ BuildOptions, Concurrency, ConfigSettings, Constraints, DryRun, ExtrasSpecification, - HashCheckingMode, IndexStrategy, PackageConfigSettings, PreviewMode, Reinstall, SourceStrategy, - Upgrade, + HashCheckingMode, IndexStrategy, PackageConfigSettings, Preview, PreviewFeatures, Reinstall, + SourceStrategy, Upgrade, }; use uv_configuration::{KeyringProviderType, TargetTriple}; use uv_dispatch::{BuildDispatch, SharedState}; @@ -95,7 +95,7 @@ pub(crate) async fn pip_install( cache: Cache, dry_run: DryRun, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> anyhow::Result { let start = std::time::Instant::now(); @@ -133,9 +133,10 @@ pub(crate) async fn pip_install( .await?; if pylock.is_some() { - if preview.is_disabled() { + if !preview.is_enabled(PreviewFeatures::PYLOCK) { warn_user!( - "The `--pylock` setting is experimental and may change without warning. Pass `--preview` to disable this warning." + "The `--pylock` setting is experimental and may change without warning. Pass `--preview-features {}` to disable this warning.", + PreviewFeatures::PYLOCK ); } } diff --git a/crates/uv/src/commands/pip/list.rs b/crates/uv/src/commands/pip/list.rs index 40e8c770d..9205268ba 100644 --- a/crates/uv/src/commands/pip/list.rs +++ b/crates/uv/src/commands/pip/list.rs @@ -15,7 +15,7 @@ use uv_cache::{Cache, Refresh}; use uv_cache_info::Timestamp; use uv_cli::ListFormat; use uv_client::{BaseClientBuilder, RegistryClientBuilder}; -use uv_configuration::{Concurrency, IndexStrategy, KeyringProviderType, PreviewMode}; +use uv_configuration::{Concurrency, IndexStrategy, KeyringProviderType, Preview}; use uv_distribution_filename::DistFilename; use uv_distribution_types::{ Diagnostic, IndexCapabilities, IndexLocations, InstalledDist, Name, RequiresPython, @@ -54,7 +54,7 @@ pub(crate) async fn pip_list( system: bool, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { // Disallow `--outdated` with `--format freeze`. if outdated && matches!(format, ListFormat::Freeze) { diff --git a/crates/uv/src/commands/pip/show.rs b/crates/uv/src/commands/pip/show.rs index 4d2b3c3a7..f7a46ea7a 100644 --- a/crates/uv/src/commands/pip/show.rs +++ b/crates/uv/src/commands/pip/show.rs @@ -7,7 +7,7 @@ use owo_colors::OwoColorize; use rustc_hash::FxHashMap; use uv_cache::Cache; -use uv_configuration::PreviewMode; +use uv_configuration::Preview; use uv_distribution_types::{Diagnostic, Name}; use uv_fs::Simplified; use uv_install_wheel::read_record_file; @@ -28,7 +28,7 @@ pub(crate) fn pip_show( files: bool, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { if packages.is_empty() { #[allow(clippy::print_stderr)] diff --git a/crates/uv/src/commands/pip/sync.rs b/crates/uv/src/commands/pip/sync.rs index 2fe5fbe87..2053f535b 100644 --- a/crates/uv/src/commands/pip/sync.rs +++ b/crates/uv/src/commands/pip/sync.rs @@ -9,8 +9,8 @@ use uv_cache::Cache; use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ BuildOptions, Concurrency, ConfigSettings, Constraints, DryRun, ExtrasSpecification, - HashCheckingMode, IndexStrategy, PackageConfigSettings, PreviewMode, Reinstall, SourceStrategy, - Upgrade, + HashCheckingMode, IndexStrategy, PackageConfigSettings, Preview, PreviewFeatures, Reinstall, + SourceStrategy, Upgrade, }; use uv_configuration::{KeyringProviderType, TargetTriple}; use uv_dispatch::{BuildDispatch, SharedState}; @@ -83,7 +83,7 @@ pub(crate) async fn pip_sync( cache: Cache, dry_run: DryRun, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { let client_builder = BaseClientBuilder::new() .retries_from_env()? @@ -126,9 +126,10 @@ pub(crate) async fn pip_sync( .await?; if pylock.is_some() { - if preview.is_disabled() { + if !preview.is_enabled(PreviewFeatures::PYLOCK) { warn_user!( - "The `--pylock` setting is experimental and may change without warning. Pass `--preview` to disable this warning." + "The `--pylock` setting is experimental and may change without warning. Pass `--preview-features {}` to disable this warning.", + PreviewFeatures::PYLOCK ); } } diff --git a/crates/uv/src/commands/pip/tree.rs b/crates/uv/src/commands/pip/tree.rs index 81a566b8e..8b0aa0c3a 100644 --- a/crates/uv/src/commands/pip/tree.rs +++ b/crates/uv/src/commands/pip/tree.rs @@ -13,7 +13,7 @@ use tokio::sync::Semaphore; use uv_cache::{Cache, Refresh}; use uv_cache_info::Timestamp; use uv_client::{BaseClientBuilder, RegistryClientBuilder}; -use uv_configuration::{Concurrency, IndexStrategy, KeyringProviderType, PreviewMode}; +use uv_configuration::{Concurrency, IndexStrategy, KeyringProviderType, Preview}; use uv_distribution_types::{Diagnostic, IndexCapabilities, IndexLocations, Name, RequiresPython}; use uv_installer::SitePackages; use uv_normalize::PackageName; @@ -52,7 +52,7 @@ pub(crate) async fn pip_tree( system: bool, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { // Detect the current Python interpreter. let environment = PythonEnvironment::find( diff --git a/crates/uv/src/commands/pip/uninstall.rs b/crates/uv/src/commands/pip/uninstall.rs index f617a0203..af7dfc8f3 100644 --- a/crates/uv/src/commands/pip/uninstall.rs +++ b/crates/uv/src/commands/pip/uninstall.rs @@ -7,7 +7,7 @@ use tracing::{debug, warn}; use uv_cache::Cache; use uv_client::BaseClientBuilder; -use uv_configuration::{DryRun, KeyringProviderType, PreviewMode}; +use uv_configuration::{DryRun, KeyringProviderType, Preview}; use uv_distribution_types::Requirement; use uv_distribution_types::{InstalledMetadata, Name, UnresolvedRequirement}; use uv_fs::Simplified; @@ -37,7 +37,7 @@ pub(crate) async fn pip_uninstall( network_settings: &NetworkSettings, dry_run: DryRun, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { let start = std::time::Instant::now(); diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs index 64419bb02..2b7938da6 100644 --- a/crates/uv/src/commands/project/add.rs +++ b/crates/uv/src/commands/project/add.rs @@ -18,8 +18,8 @@ use uv_cache_key::RepositoryUrl; use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ Concurrency, Constraints, DependencyGroups, DependencyGroupsWithDefaults, DevMode, DryRun, - EditableMode, ExtrasSpecification, ExtrasSpecificationWithDefaults, InstallOptions, - PreviewMode, SourceStrategy, + EditableMode, ExtrasSpecification, ExtrasSpecificationWithDefaults, InstallOptions, Preview, + PreviewFeatures, SourceStrategy, }; use uv_dispatch::BuildDispatch; use uv_distribution::DistributionDatabase; @@ -95,10 +95,13 @@ pub(crate) async fn add( no_config: bool, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { - if bounds.is_some() && preview.is_disabled() { - warn_user_once!("The bounds option is in preview and may change in any future release."); + if bounds.is_some() && !preview.is_enabled(PreviewFeatures::ADD_BOUNDS) { + warn_user_once!( + "The `bounds` option is in preview and may change in any future release. Pass `--preview-features {}` to disable this warning.", + PreviewFeatures::ADD_BOUNDS + ); } for source in &requirements { @@ -944,7 +947,7 @@ async fn lock_and_sync( concurrency: Concurrency, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result<(), ProjectError> { let mut lock = project::lock::LockOperation::new( if locked { diff --git a/crates/uv/src/commands/project/environment.rs b/crates/uv/src/commands/project/environment.rs index af3b3b351..675b1a960 100644 --- a/crates/uv/src/commands/project/environment.rs +++ b/crates/uv/src/commands/project/environment.rs @@ -12,7 +12,7 @@ use crate::settings::{NetworkSettings, ResolverInstallerSettings}; use uv_cache::{Cache, CacheBucket}; use uv_cache_key::{cache_digest, hash_digest}; -use uv_configuration::{Concurrency, Constraints, PreviewMode}; +use uv_configuration::{Concurrency, Constraints, Preview}; use uv_distribution_types::{Name, Resolution}; use uv_fs::PythonExt; use uv_python::{Interpreter, PythonEnvironment, canonicalize_executable}; @@ -119,7 +119,7 @@ impl CachedEnvironment { concurrency: Concurrency, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { let interpreter = Self::base_interpreter(interpreter, cache)?; diff --git a/crates/uv/src/commands/project/export.rs b/crates/uv/src/commands/project/export.rs index c14bfd904..6b06e493a 100644 --- a/crates/uv/src/commands/project/export.rs +++ b/crates/uv/src/commands/project/export.rs @@ -9,7 +9,7 @@ use owo_colors::OwoColorize; use uv_cache::Cache; use uv_configuration::{ Concurrency, DependencyGroups, EditableMode, ExportFormat, ExtrasSpecification, InstallOptions, - PreviewMode, + Preview, }; use uv_normalize::{DefaultExtras, DefaultGroups, PackageName}; use uv_python::{PythonDownloads, PythonPreference, PythonRequest}; @@ -79,7 +79,7 @@ pub(crate) async fn export( quiet: bool, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { // Identify the target. let workspace_cache = WorkspaceCache::default(); diff --git a/crates/uv/src/commands/project/init.rs b/crates/uv/src/commands/project/init.rs index 9ba2a434d..6e04524fb 100644 --- a/crates/uv/src/commands/project/init.rs +++ b/crates/uv/src/commands/project/init.rs @@ -12,7 +12,7 @@ use uv_cache::Cache; use uv_cli::AuthorFrom; use uv_client::BaseClientBuilder; use uv_configuration::{ - DependencyGroupsWithDefaults, PreviewMode, ProjectBuildBackend, VersionControlError, + DependencyGroupsWithDefaults, Preview, ProjectBuildBackend, VersionControlError, VersionControlSystem, }; use uv_fs::{CWD, Simplified}; @@ -62,7 +62,7 @@ pub(crate) async fn init( no_config: bool, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { match init_kind { InitKind::Script => { @@ -201,7 +201,7 @@ async fn init_script( pin_python: bool, package: bool, no_config: bool, - preview: PreviewMode, + preview: Preview, ) -> Result<()> { if no_workspace { warn_user_once!("`--no-workspace` is a no-op for Python scripts, which are standalone"); @@ -296,7 +296,7 @@ async fn init_project( no_config: bool, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result<()> { // Discover the current workspace, if it exists. let workspace_cache = WorkspaceCache::default(); diff --git a/crates/uv/src/commands/project/lock.rs b/crates/uv/src/commands/project/lock.rs index 706c86593..8eb0d4869 100644 --- a/crates/uv/src/commands/project/lock.rs +++ b/crates/uv/src/commands/project/lock.rs @@ -12,8 +12,8 @@ use tracing::debug; use uv_cache::Cache; use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ - Concurrency, Constraints, DependencyGroupsWithDefaults, DryRun, ExtrasSpecification, - PreviewMode, Reinstall, Upgrade, + Concurrency, Constraints, DependencyGroupsWithDefaults, DryRun, ExtrasSpecification, Preview, + Reinstall, Upgrade, }; use uv_dispatch::BuildDispatch; use uv_distribution::DistributionDatabase; @@ -93,7 +93,7 @@ pub(crate) async fn lock( no_config: bool, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> anyhow::Result { // If necessary, initialize the PEP 723 script. let script = match script { @@ -271,7 +271,7 @@ pub(super) struct LockOperation<'env> { cache: &'env Cache, workspace_cache: &'env WorkspaceCache, printer: Printer, - preview: PreviewMode, + preview: Preview, } impl<'env> LockOperation<'env> { @@ -286,7 +286,7 @@ impl<'env> LockOperation<'env> { cache: &'env Cache, workspace_cache: &'env WorkspaceCache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Self { Self { mode, @@ -418,7 +418,7 @@ async fn do_lock( cache: &Cache, workspace_cache: &WorkspaceCache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { let start = std::time::Instant::now(); diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs index e6f41c2af..a5953cd76 100644 --- a/crates/uv/src/commands/project/mod.rs +++ b/crates/uv/src/commands/project/mod.rs @@ -12,8 +12,8 @@ use uv_cache::{Cache, CacheBucket}; use uv_cache_key::cache_digest; use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ - Concurrency, Constraints, DependencyGroupsWithDefaults, DryRun, ExtrasSpecification, - PreviewMode, Reinstall, SourceStrategy, Upgrade, + Concurrency, Constraints, DependencyGroupsWithDefaults, DryRun, ExtrasSpecification, Preview, + PreviewFeatures, Reinstall, SourceStrategy, Upgrade, }; use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution::{DistributionDatabase, LoweredRequirement}; @@ -647,7 +647,7 @@ impl ScriptInterpreter { active: Option, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { // For now, we assume that scripts are never evaluated in the context of a workspace. let workspace = None; @@ -887,7 +887,7 @@ impl ProjectInterpreter { active: Option, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { // Resolve the Python request and requirement for the workspace. let WorkspacePython { @@ -1269,7 +1269,7 @@ impl ProjectEnvironment { cache: &Cache, dry_run: DryRun, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { // Lock the project environment to avoid synchronization issues. let _lock = ProjectInterpreter::lock(workspace) @@ -1279,7 +1279,7 @@ impl ProjectEnvironment { }) .ok(); - let upgradeable = preview.is_enabled() + let upgradeable = preview.is_enabled(PreviewFeatures::PYTHON_UPGRADE) && python .as_ref() .is_none_or(|request| !request.includes_patch()); @@ -1501,7 +1501,7 @@ impl ScriptEnvironment { cache: &Cache, dry_run: DryRun, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { // Lock the script environment to avoid synchronization issues. let _lock = ScriptInterpreter::lock(script) @@ -1658,7 +1658,7 @@ pub(crate) async fn resolve_names( cache: &Cache, workspace_cache: &WorkspaceCache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result, uv_requirements::Error> { // Partition the requirements into named and unnamed requirements. let (mut requirements, unnamed): (Vec<_>, Vec<_>) = @@ -1829,7 +1829,7 @@ pub(crate) async fn resolve_environment( concurrency: Concurrency, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { warn_on_requirements_txt_setting(&spec.requirements, settings); @@ -2017,7 +2017,7 @@ pub(crate) async fn sync_environment( concurrency: Concurrency, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { let InstallerSettingsRef { index_locations, @@ -2175,7 +2175,7 @@ pub(crate) async fn update_environment( workspace_cache: WorkspaceCache, dry_run: DryRun, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { warn_on_requirements_txt_setting(&spec, &settings.resolver); @@ -2416,7 +2416,7 @@ pub(crate) async fn init_script_python_requirement( client_builder: &BaseClientBuilder<'_>, cache: &Cache, reporter: &PythonDownloadReporter, - preview: PreviewMode, + preview: Preview, ) -> anyhow::Result { let python_request = if let Some(request) = python { // (1) Explicit request from user diff --git a/crates/uv/src/commands/project/remove.rs b/crates/uv/src/commands/project/remove.rs index 50615699e..1af4c818f 100644 --- a/crates/uv/src/commands/project/remove.rs +++ b/crates/uv/src/commands/project/remove.rs @@ -10,7 +10,7 @@ use tracing::{debug, warn}; use uv_cache::Cache; use uv_configuration::{ Concurrency, DependencyGroups, DryRun, EditableMode, ExtrasSpecification, InstallOptions, - PreviewMode, + Preview, }; use uv_fs::Simplified; use uv_normalize::{DEV_DEPENDENCIES, DefaultExtras, DefaultGroups}; @@ -60,7 +60,7 @@ pub(crate) async fn remove( no_config: bool, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { let target = if let Some(script) = script { // If we found a PEP 723 script and the user provided a project-only setting, warn. diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs index 6b44bf925..20e11db18 100644 --- a/crates/uv/src/commands/project/run.rs +++ b/crates/uv/src/commands/project/run.rs @@ -19,7 +19,7 @@ use uv_cli::ExternalCommand; use uv_client::BaseClientBuilder; use uv_configuration::{ Concurrency, Constraints, DependencyGroups, DryRun, EditableMode, ExtrasSpecification, - InstallOptions, PreviewMode, + InstallOptions, Preview, }; use uv_distribution_types::Requirement; use uv_fs::which::is_executable; @@ -94,7 +94,7 @@ pub(crate) async fn run( printer: Printer, env_file: Vec, no_env_file: bool, - preview: PreviewMode, + preview: Preview, max_recursion_depth: u32, ) -> anyhow::Result { // Check if max recursion depth was exceeded. This most commonly happens diff --git a/crates/uv/src/commands/project/sync.rs b/crates/uv/src/commands/project/sync.rs index cbce0082a..7d1b0f3ce 100644 --- a/crates/uv/src/commands/project/sync.rs +++ b/crates/uv/src/commands/project/sync.rs @@ -14,7 +14,7 @@ use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ Concurrency, Constraints, DependencyGroups, DependencyGroupsWithDefaults, DryRun, EditableMode, ExtrasSpecification, ExtrasSpecificationWithDefaults, HashCheckingMode, InstallOptions, - PreviewMode, TargetTriple, + Preview, PreviewFeatures, TargetTriple, }; use uv_dispatch::BuildDispatch; use uv_distribution_types::{ @@ -77,12 +77,14 @@ pub(crate) async fn sync( no_config: bool, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, output_format: SyncFormat, ) -> Result { - if preview.is_enabled() && matches!(output_format, SyncFormat::Json) { + if preview.is_enabled(PreviewFeatures::JSON_OUTPUT) && matches!(output_format, SyncFormat::Json) + { warn_user!( - "The `--output-format json` option is experimental and the schema may change without warning. Pass `--preview` to disable this warning." + "The `--output-format json` option is experimental and the schema may change without warning. Pass `--preview-features {}` to disable this warning.", + PreviewFeatures::JSON_OUTPUT ); } @@ -564,7 +566,7 @@ pub(super) async fn do_sync( workspace_cache: WorkspaceCache, dry_run: DryRun, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result<(), ProjectError> { // Extract the project settings. let InstallerSettingsRef { diff --git a/crates/uv/src/commands/project/tree.rs b/crates/uv/src/commands/project/tree.rs index 756820dc7..1d594bd53 100644 --- a/crates/uv/src/commands/project/tree.rs +++ b/crates/uv/src/commands/project/tree.rs @@ -7,7 +7,7 @@ use tokio::sync::Semaphore; use uv_cache::{Cache, Refresh}; use uv_cache_info::Timestamp; use uv_client::RegistryClientBuilder; -use uv_configuration::{Concurrency, DependencyGroups, PreviewMode, TargetTriple}; +use uv_configuration::{Concurrency, DependencyGroups, Preview, TargetTriple}; use uv_distribution_types::IndexCapabilities; use uv_normalize::DefaultGroups; use uv_pep508::PackageName; @@ -57,7 +57,7 @@ pub(crate) async fn tree( no_config: bool, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { // Find the project requirements. let workspace_cache = WorkspaceCache::default(); diff --git a/crates/uv/src/commands/project/version.rs b/crates/uv/src/commands/project/version.rs index c4b32485d..566573594 100644 --- a/crates/uv/src/commands/project/version.rs +++ b/crates/uv/src/commands/project/version.rs @@ -11,7 +11,7 @@ use uv_cli::version::VersionInfo; use uv_cli::{VersionBump, VersionFormat}; use uv_configuration::{ Concurrency, DependencyGroups, DependencyGroupsWithDefaults, DryRun, EditableMode, - ExtrasSpecification, InstallOptions, PreviewMode, + ExtrasSpecification, InstallOptions, Preview, }; use uv_fs::Simplified; use uv_normalize::DefaultExtras; @@ -76,7 +76,7 @@ pub(crate) async fn project_version( no_config: bool, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { // Read the metadata let project = find_target(project_dir, package.as_ref(), explicit_project).await?; @@ -414,7 +414,7 @@ async fn print_frozen_version( short: bool, output_format: VersionFormat, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { // Discover the interpreter (this is the same interpreter --no-sync uses). let interpreter = ProjectInterpreter::discover( @@ -509,7 +509,7 @@ async fn lock_and_sync( no_config: bool, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { // If frozen, don't touch the lock or sync at all if frozen { diff --git a/crates/uv/src/commands/python/find.rs b/crates/uv/src/commands/python/find.rs index e188e9d20..806e670a8 100644 --- a/crates/uv/src/commands/python/find.rs +++ b/crates/uv/src/commands/python/find.rs @@ -3,7 +3,7 @@ use std::fmt::Write; use std::path::Path; use uv_cache::Cache; -use uv_configuration::{DependencyGroupsWithDefaults, PreviewMode}; +use uv_configuration::{DependencyGroupsWithDefaults, Preview}; use uv_fs::Simplified; use uv_python::{ EnvironmentPreference, PythonDownloads, PythonInstallation, PythonPreference, PythonRequest, @@ -32,7 +32,7 @@ pub(crate) async fn find( python_preference: PythonPreference, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { let environment_preference = if system { EnvironmentPreference::OnlySystem @@ -123,7 +123,7 @@ pub(crate) async fn find_script( no_config: bool, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { let interpreter = match ScriptInterpreter::discover( script, diff --git a/crates/uv/src/commands/python/install.rs b/crates/uv/src/commands/python/install.rs index e54c44424..f2082ce8a 100644 --- a/crates/uv/src/commands/python/install.rs +++ b/crates/uv/src/commands/python/install.rs @@ -14,7 +14,7 @@ use owo_colors::OwoColorize; use rustc_hash::{FxHashMap, FxHashSet}; use tracing::{debug, trace}; -use uv_configuration::PreviewMode; +use uv_configuration::{Preview, PreviewFeatures}; use uv_fs::Simplified; use uv_python::downloads::{ self, ArchRequest, DownloadResult, ManagedPythonDownload, PythonDownloadRequest, @@ -161,7 +161,7 @@ pub(crate) async fn install( default: bool, python_downloads: PythonDownloads, no_config: bool, - preview: PreviewMode, + preview: Preview, printer: Printer, ) -> Result { let start = std::time::Instant::now(); @@ -170,15 +170,17 @@ pub(crate) async fn install( // `--default` is used. It's not clear how this overlaps with a global Python pin, but I'd be // surprised if `uv python find` returned the "newest" Python version rather than the one I just // installed with the `--default` flag. - if default && !preview.is_enabled() { + if default && !preview.is_enabled(PreviewFeatures::PYTHON_INSTALL_DEFAULT) { warn_user!( - "The `--default` option is experimental and may change without warning. Pass `--preview` to disable this warning" + "The `--default` option is experimental and may change without warning. Pass `--preview-features {}` to disable this warning", + PreviewFeatures::PYTHON_INSTALL_DEFAULT ); } - if upgrade && preview.is_disabled() { + if upgrade && !preview.is_enabled(PreviewFeatures::PYTHON_UPGRADE) { warn_user!( - "`uv python upgrade` is experimental and may change without warning. Pass `--preview` to disable this warning" + "`uv python upgrade` is experimental and may change without warning. Pass `--preview-features {}` to disable this warning", + PreviewFeatures::PYTHON_UPGRADE ); } @@ -737,12 +739,13 @@ fn create_bin_links( installations: &[&ManagedPythonInstallation], changelog: &mut Changelog, errors: &mut Vec<(InstallErrorKind, PythonInstallationKey, Error)>, - preview: PreviewMode, + preview: Preview, ) { // TODO(zanieb): We want more feedback on the `is_default_install` behavior before stabilizing // it. In particular, it may be confusing because it does not apply when versions are loaded // from a `.python-version` file. - let targets = if (default || (is_default_install && preview.is_enabled())) + let targets = if (default + || (is_default_install && preview.is_enabled(PreviewFeatures::PYTHON_INSTALL_DEFAULT))) && first_request.matches_installation(installation) { vec![ diff --git a/crates/uv/src/commands/python/list.rs b/crates/uv/src/commands/python/list.rs index 17528a11e..c30eecf83 100644 --- a/crates/uv/src/commands/python/list.rs +++ b/crates/uv/src/commands/python/list.rs @@ -2,7 +2,7 @@ use serde::Serialize; use std::collections::BTreeSet; use std::fmt::Write; use uv_cli::PythonListFormat; -use uv_configuration::PreviewMode; +use uv_configuration::Preview; use uv_pep440::Version; use anyhow::Result; @@ -65,7 +65,7 @@ pub(crate) async fn list( python_downloads: PythonDownloads, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { let request = request.as_deref().map(PythonRequest::parse); let base_download_request = if python_preference == PythonPreference::OnlySystem { diff --git a/crates/uv/src/commands/python/pin.rs b/crates/uv/src/commands/python/pin.rs index 0e78e6b5c..064cc780c 100644 --- a/crates/uv/src/commands/python/pin.rs +++ b/crates/uv/src/commands/python/pin.rs @@ -8,7 +8,7 @@ use tracing::debug; use uv_cache::Cache; use uv_client::BaseClientBuilder; -use uv_configuration::{DependencyGroupsWithDefaults, PreviewMode}; +use uv_configuration::{DependencyGroupsWithDefaults, Preview}; use uv_fs::Simplified; use uv_python::{ EnvironmentPreference, PYTHON_VERSION_FILENAME, PythonDownloads, PythonInstallation, @@ -39,7 +39,7 @@ pub(crate) async fn pin( network_settings: NetworkSettings, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { let workspace_cache = WorkspaceCache::default(); let virtual_project = if no_project { @@ -270,7 +270,7 @@ fn warn_if_existing_pin_incompatible_with_project( virtual_project: &VirtualProject, python_preference: PythonPreference, cache: &Cache, - preview: PreviewMode, + preview: Preview, ) { // Check if the pinned version is compatible with the project. if let Some(pin_version) = pep440_version_from_request(pin) { diff --git a/crates/uv/src/commands/python/uninstall.rs b/crates/uv/src/commands/python/uninstall.rs index 3370e1f6f..b58b5831a 100644 --- a/crates/uv/src/commands/python/uninstall.rs +++ b/crates/uv/src/commands/python/uninstall.rs @@ -11,7 +11,7 @@ use owo_colors::OwoColorize; use rustc_hash::{FxHashMap, FxHashSet}; use tracing::{debug, warn}; -use uv_configuration::PreviewMode; +use uv_configuration::Preview; use uv_fs::Simplified; use uv_python::downloads::PythonDownloadRequest; use uv_python::managed::{ @@ -30,7 +30,7 @@ pub(crate) async fn uninstall( targets: Vec, all: bool, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { let installations = ManagedPythonInstallations::from_settings(install_dir)?.init()?; @@ -66,7 +66,7 @@ async fn do_uninstall( targets: Vec, all: bool, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { let start = std::time::Instant::now(); diff --git a/crates/uv/src/commands/tool/common.rs b/crates/uv/src/commands/tool/common.rs index b24a64e25..5647afa32 100644 --- a/crates/uv/src/commands/tool/common.rs +++ b/crates/uv/src/commands/tool/common.rs @@ -7,7 +7,7 @@ use std::{collections::BTreeSet, ffi::OsString}; use tracing::{debug, warn}; use uv_cache::Cache; use uv_client::BaseClientBuilder; -use uv_configuration::PreviewMode; +use uv_configuration::Preview; use uv_distribution_types::Requirement; use uv_distribution_types::{InstalledDist, Name}; use uv_fs::Simplified; @@ -81,7 +81,7 @@ pub(crate) async fn refine_interpreter( python_preference: PythonPreference, python_downloads: PythonDownloads, cache: &Cache, - preview: PreviewMode, + preview: Preview, ) -> anyhow::Result, ProjectError> { let pip::operations::Error::Resolve(uv_resolver::ResolveError::NoSolution(no_solution_err)) = err diff --git a/crates/uv/src/commands/tool/dir.rs b/crates/uv/src/commands/tool/dir.rs index b246e701d..7f937e35e 100644 --- a/crates/uv/src/commands/tool/dir.rs +++ b/crates/uv/src/commands/tool/dir.rs @@ -2,12 +2,12 @@ use anstream::println; use anyhow::Context; use owo_colors::OwoColorize; -use uv_configuration::PreviewMode; +use uv_configuration::Preview; use uv_fs::Simplified; use uv_tool::{InstalledTools, tool_executable_dir}; /// Show the tool directory. -pub(crate) fn dir(bin: bool, _preview: PreviewMode) -> anyhow::Result<()> { +pub(crate) fn dir(bin: bool, _preview: Preview) -> anyhow::Result<()> { if bin { let executable_directory = tool_executable_dir()?; println!("{}", executable_directory.simplified_display().cyan()); diff --git a/crates/uv/src/commands/tool/install.rs b/crates/uv/src/commands/tool/install.rs index 12de5fd1f..192597e93 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; -use uv_configuration::{Concurrency, Constraints, DryRun, PreviewMode, Reinstall, Upgrade}; +use uv_configuration::{Concurrency, Constraints, DryRun, Preview, Reinstall, Upgrade}; use uv_distribution_types::{ NameRequirementSpecification, Requirement, RequirementSource, UnresolvedRequirementSpecification, @@ -62,7 +62,7 @@ pub(crate) async fn install( concurrency: Concurrency, cache: Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { let client_builder = BaseClientBuilder::new() .retries_from_env()? diff --git a/crates/uv/src/commands/tool/run.rs b/crates/uv/src/commands/tool/run.rs index 7c91b9fe9..1c86feb3a 100644 --- a/crates/uv/src/commands/tool/run.rs +++ b/crates/uv/src/commands/tool/run.rs @@ -17,7 +17,7 @@ use uv_cache_info::Timestamp; use uv_cli::ExternalCommand; use uv_client::BaseClientBuilder; use uv_configuration::Constraints; -use uv_configuration::{Concurrency, PreviewMode}; +use uv_configuration::{Concurrency, Preview}; use uv_distribution_types::InstalledDist; use uv_distribution_types::{ IndexUrl, Name, NameRequirementSpecification, Requirement, RequirementSource, @@ -101,7 +101,7 @@ pub(crate) async fn run( printer: Printer, env_file: Vec, no_env_file: bool, - preview: PreviewMode, + preview: Preview, ) -> anyhow::Result { /// Whether or not a path looks like a Python script based on the file extension. fn has_python_script_ext(path: &Path) -> bool { @@ -686,7 +686,7 @@ async fn get_or_create_environment( concurrency: Concurrency, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result<(ToolRequirement, PythonEnvironment), ProjectError> { let client_builder = BaseClientBuilder::new() .retries_from_env()? diff --git a/crates/uv/src/commands/tool/upgrade.rs b/crates/uv/src/commands/tool/upgrade.rs index 9d2d32a21..13e1f2ae3 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; -use uv_configuration::{Concurrency, Constraints, DryRun, PreviewMode}; +use uv_configuration::{Concurrency, Constraints, DryRun, Preview}; use uv_distribution_types::Requirement; use uv_fs::CWD; use uv_normalize::PackageName; @@ -47,7 +47,7 @@ pub(crate) async fn upgrade( concurrency: Concurrency, cache: &Cache, printer: Printer, - preview: PreviewMode, + preview: Preview, ) -> Result { let installed_tools = InstalledTools::from_settings()?.init()?; let _lock = installed_tools.lock().await?; @@ -221,7 +221,7 @@ async fn upgrade_tool( filesystem: &ResolverInstallerOptions, installer_metadata: bool, concurrency: Concurrency, - preview: PreviewMode, + preview: Preview, ) -> Result { // Ensure the tool is installed. let existing_tool_receipt = match installed_tools.get_tool_receipt(name) { diff --git a/crates/uv/src/commands/venv.rs b/crates/uv/src/commands/venv.rs index cd44924ab..1f2ce3dfb 100644 --- a/crates/uv/src/commands/venv.rs +++ b/crates/uv/src/commands/venv.rs @@ -11,7 +11,8 @@ use uv_cache::Cache; use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ BuildOptions, Concurrency, ConfigSettings, Constraints, DependencyGroups, IndexStrategy, - KeyringProviderType, NoBinary, NoBuild, PackageConfigSettings, PreviewMode, SourceStrategy, + KeyringProviderType, NoBinary, NoBuild, PackageConfigSettings, Preview, PreviewFeatures, + SourceStrategy, }; use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution_types::Requirement; @@ -81,7 +82,7 @@ pub(crate) async fn venv( cache: &Cache, printer: Printer, relocatable: bool, - preview: PreviewMode, + preview: Preview, ) -> Result { let workspace_cache = WorkspaceCache::default(); let project = if no_project { @@ -198,7 +199,7 @@ pub(crate) async fn venv( path.user_display().cyan() )?; - let upgradeable = preview.is_enabled() + let upgradeable = preview.is_enabled(PreviewFeatures::PYTHON_UPGRADE) && python_request .as_ref() .is_none_or(|request| !request.includes_patch()); diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index 534640f94..b563b0b8e 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -23,9 +23,9 @@ use uv_client::Connectivity; use uv_configuration::{ BuildOptions, Concurrency, ConfigSettings, DependencyGroups, DryRun, EditableMode, ExportFormat, ExtrasSpecification, HashCheckingMode, IndexStrategy, InstallOptions, - KeyringProviderType, NoBinary, NoBuild, PackageConfigSettings, PreviewMode, - ProjectBuildBackend, Reinstall, RequiredVersion, SourceStrategy, TargetTriple, TrustedHost, - TrustedPublishing, Upgrade, VersionControlSystem, + KeyringProviderType, NoBinary, NoBuild, PackageConfigSettings, Preview, ProjectBuildBackend, + Reinstall, RequiredVersion, SourceStrategy, TargetTriple, TrustedHost, TrustedPublishing, + Upgrade, VersionControlSystem, }; use uv_distribution_types::{DependencyMetadata, Index, IndexLocations, IndexUrl, Requirement}; use uv_install_wheel::LinkMode; @@ -63,7 +63,7 @@ pub(crate) struct GlobalSettings { pub(crate) network_settings: NetworkSettings, pub(crate) concurrency: Concurrency, pub(crate) show_settings: bool, - pub(crate) preview: PreviewMode, + pub(crate) preview: Preview, pub(crate) python_preference: PythonPreference, pub(crate) python_downloads: PythonDownloads, pub(crate) no_progress: bool, @@ -117,10 +117,12 @@ impl GlobalSettings { .unwrap_or_else(Concurrency::threads), }, show_settings: args.show_settings, - preview: PreviewMode::from( + preview: Preview::from_args( flag(args.preview, args.no_preview, "preview") .combine(workspace.and_then(|workspace| workspace.globals.preview)) .unwrap_or(false), + args.no_preview, + &args.preview_features, ), python_preference, python_downloads: flag( diff --git a/crates/uv/tests/it/common/mod.rs b/crates/uv/tests/it/common/mod.rs index 65483ffd6..d7fbdaa6f 100644 --- a/crates/uv/tests/it/common/mod.rs +++ b/crates/uv/tests/it/common/mod.rs @@ -21,7 +21,7 @@ use regex::Regex; use tokio::io::AsyncWriteExt; use uv_cache::Cache; -use uv_configuration::PreviewMode; +use uv_configuration::Preview; use uv_fs::Simplified; use uv_python::managed::ManagedPythonInstallations; use uv_python::{ @@ -1505,7 +1505,7 @@ pub fn python_installations_for_versions( EnvironmentPreference::OnlySystem, PythonPreference::Managed, &cache, - PreviewMode::Disabled, + Preview::default(), ) { python.into_interpreter().sys_executable().to_owned() } else { diff --git a/crates/uv/tests/it/edit.rs b/crates/uv/tests/it/edit.rs index 05527b139..7fa46f0c3 100644 --- a/crates/uv/tests/it/edit.rs +++ b/crates/uv/tests/it/edit.rs @@ -13021,7 +13021,7 @@ fn add_bounds() -> Result<()> { ----- stdout ----- ----- stderr ----- - warning: The bounds option is in preview and may change in any future release. + warning: The `bounds` option is in preview and may change in any future release. Pass `--preview-features add-bounds` to disable this warning. Resolved 2 packages in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] @@ -13061,7 +13061,7 @@ fn add_bounds() -> Result<()> { ----- stdout ----- ----- stderr ----- - warning: The bounds option is in preview and may change in any future release. + warning: The `bounds` option is in preview and may change in any future release. Pass `--preview-features add-bounds` to disable this warning. Resolved 4 packages in [TIME] Prepared 2 packages in [TIME] Installed 2 packages in [TIME] diff --git a/crates/uv/tests/it/python_install.rs b/crates/uv/tests/it/python_install.rs index 41b046026..c5f98af0d 100644 --- a/crates/uv/tests/it/python_install.rs +++ b/crates/uv/tests/it/python_install.rs @@ -630,14 +630,14 @@ fn python_install_preview() { "###); // Should be a no-op when already installed - uv_snapshot!(context.filters(), context.python_install().arg("--preview"), @r###" + uv_snapshot!(context.filters(), context.python_install().arg("--preview"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- Python is already installed. Use `uv python install ` to install another version. - "###); + "); // You can opt-in to a reinstall uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("--reinstall"), @r" @@ -1260,7 +1260,7 @@ fn python_install_default() { ----- stdout ----- ----- stderr ----- - warning: The `--default` option is experimental and may change without warning. Pass `--preview` to disable this warning + warning: The `--default` option is experimental and may change without warning. Pass `--preview-features python-install-default` to disable this warning Installed Python 3.13.5 in [TIME] + cpython-3.13.5-[PLATFORM] (python, python3) "); @@ -1294,7 +1294,7 @@ fn python_install_default() { ----- stdout ----- ----- stderr ----- - warning: The `--default` option is experimental and may change without warning. Pass `--preview` to disable this warning + warning: The `--default` option is experimental and may change without warning. Pass `--preview-features python-install-default` to disable this warning Installed Python 3.13.5 in [TIME] + cpython-3.13.5-[PLATFORM] (python, python3, python3.13) "); @@ -1379,7 +1379,7 @@ fn python_install_default() { ----- stdout ----- ----- stderr ----- - warning: The `--default` option is experimental and may change without warning. Pass `--preview` to disable this warning + warning: The `--default` option is experimental and may change without warning. Pass `--preview-features python-install-default` to disable this warning error: The `--default` flag cannot be used with multiple targets "); @@ -1390,7 +1390,7 @@ fn python_install_default() { ----- stdout ----- ----- stderr ----- - warning: The `--default` option is experimental and may change without warning. Pass `--preview` to disable this warning + warning: The `--default` option is experimental and may change without warning. Pass `--preview-features python-install-default` to disable this warning Installed Python 3.12.11 in [TIME] + cpython-3.12.11-[PLATFORM] (python, python3, python3.12) "); diff --git a/crates/uv/tests/it/show_settings.rs b/crates/uv/tests/it/show_settings.rs index bbcddd2b1..65555ba56 100644 --- a/crates/uv/tests/it/show_settings.rs +++ b/crates/uv/tests/it/show_settings.rs @@ -71,7 +71,11 @@ fn resolve_uv_toml() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -256,7 +260,11 @@ fn resolve_uv_toml() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -442,7 +450,11 @@ fn resolve_uv_toml() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -660,7 +672,11 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -847,7 +863,11 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -1010,7 +1030,11 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -1222,7 +1246,11 @@ fn resolve_index_url() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -1442,7 +1470,11 @@ fn resolve_index_url() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -1720,7 +1752,11 @@ fn resolve_find_links() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -1929,7 +1965,11 @@ fn resolve_top_level() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -2097,7 +2137,11 @@ fn resolve_top_level() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -2315,7 +2359,11 @@ fn resolve_top_level() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -2556,7 +2604,11 @@ fn resolve_user_configuration() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -2714,7 +2766,11 @@ fn resolve_user_configuration() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -2872,7 +2928,11 @@ fn resolve_user_configuration() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -3032,7 +3092,11 @@ fn resolve_user_configuration() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -3211,7 +3275,11 @@ fn resolve_tool() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -3380,7 +3448,11 @@ fn resolve_poetry_toml() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -3572,7 +3644,11 @@ fn resolve_both() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -3802,7 +3878,11 @@ fn resolve_both_special_fields() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -4111,7 +4191,11 @@ fn resolve_config_file() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -4396,7 +4480,11 @@ fn resolve_skip_empty() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -4557,7 +4645,11 @@ fn resolve_skip_empty() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -4737,7 +4829,11 @@ fn allow_insecure_host() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -4909,7 +5005,11 @@ fn index_priority() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -5129,7 +5229,11 @@ fn index_priority() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -5355,7 +5459,11 @@ fn index_priority() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -5576,7 +5684,11 @@ fn index_priority() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -5804,7 +5916,11 @@ fn index_priority() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -6025,7 +6141,11 @@ fn index_priority() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -6259,7 +6379,11 @@ fn verify_hashes() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -6410,7 +6534,11 @@ fn verify_hashes() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -6559,7 +6687,11 @@ fn verify_hashes() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -6710,7 +6842,11 @@ fn verify_hashes() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -6859,7 +6995,11 @@ fn verify_hashes() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -7009,7 +7149,11 @@ fn verify_hashes() -> anyhow::Result<()> { installs: 8, }, show_settings: true, - preview: Disabled, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, python_preference: Managed, python_downloads: Automatic, no_progress: false, @@ -7138,3 +7282,633 @@ fn verify_hashes() -> anyhow::Result<()> { Ok(()) } + +/// Test preview feature flagging. +#[test] +#[cfg_attr( + windows, + ignore = "Configuration tests are not yet supported on Windows" +)] +fn preview_features() { + let context = TestContext::new("3.12"); + + let cmd = || { + let mut cmd = context.version(); + cmd.arg("--show-settings"); + add_shared_args(cmd, context.temp_dir.path()) + }; + + uv_snapshot!(context.filters(), cmd().arg("--preview"), @r#" + success: true + exit_code: 0 + ----- stdout ----- + GlobalSettings { + required_version: None, + quiet: 0, + verbose: 0, + color: Auto, + network_settings: NetworkSettings { + connectivity: Online, + native_tls: false, + allow_insecure_host: [], + }, + concurrency: Concurrency { + downloads: 50, + builds: 16, + installs: 8, + }, + show_settings: true, + preview: Preview { + flags: PreviewFeatures( + PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS, + ), + }, + python_preference: Managed, + python_downloads: Automatic, + no_progress: false, + installer_metadata: true, + } + CacheSettings { + no_cache: false, + cache_dir: Some( + "[CACHE_DIR]/", + ), + } + VersionSettings { + value: None, + bump: [], + short: false, + output_format: Text, + dry_run: false, + locked: false, + frozen: false, + active: None, + no_sync: false, + package: None, + python: None, + install_mirrors: PythonInstallMirrors { + python_install_mirror: None, + pypy_install_mirror: None, + python_downloads_json_url: None, + }, + refresh: None( + Timestamp( + SystemTime { + tv_sec: [TIME], + tv_nsec: [TIME], + }, + ), + ), + settings: ResolverInstallerSettings { + resolver: ResolverSettings { + build_options: BuildOptions { + no_binary: None, + no_build: None, + }, + config_setting: ConfigSettings( + {}, + ), + config_settings_package: PackageConfigSettings( + {}, + ), + dependency_metadata: DependencyMetadata( + {}, + ), + exclude_newer: None, + fork_strategy: RequiresPython, + index_locations: IndexLocations { + indexes: [], + flat_index: [], + no_index: false, + }, + index_strategy: FirstIndex, + keyring_provider: Disabled, + link_mode: Clone, + no_build_isolation: false, + no_build_isolation_package: [], + prerelease: IfNecessaryOrExplicit, + resolution: Highest, + sources: Enabled, + upgrade: None, + }, + compile_bytecode: false, + reinstall: None, + }, + } + + ----- stderr ----- + "# + ); + + uv_snapshot!(context.filters(), cmd().arg("--preview").arg("--no-preview"), @r#" + success: true + exit_code: 0 + ----- stdout ----- + GlobalSettings { + required_version: None, + quiet: 0, + verbose: 0, + color: Auto, + network_settings: NetworkSettings { + connectivity: Online, + native_tls: false, + allow_insecure_host: [], + }, + concurrency: Concurrency { + downloads: 50, + builds: 16, + installs: 8, + }, + show_settings: true, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, + python_preference: Managed, + python_downloads: Automatic, + no_progress: false, + installer_metadata: true, + } + CacheSettings { + no_cache: false, + cache_dir: Some( + "[CACHE_DIR]/", + ), + } + VersionSettings { + value: None, + bump: [], + short: false, + output_format: Text, + dry_run: false, + locked: false, + frozen: false, + active: None, + no_sync: false, + package: None, + python: None, + install_mirrors: PythonInstallMirrors { + python_install_mirror: None, + pypy_install_mirror: None, + python_downloads_json_url: None, + }, + refresh: None( + Timestamp( + SystemTime { + tv_sec: [TIME], + tv_nsec: [TIME], + }, + ), + ), + settings: ResolverInstallerSettings { + resolver: ResolverSettings { + build_options: BuildOptions { + no_binary: None, + no_build: None, + }, + config_setting: ConfigSettings( + {}, + ), + config_settings_package: PackageConfigSettings( + {}, + ), + dependency_metadata: DependencyMetadata( + {}, + ), + exclude_newer: None, + fork_strategy: RequiresPython, + index_locations: IndexLocations { + indexes: [], + flat_index: [], + no_index: false, + }, + index_strategy: FirstIndex, + keyring_provider: Disabled, + link_mode: Clone, + no_build_isolation: false, + no_build_isolation_package: [], + prerelease: IfNecessaryOrExplicit, + resolution: Highest, + sources: Enabled, + upgrade: None, + }, + compile_bytecode: false, + reinstall: None, + }, + } + + ----- stderr ----- + "# + ); + + uv_snapshot!(context.filters(), cmd().arg("--preview").arg("--preview-features").arg("python-install-default"), @r#" + success: true + exit_code: 0 + ----- stdout ----- + GlobalSettings { + required_version: None, + quiet: 0, + verbose: 0, + color: Auto, + network_settings: NetworkSettings { + connectivity: Online, + native_tls: false, + allow_insecure_host: [], + }, + concurrency: Concurrency { + downloads: 50, + builds: 16, + installs: 8, + }, + show_settings: true, + preview: Preview { + flags: PreviewFeatures( + PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS, + ), + }, + python_preference: Managed, + python_downloads: Automatic, + no_progress: false, + installer_metadata: true, + } + CacheSettings { + no_cache: false, + cache_dir: Some( + "[CACHE_DIR]/", + ), + } + VersionSettings { + value: None, + bump: [], + short: false, + output_format: Text, + dry_run: false, + locked: false, + frozen: false, + active: None, + no_sync: false, + package: None, + python: None, + install_mirrors: PythonInstallMirrors { + python_install_mirror: None, + pypy_install_mirror: None, + python_downloads_json_url: None, + }, + refresh: None( + Timestamp( + SystemTime { + tv_sec: [TIME], + tv_nsec: [TIME], + }, + ), + ), + settings: ResolverInstallerSettings { + resolver: ResolverSettings { + build_options: BuildOptions { + no_binary: None, + no_build: None, + }, + config_setting: ConfigSettings( + {}, + ), + config_settings_package: PackageConfigSettings( + {}, + ), + dependency_metadata: DependencyMetadata( + {}, + ), + exclude_newer: None, + fork_strategy: RequiresPython, + index_locations: IndexLocations { + indexes: [], + flat_index: [], + no_index: false, + }, + index_strategy: FirstIndex, + keyring_provider: Disabled, + link_mode: Clone, + no_build_isolation: false, + no_build_isolation_package: [], + prerelease: IfNecessaryOrExplicit, + resolution: Highest, + sources: Enabled, + upgrade: None, + }, + compile_bytecode: false, + reinstall: None, + }, + } + + ----- stderr ----- + "# + ); + + uv_snapshot!(context.filters(), cmd().arg("--preview-features").arg("python-install-default,python-upgrade"), @r#" + success: true + exit_code: 0 + ----- stdout ----- + GlobalSettings { + required_version: None, + quiet: 0, + verbose: 0, + color: Auto, + network_settings: NetworkSettings { + connectivity: Online, + native_tls: false, + allow_insecure_host: [], + }, + concurrency: Concurrency { + downloads: 50, + builds: 16, + installs: 8, + }, + show_settings: true, + preview: Preview { + flags: PreviewFeatures( + PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE, + ), + }, + python_preference: Managed, + python_downloads: Automatic, + no_progress: false, + installer_metadata: true, + } + CacheSettings { + no_cache: false, + cache_dir: Some( + "[CACHE_DIR]/", + ), + } + VersionSettings { + value: None, + bump: [], + short: false, + output_format: Text, + dry_run: false, + locked: false, + frozen: false, + active: None, + no_sync: false, + package: None, + python: None, + install_mirrors: PythonInstallMirrors { + python_install_mirror: None, + pypy_install_mirror: None, + python_downloads_json_url: None, + }, + refresh: None( + Timestamp( + SystemTime { + tv_sec: [TIME], + tv_nsec: [TIME], + }, + ), + ), + settings: ResolverInstallerSettings { + resolver: ResolverSettings { + build_options: BuildOptions { + no_binary: None, + no_build: None, + }, + config_setting: ConfigSettings( + {}, + ), + config_settings_package: PackageConfigSettings( + {}, + ), + dependency_metadata: DependencyMetadata( + {}, + ), + exclude_newer: None, + fork_strategy: RequiresPython, + index_locations: IndexLocations { + indexes: [], + flat_index: [], + no_index: false, + }, + index_strategy: FirstIndex, + keyring_provider: Disabled, + link_mode: Clone, + no_build_isolation: false, + no_build_isolation_package: [], + prerelease: IfNecessaryOrExplicit, + resolution: Highest, + sources: Enabled, + upgrade: None, + }, + compile_bytecode: false, + reinstall: None, + }, + } + + ----- stderr ----- + "# + ); + + uv_snapshot!(context.filters(), cmd().arg("--preview-features").arg("python-install-default").arg("--preview-feature").arg("python-upgrade"), @r#" + success: true + exit_code: 0 + ----- stdout ----- + GlobalSettings { + required_version: None, + quiet: 0, + verbose: 0, + color: Auto, + network_settings: NetworkSettings { + connectivity: Online, + native_tls: false, + allow_insecure_host: [], + }, + concurrency: Concurrency { + downloads: 50, + builds: 16, + installs: 8, + }, + show_settings: true, + preview: Preview { + flags: PreviewFeatures( + PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE, + ), + }, + python_preference: Managed, + python_downloads: Automatic, + no_progress: false, + installer_metadata: true, + } + CacheSettings { + no_cache: false, + cache_dir: Some( + "[CACHE_DIR]/", + ), + } + VersionSettings { + value: None, + bump: [], + short: false, + output_format: Text, + dry_run: false, + locked: false, + frozen: false, + active: None, + no_sync: false, + package: None, + python: None, + install_mirrors: PythonInstallMirrors { + python_install_mirror: None, + pypy_install_mirror: None, + python_downloads_json_url: None, + }, + refresh: None( + Timestamp( + SystemTime { + tv_sec: [TIME], + tv_nsec: [TIME], + }, + ), + ), + settings: ResolverInstallerSettings { + resolver: ResolverSettings { + build_options: BuildOptions { + no_binary: None, + no_build: None, + }, + config_setting: ConfigSettings( + {}, + ), + config_settings_package: PackageConfigSettings( + {}, + ), + dependency_metadata: DependencyMetadata( + {}, + ), + exclude_newer: None, + fork_strategy: RequiresPython, + index_locations: IndexLocations { + indexes: [], + flat_index: [], + no_index: false, + }, + index_strategy: FirstIndex, + keyring_provider: Disabled, + link_mode: Clone, + no_build_isolation: false, + no_build_isolation_package: [], + prerelease: IfNecessaryOrExplicit, + resolution: Highest, + sources: Enabled, + upgrade: None, + }, + compile_bytecode: false, + reinstall: None, + }, + } + + ----- stderr ----- + "# + ); + + uv_snapshot!(context.filters(), cmd() + .arg("--preview-features").arg("python-install-default").arg("--preview-feature").arg("python-upgrade") + .arg("--no-preview"), @r#" + success: true + exit_code: 0 + ----- stdout ----- + GlobalSettings { + required_version: None, + quiet: 0, + verbose: 0, + color: Auto, + network_settings: NetworkSettings { + connectivity: Online, + native_tls: false, + allow_insecure_host: [], + }, + concurrency: Concurrency { + downloads: 50, + builds: 16, + installs: 8, + }, + show_settings: true, + preview: Preview { + flags: PreviewFeatures( + 0x0, + ), + }, + python_preference: Managed, + python_downloads: Automatic, + no_progress: false, + installer_metadata: true, + } + CacheSettings { + no_cache: false, + cache_dir: Some( + "[CACHE_DIR]/", + ), + } + VersionSettings { + value: None, + bump: [], + short: false, + output_format: Text, + dry_run: false, + locked: false, + frozen: false, + active: None, + no_sync: false, + package: None, + python: None, + install_mirrors: PythonInstallMirrors { + python_install_mirror: None, + pypy_install_mirror: None, + python_downloads_json_url: None, + }, + refresh: None( + Timestamp( + SystemTime { + tv_sec: [TIME], + tv_nsec: [TIME], + }, + ), + ), + settings: ResolverInstallerSettings { + resolver: ResolverSettings { + build_options: BuildOptions { + no_binary: None, + no_build: None, + }, + config_setting: ConfigSettings( + {}, + ), + config_settings_package: PackageConfigSettings( + {}, + ), + dependency_metadata: DependencyMetadata( + {}, + ), + exclude_newer: None, + fork_strategy: RequiresPython, + index_locations: IndexLocations { + indexes: [], + flat_index: [], + no_index: false, + }, + index_strategy: FirstIndex, + keyring_provider: Disabled, + link_mode: Clone, + no_build_isolation: false, + no_build_isolation_package: [], + prerelease: IfNecessaryOrExplicit, + resolution: Highest, + sources: Enabled, + upgrade: None, + }, + compile_bytecode: false, + reinstall: None, + }, + } + + ----- stderr ----- + "# + ); +} diff --git a/docs/reference/environment.md b/docs/reference/environment.md index 6b93dbe7e..1958b8780 100644 --- a/docs/reference/environment.md +++ b/docs/reference/environment.md @@ -306,6 +306,10 @@ Equivalent to the `--prerelease` command-line argument. For example, if set to Equivalent to the `--preview` argument. Enables preview mode. +### `UV_PREVIEW_FEATURES` + +Equivalent to the `--preview-features` argument. Enables specific preview features. + ### `UV_PROJECT` Equivalent to the `--project` command-line argument.