Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/uv-settings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ fn warn_uv_toml_masked_fields(options: &Options) {
no_cache,
cache_dir,
preview,
preview_features,
python_preference,
python_downloads,
concurrent_downloads,
Expand Down Expand Up @@ -389,6 +390,9 @@ fn warn_uv_toml_masked_fields(options: &Options) {
if preview.is_some() {
masked_fields.push("preview");
}
if preview_features.is_some() {
masked_fields.push("preview-features");
}
if python_preference.is_some() {
masked_fields.push("python-preference");
}
Expand Down
14 changes: 14 additions & 0 deletions crates/uv-settings/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,17 @@ pub struct GlobalOptions {
"#
)]
pub preview: Option<bool>,
/// Enable experimental preview features.
/// Preview features may change without warning.
/// Use comma-separated values or pass multiple times to enable multiple features.
#[option(
default = "[]",
value_type = "list[str]",
example = r#"
preview-features = ["json-output", "format"]
"#
)]
pub preview_features: Option<Vec<String>>,
/// Whether to prefer using Python installations that are already present on the system, or
/// those that are downloaded and installed by uv.
#[option(
Expand Down Expand Up @@ -2050,6 +2061,7 @@ pub struct OptionsWire {
no_cache: Option<bool>,
cache_dir: Option<PathBuf>,
preview: Option<bool>,
preview_features: Option<Vec<String>>,
python_preference: Option<PythonPreference>,
python_downloads: Option<PythonDownloads>,
concurrent_downloads: Option<NonZeroUsize>,
Expand Down Expand Up @@ -2144,6 +2156,7 @@ impl From<OptionsWire> for Options {
no_cache,
cache_dir,
preview,
preview_features,
python_preference,
python_downloads,
python_install_mirror,
Expand Down Expand Up @@ -2215,6 +2228,7 @@ impl From<OptionsWire> for Options {
no_cache,
cache_dir,
preview,
preview_features,
python_preference,
python_downloads,
concurrent_downloads,
Expand Down
49 changes: 42 additions & 7 deletions crates/uv/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,48 @@ impl GlobalSettings {
.unwrap_or_else(Concurrency::threads),
},
show_settings: args.show_settings,
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,
),
preview: {
// Start with command line features (highest precedence)
let mut all_preview_features = args.preview_features.clone();

// Add environment variable features if no command line features were specified
if all_preview_features.is_empty() {
if let Ok(env_value) = std::env::var(EnvVars::UV_PREVIEW_FEATURES) {
let env_features = env_value
.split(',')
.map(|feature| feature.trim().to_string())
.filter(|feature| !feature.is_empty())
.collect::<Vec<_>>();

for feature in env_features {
if let Ok(parsed) = feature.parse::<uv_preview::PreviewFeatures>() {
all_preview_features.push(parsed);
}
}
}
}

// Add configuration features if no command line or environment features were specified
if all_preview_features.is_empty() {
if let Some(config_features) =
workspace.and_then(|workspace| workspace.globals.preview_features.as_ref())
{
for feature in config_features {
if let Ok(parsed) = feature.parse::<uv_preview::PreviewFeatures>() {
all_preview_features.push(parsed);
}
}
}
}

Preview::from_args(
flag(args.preview, args.no_preview, "preview")
.combine(workspace.and_then(|workspace| workspace.globals.preview))
.unwrap_or(false),
args.no_preview,
&all_preview_features,
)
},
python_preference,
python_downloads: flag(
args.allow_python_downloads,
Expand Down
60 changes: 60 additions & 0 deletions crates/uv/tests/it/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,66 @@ impl TestContext {
self
}

/// Set the "concurrent builds" for all commands in this context.
pub fn with_concurrent_builds(mut self, concurrent_builds: &str) -> Self {
self.extra_env.push((
EnvVars::UV_CONCURRENT_BUILDS.into(),
concurrent_builds.into(),
));
self
}

/// Set the "concurrent downloads" for all commands in this context.
pub fn with_concurrent_downloads(mut self, concurrent_downloads: &str) -> Self {
self.extra_env.push((
EnvVars::UV_CONCURRENT_DOWNLOADS.into(),
concurrent_downloads.into(),
));
self
}

/// Set the "link mode" for all commands in this context.
pub fn with_link_mode(mut self, link_mode: &str) -> Self {
self.extra_env
.push((EnvVars::UV_LINK_MODE.into(), link_mode.into()));
self
}

/// Set the "http retries" for all commands in this context.
pub fn with_http_retries(mut self, http_retries: &str) -> Self {
self.extra_env
.push((EnvVars::UV_HTTP_RETRIES.into(), http_retries.into()));
self
}

/// Set the "color" preference for all commands in this context.
pub fn with_color(mut self, color: &str) -> Self {
// Color is controlled by NO_COLOR, FORCE_COLOR, and CLICOLOR_FORCE environment variables
// See: crates/uv/src/settings.rs GlobalSettings::resolve
match color.to_lowercase().as_str() {
"never" => {
self.extra_env.push((EnvVars::NO_COLOR.into(), "1".into()));
}
"always" => {
self.extra_env
.push((EnvVars::FORCE_COLOR.into(), "1".into()));
}
"auto" => {
// Auto is the default behavior, but we explicitly remove color-related env vars
// to ensure consistent behavior across environments
self.extra_env.push((EnvVars::NO_COLOR.into(), "".into()));
self.extra_env
.push((EnvVars::FORCE_COLOR.into(), "".into()));
self.extra_env
.push((EnvVars::CLICOLOR_FORCE.into(), "".into()));
}
_ => {
panic!("Invalid color value: {color}. Use 'never', 'always', or 'auto'");
}
}
self
}

/// Add extra standard filtering for messages like "Resolved 10 packages" which
/// can differ between platforms.
///
Expand Down
Loading
Loading