Coerce empty string values to None for UV_PYTHON env var (#7878)

## Summary

Closes #7841. If there are other env vars that would also benefit from
this value parser, please let me know and I can add them to this PR.

## Test Plan

When running the same example from the linked issue, it now works:

```
UV_PYTHON= cargo run -- init x
   Compiling ...
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 29.06s
     Running `target/debug/uv init x`
Initialized project `x` at `/Users/krishnanchandra/Projects/uv/x`
```
This commit is contained in:
Krishnan Chandra 2024-10-04 07:32:03 -04:00 committed by GitHub
parent c591636dbe
commit d73b25385f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 97 additions and 66 deletions

View file

@ -827,6 +827,16 @@ fn parse_maybe_file_path(input: &str) -> Result<Maybe<PathBuf>, String> {
}
}
// Parse a string, mapping the empty string to `None`.
#[allow(clippy::unnecessary_wraps)]
fn parse_maybe_string(input: &str) -> Result<Maybe<String>, String> {
if input.is_empty() {
Ok(Maybe::None)
} else {
Ok(Maybe::Some(input.to_string()))
}
}
#[derive(Args)]
#[allow(clippy::struct_excessive_bools)]
pub struct PipCompileArgs {
@ -967,8 +977,8 @@ pub struct PipCompileArgs {
///
/// See `uv help python` for details on Python discovery and supported
/// request formats.
#[arg(long, verbatim_doc_comment, help_heading = "Python options")]
pub python: Option<String>,
#[arg(long, verbatim_doc_comment, help_heading = "Python options", value_parser = parse_maybe_string)]
pub python: Option<Maybe<String>>,
/// Install packages into the system Python environment.
///
@ -1221,9 +1231,10 @@ pub struct PipSyncArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
/// Install packages into the system Python environment.
///
@ -1503,9 +1514,10 @@ pub struct PipInstallArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
/// Install packages into the system Python environment.
///
@ -1668,9 +1680,10 @@ pub struct PipUninstallArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
/// Attempt to use `keyring` for authentication for remote requirements files.
///
@ -1776,9 +1789,10 @@ pub struct PipFreezeArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
/// List packages in the system Python environment.
///
@ -1840,9 +1854,10 @@ pub struct PipListArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
/// List packages in the system Python environment.
///
@ -1880,9 +1895,10 @@ pub struct PipCheckArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
/// Check packages in the system Python environment.
///
@ -1928,9 +1944,10 @@ pub struct PipShowArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
/// Show a package in the system Python environment.
///
@ -1983,9 +2000,10 @@ pub struct PipTreeArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
/// List packages in the system Python environment.
///
@ -2118,9 +2136,10 @@ pub struct BuildArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
#[command(flatten)]
pub resolver: ResolverArgs,
@ -2147,9 +2166,10 @@ pub struct VenvArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
/// Ignore virtual environments when searching for the Python interpreter.
///
@ -2435,9 +2455,10 @@ pub struct InitArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
}
#[derive(Args)]
@ -2610,9 +2631,10 @@ pub struct RunArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
/// Whether to show resolver and installer output from any environment modifications.
///
@ -2752,9 +2774,10 @@ pub struct SyncArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
}
#[derive(Args)]
@ -2795,9 +2818,10 @@ pub struct LockArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
}
#[derive(Args)]
@ -2917,9 +2941,10 @@ pub struct AddArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
}
#[derive(Args)]
@ -2983,9 +3008,10 @@ pub struct RemoveArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
}
#[derive(Args)]
@ -3057,9 +3083,10 @@ pub struct TreeArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
}
#[derive(Args)]
@ -3181,9 +3208,10 @@ pub struct ExportArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
}
#[derive(Args)]
@ -3330,9 +3358,10 @@ pub struct ToolRunArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
/// Whether to show resolver and installer output from any environment modifications.
///
@ -3391,9 +3420,10 @@ pub struct ToolInstallArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
}
#[derive(Args)]
@ -3467,9 +3497,10 @@ pub struct ToolUpgradeArgs {
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
help_heading = "Python options",
value_parser = parse_maybe_string,
)]
pub python: Option<String>,
pub python: Option<Maybe<String>>,
#[command(flatten)]
pub installer: ResolverInstallerArgs,

View file

@ -208,7 +208,7 @@ impl InitSettings {
no_readme,
no_pin_python,
no_workspace,
python,
python: python.and_then(Maybe::into_option),
}
}
}
@ -286,7 +286,7 @@ impl RunSettings {
package,
no_project,
no_sync,
python,
python: python.and_then(Maybe::into_option),
refresh: Refresh::from(refresh),
settings: ResolverInstallerSettings::combine(
resolver_installer_options(installer, build),
@ -364,7 +364,7 @@ impl ToolRunSettings {
.collect(),
isolated,
show_resolution,
python,
python: python.and_then(Maybe::into_option),
refresh: Refresh::from(refresh),
settings: ResolverInstallerSettings::combine(
resolver_installer_options(installer, build),
@ -424,7 +424,7 @@ impl ToolInstallSettings {
.into_iter()
.filter_map(Maybe::into_option)
.collect(),
python,
python: python.and_then(Maybe::into_option),
force,
editable,
refresh: Refresh::from(refresh),
@ -472,7 +472,7 @@ impl ToolUpgradeSettings {
Self {
name: if all { vec![] } else { name },
python,
python: python.and_then(Maybe::into_option),
args,
filesystem,
}
@ -744,7 +744,7 @@ impl SyncSettings {
Modifications::Sufficient
},
package,
python,
python: python.and_then(Maybe::into_option),
refresh: Refresh::from(refresh),
settings,
}
@ -778,7 +778,7 @@ impl LockSettings {
Self {
locked,
frozen,
python,
python: python.and_then(Maybe::into_option),
refresh: Refresh::from(refresh),
settings: ResolverSettings::combine(resolver_options(resolver, build), filesystem),
}
@ -856,7 +856,7 @@ impl AddSettings {
branch,
package,
script,
python,
python: python.and_then(Maybe::into_option),
editable: flag(editable, no_editable),
extras: extra.unwrap_or_default(),
refresh: Refresh::from(refresh),
@ -919,7 +919,7 @@ impl RemoveSettings {
dependency_type,
package,
script,
python,
python: python.and_then(Maybe::into_option),
refresh: Refresh::from(refresh),
settings: ResolverInstallerSettings::combine(
resolver_installer_options(installer, build),
@ -973,7 +973,7 @@ impl TreeSettings {
invert: tree.invert,
python_version,
python_platform,
python,
python: python.and_then(Maybe::into_option),
resolver: ResolverSettings::combine(resolver_options(resolver, build), filesystem),
}
}
@ -1044,7 +1044,7 @@ impl ExportSettings {
output_file,
locked,
frozen,
python,
python: python.and_then(Maybe::into_option),
refresh: Refresh::from(refresh),
settings: ResolverSettings::combine(resolver_options(resolver, build), filesystem),
}
@ -1173,7 +1173,7 @@ impl PipCompileSettings {
refresh: Refresh::from(refresh),
settings: PipSettings::combine(
PipOptions {
python,
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
no_build: flag(no_build, build),
no_binary,
@ -1266,7 +1266,7 @@ impl PipSyncSettings {
refresh: Refresh::from(refresh),
settings: PipSettings::combine(
PipOptions {
python,
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
break_system_packages: flag(break_system_packages, no_break_system_packages),
target,
@ -1398,7 +1398,7 @@ impl PipInstallSettings {
refresh: Refresh::from(refresh),
settings: PipSettings::combine(
PipOptions {
python,
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
break_system_packages: flag(break_system_packages, no_break_system_packages),
target,
@ -1454,7 +1454,7 @@ impl PipUninstallSettings {
requirement,
settings: PipSettings::combine(
PipOptions {
python,
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
break_system_packages: flag(break_system_packages, no_break_system_packages),
target,
@ -1499,7 +1499,7 @@ impl PipFreezeSettings {
exclude_editable,
settings: PipSettings::combine(
PipOptions {
python,
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
strict: flag(strict, no_strict),
..PipOptions::default()
@ -1542,7 +1542,7 @@ impl PipListSettings {
format,
settings: PipSettings::combine(
PipOptions {
python,
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
strict: flag(strict, no_strict),
..PipOptions::default()
@ -1578,7 +1578,7 @@ impl PipShowSettings {
package,
settings: PipSettings::combine(
PipOptions {
python,
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
strict: flag(strict, no_strict),
..PipOptions::default()
@ -1627,7 +1627,7 @@ impl PipTreeSettings {
// Shared settings.
shared: PipSettings::combine(
PipOptions {
python,
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
strict: flag(strict, no_strict),
..PipOptions::default()
@ -1657,7 +1657,7 @@ impl PipCheckSettings {
Self {
settings: PipSettings::combine(
PipOptions {
python,
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
..PipOptions::default()
},
@ -1724,7 +1724,7 @@ impl BuildSettings {
flag(require_hashes, no_require_hashes).unwrap_or_default(),
flag(verify_hashes, no_verify_hashes).unwrap_or_default(),
),
python,
python: python.and_then(Maybe::into_option),
refresh: Refresh::from(refresh),
settings: ResolverSettings::combine(resolver_options(resolver, build), filesystem),
}
@ -1778,7 +1778,7 @@ impl VenvSettings {
relocatable,
settings: PipSettings::combine(
PipOptions {
python,
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
index_strategy,
keyring_provider,