From 7efd13ca33599a00961ea4addf581c605851973a Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Sat, 20 Apr 2024 23:32:28 +0200 Subject: [PATCH] Add UV_CONSTRAINT environment variable to provide value for `--constraint` (#3162) ## Summary This PR is adding `UV_CONSTRAINT` environment variable as analogous to `PIP_CONSTRAINT` to allow providing constraint file via environment variable. Implementing this will simplify adoption of uv in testing procedure in projects that I'm involved (testing using tox). This was my motivation for opening #1841 that is closed in favor of #1789 which was closed without implementing this feature. In this implementation, I have used space as a separator as analogous to `pip`. This introduces an obvious problem if the path contains space. Another option could be to use standard separator (`:` - UNIX like, `;` - Windows). Which one did you prefer? ## Test Plan It is my first contribution and first rust coding experience. It will be nice if one could point how I should implement testing this. --- README.md | 2 ++ crates/uv/src/cli.rs | 20 ++++++++++++++++---- crates/uv/src/settings.rs | 10 ++++++++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index bc1cd8aab..7244f25ae 100644 --- a/README.md +++ b/README.md @@ -502,6 +502,8 @@ uv accepts the following command-line arguments as environment variables: index URLs, rather than limiting its search to the first index URL that contains the package. - `UV_REQUIRE_HASHES`: Equivalent to the `--require-hashes` command-line argument. If set to `true`, uv will require that all dependencies have a hash specified in the requirements file. +- 'UV_CONSTRAINT': Equivalent to the `--constraint` command-line argument. If set, uv will use this + file as the constraints file. Uses space-separated list of files. In each case, the corresponding command-line argument takes precedence over an environment variable. diff --git a/crates/uv/src/cli.rs b/crates/uv/src/cli.rs index 3555a5556..069e38836 100644 --- a/crates/uv/src/cli.rs +++ b/crates/uv/src/cli.rs @@ -224,6 +224,18 @@ fn parse_index_url(input: &str) -> Result, String> { } } +/// Parse a string into a [`PathBuf`], mapping the empty string to `None`. +fn parse_file_path(input: &str) -> Result, String> { + if input.is_empty() { + Ok(Maybe::None) + } else { + match PathBuf::from_str(input) { + Ok(path) => Ok(Maybe::Some(path)), + Err(err) => Err(err.to_string()), + } + } +} + #[derive(Args)] #[allow(clippy::struct_excessive_bools)] pub(crate) struct PipCompileArgs { @@ -240,8 +252,8 @@ pub(crate) struct PipCompileArgs { /// trigger the installation of that package. /// /// This is equivalent to pip's `--constraint` option. - #[arg(long, short)] - pub(crate) constraint: Vec, + #[arg(long, short, env = "UV_CONSTRAINT", value_delimiter = ' ', value_parser = parse_file_path)] + pub(crate) constraint: Vec>, /// Override versions using the given requirements files. /// @@ -869,8 +881,8 @@ pub(crate) struct PipInstallArgs { /// trigger the installation of that package. /// /// This is equivalent to pip's `--constraint` option. - #[arg(long, short)] - pub(crate) constraint: Vec, + #[arg(long, short, env = "UV_CONSTRAINT", value_delimiter = ' ', value_parser = parse_file_path)] + pub(crate) constraint: Vec>, /// Override versions using the given requirements files. /// diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index ba936a9ec..5a302fada 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -154,7 +154,10 @@ impl PipCompileSettings { Self { // CLI-only settings. src_file, - constraint, + constraint: constraint + .into_iter() + .filter_map(Maybe::into_option) + .collect(), r#override, refresh: Refresh::from_args(refresh, refresh_package), upgrade: Upgrade::from_args(upgrade, upgrade_package), @@ -393,7 +396,10 @@ impl PipInstallSettings { package, requirement, editable, - constraint, + constraint: constraint + .into_iter() + .filter_map(Maybe::into_option) + .collect(), r#override, upgrade: Upgrade::from_args(upgrade, upgrade_package), reinstall: Reinstall::from_args(reinstall, reinstall_package),