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

View file

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