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).
This commit is contained in:
Zanie Blue 2025-07-25 11:01:57 -05:00 committed by GitHub
parent 9376cf5482
commit bfb4bc2aeb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
55 changed files with 1327 additions and 304 deletions

2
Cargo.lock generated
View file

@ -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]]

View file

@ -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 {

View file

@ -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<OsString, OsString>,
level: BuildOutput,
concurrent_builds: usize,
preview: PreviewMode,
preview: Preview,
) -> Result<Self, Error> {
let temp_dir = build_context.cache().venv_dir()?;

View file

@ -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<Vec<Maybe<TrustedHost>>>,
/// 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<PreviewFeatures>,
/// Avoid discovering a `pyproject.toml` or `uv.toml` file.
///
/// Normally, configuration files are discovered in the current directory,

View file

@ -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 }

View file

@ -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<Self, Self::Err> {
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<bool> 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();
}
}

View file

@ -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()

View file

@ -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,

View file

@ -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<dyn Iterator<Item = Result<(PythonSource, PathBuf), Error>> + '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<dyn Iterator<Item = Result<(PythonSource, PathBuf), Error>> + '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<Item = Result<(PythonSource, Interpreter), Error>> + '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<dyn Iterator<Item = Result<FindPythonResult, Error>> + '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<FindPythonResult, Error> {
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<FindPythonResult, Error> {
debug!("Starting Python discovery for {}", request);

View file

@ -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<Self, Error> {
let installation = match find_python_installation(
request,

View file

@ -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<Self, Error> {
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<Self, Error> {
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<Self, Error> {
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<Self, Error> {
let installations = ManagedPythonInstallations::from_settings(None)?.init()?;
let installations_dir = installations.root();

View file

@ -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(),
)
})??;

View file

@ -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<Self> {
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<Self> {
PythonMinorVersionLink::from_executable(
installation.executable(false).as_path(),

View file

@ -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";

View file

@ -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<PythonEnvironment, Error> {
let environment_path = self.tool_dir(name);

View file

@ -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<PythonEnvironment, Error> {
// Create the virtualenv at the given location.
let virtualenv = virtualenv::create(

View file

@ -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<VirtualEnvironment, Error> {
// Determine the base Python executable; that is, the Python executable that should be
// considered the "base" for the virtual environment.

View file

@ -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<ExitStatus> {
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<BuildResult> {
// 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<Vec<BuildMessage>, Error> {
let output_dir = if let Some(output_dir) = output_dir {
Cow::Owned(std::path::absolute(output_dir)?)

View file

@ -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<ExitStatus> {
let start = Instant::now();

View file

@ -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<ExitStatus> {
// If the user provides a `pyproject.toml` or other TOML file as the output file, raise an
// error.

View file

@ -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<Vec<PathBuf>>,
cache: &Cache,
printer: Printer,
preview: PreviewMode,
preview: Preview,
) -> Result<ExitStatus> {
// Detect the current Python interpreter.
let environment = PythonEnvironment::find(

View file

@ -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<ExitStatus> {
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
);
}
}

View file

@ -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<ExitStatus> {
// Disallow `--outdated` with `--format freeze`.
if outdated && matches!(format, ListFormat::Freeze) {

View file

@ -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<ExitStatus> {
if packages.is_empty() {
#[allow(clippy::print_stderr)]

View file

@ -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<ExitStatus> {
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
);
}
}

View file

@ -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<ExitStatus> {
// Detect the current Python interpreter.
let environment = PythonEnvironment::find(

View file

@ -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<ExitStatus> {
let start = std::time::Instant::now();

View file

@ -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<ExitStatus> {
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 {

View file

@ -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<Self, ProjectError> {
let interpreter = Self::base_interpreter(interpreter, cache)?;

View file

@ -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<ExitStatus> {
// Identify the target.
let workspace_cache = WorkspaceCache::default();

View file

@ -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<ExitStatus> {
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();

View file

@ -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<ExitStatus> {
// 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<LockResult, ProjectError> {
let start = std::time::Instant::now();

View file

@ -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<bool>,
cache: &Cache,
printer: Printer,
preview: PreviewMode,
preview: Preview,
) -> Result<Self, ProjectError> {
// 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<bool>,
cache: &Cache,
printer: Printer,
preview: PreviewMode,
preview: Preview,
) -> Result<Self, ProjectError> {
// 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<Self, ProjectError> {
// 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<Self, ProjectError> {
// 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<Vec<Requirement>, 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<ResolverOutput, ProjectError> {
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<PythonEnvironment, ProjectError> {
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<EnvironmentUpdate, ProjectError> {
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<RequiresPython> {
let python_request = if let Some(request) = python {
// (1) Explicit request from user

View file

@ -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<ExitStatus> {
let target = if let Some(script) = script {
// If we found a PEP 723 script and the user provided a project-only setting, warn.

View file

@ -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<PathBuf>,
no_env_file: bool,
preview: PreviewMode,
preview: Preview,
max_recursion_depth: u32,
) -> anyhow::Result<ExitStatus> {
// Check if max recursion depth was exceeded. This most commonly happens

View file

@ -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<ExitStatus> {
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 {

View file

@ -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<ExitStatus> {
// Find the project requirements.
let workspace_cache = WorkspaceCache::default();

View file

@ -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<ExitStatus> {
// 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<ExitStatus> {
// 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<ExitStatus> {
// If frozen, don't touch the lock or sync at all
if frozen {

View file

@ -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<ExitStatus> {
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<ExitStatus> {
let interpreter = match ScriptInterpreter::discover(
script,

View file

@ -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<ExitStatus> {
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![

View file

@ -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<ExitStatus> {
let request = request.as_deref().map(PythonRequest::parse);
let base_download_request = if python_preference == PythonPreference::OnlySystem {

View file

@ -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<ExitStatus> {
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) {

View file

@ -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<String>,
all: bool,
printer: Printer,
preview: PreviewMode,
preview: Preview,
) -> Result<ExitStatus> {
let installations = ManagedPythonInstallations::from_settings(install_dir)?.init()?;
@ -66,7 +66,7 @@ async fn do_uninstall(
targets: Vec<String>,
all: bool,
printer: Printer,
preview: PreviewMode,
preview: Preview,
) -> Result<ExitStatus> {
let start = std::time::Instant::now();

View file

@ -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<Option<Interpreter>, ProjectError> {
let pip::operations::Error::Resolve(uv_resolver::ResolveError::NoSolution(no_solution_err)) =
err

View file

@ -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());

View file

@ -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<ExitStatus> {
let client_builder = BaseClientBuilder::new()
.retries_from_env()?

View file

@ -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<PathBuf>,
no_env_file: bool,
preview: PreviewMode,
preview: Preview,
) -> anyhow::Result<ExitStatus> {
/// 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()?

View file

@ -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<ExitStatus> {
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<UpgradeOutcome> {
// Ensure the tool is installed.
let existing_tool_receipt = match installed_tools.get_tool_receipt(name) {

View file

@ -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<ExitStatus> {
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());

View file

@ -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(

View file

@ -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 {

View file

@ -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]

View file

@ -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 <request>` 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)
");

File diff suppressed because it is too large Load diff

View file

@ -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.