Skip to content

Commit

Permalink
Omit local segments when adding uv add bounds (#5753)
Browse files Browse the repository at this point in the history
## Summary

Closes #5752.
  • Loading branch information
charliermarsh committed Aug 3, 2024
1 parent ba924d2 commit 257007c
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 7 deletions.
12 changes: 6 additions & 6 deletions crates/uv-workspace/src/pyproject_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ impl PyProjectTomlMut {
pub fn set_dependency_minimum_version(
&mut self,
index: usize,
version: &Version,
version: Version,
) -> Result<(), Error> {
// Get or create `project.dependencies`.
let dependencies = self
Expand All @@ -216,7 +216,7 @@ impl PyProjectTomlMut {
.and_then(try_parse_requirement)
.ok_or(Error::MalformedDependencies)?;
req.version_or_url = Some(VersionOrUrl::VersionSpecifier(VersionSpecifiers::from(
VersionSpecifier::greater_than_equal_version(version.clone()),
VersionSpecifier::greater_than_equal_version(version),
)));
dependencies.replace(index, req.to_string());

Expand All @@ -227,7 +227,7 @@ impl PyProjectTomlMut {
pub fn set_dev_dependency_minimum_version(
&mut self,
index: usize,
version: &Version,
version: Version,
) -> Result<(), Error> {
// Get or create `tool.uv.dev-dependencies`.
let dev_dependencies = self
Expand All @@ -254,7 +254,7 @@ impl PyProjectTomlMut {
.and_then(try_parse_requirement)
.ok_or(Error::MalformedDependencies)?;
req.version_or_url = Some(VersionOrUrl::VersionSpecifier(VersionSpecifiers::from(
VersionSpecifier::greater_than_equal_version(version.clone()),
VersionSpecifier::greater_than_equal_version(version),
)));
dev_dependencies.replace(index, req.to_string());

Expand All @@ -266,7 +266,7 @@ impl PyProjectTomlMut {
&mut self,
group: &ExtraName,
index: usize,
version: &Version,
version: Version,
) -> Result<(), Error> {
// Get or create `project.optional-dependencies`.
let optional_dependencies = self
Expand Down Expand Up @@ -295,7 +295,7 @@ impl PyProjectTomlMut {
.and_then(try_parse_requirement)
.ok_or(Error::MalformedDependencies)?;
req.version_or_url = Some(VersionOrUrl::VersionSpecifier(VersionSpecifiers::from(
VersionSpecifier::greater_than_equal_version(version.clone()),
VersionSpecifier::greater_than_equal_version(version),
)));
group.replace(index, req.to_string());

Expand Down
4 changes: 4 additions & 0 deletions crates/uv/src/commands/project/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,10 @@ pub(crate) async fn add(
continue;
};

// Drop the local version identifier, which isn't permitted in `>=` constraints.
// For example, convert `1.2.3+local` to `1.2.3`.
let minimum = (*minimum).clone().without_local();

match edit.dependency_type {
DependencyType::Production => {
pyproject.set_dependency_minimum_version(*index, minimum)?;
Expand Down
83 changes: 82 additions & 1 deletion crates/uv/tests/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use assert_fs::prelude::*;
use indoc::indoc;
use insta::assert_snapshot;

use crate::common::packse_index_url;
use common::{uv_snapshot, TestContext};

mod common;
Expand Down Expand Up @@ -2400,7 +2401,7 @@ fn add_lower_bound_dev() -> Result<()> {
Ok(())
}

/// Set a lower bound when adding unconstrained dev dependencies.
/// Set a lower bound when adding unconstrained optional dependencies.
#[test]
fn add_lower_bound_optional() -> Result<()> {
let context = TestContext::new("3.12");
Expand Down Expand Up @@ -2509,3 +2510,83 @@ fn add_lower_bound_optional() -> Result<()> {

Ok(())
}

/// Omit the local segment when adding dependencies (since `>=1.2.3+local` is invalid).
#[test]
fn add_lower_bound_local() -> Result<()> {
let context = TestContext::new("3.12");

let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! {r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = []
"#})?;

// Adding `torch` should include a lower-bound, but no local segment.
uv_snapshot!(context.filters(), context.add(&["local-simple-a"]).arg("--extra-index-url").arg(packse_index_url()).env_remove("UV_EXCLUDE_NEWER"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
warning: `uv add` is experimental and may change without warning
Resolved 2 packages in [TIME]
Prepared 2 packages in [TIME]
Installed 2 packages in [TIME]
+ local-simple-a==1.2.3+foo
+ project==0.1.0 (from file://[TEMP_DIR]/)
"###);

let pyproject_toml = fs_err::read_to_string(context.temp_dir.join("pyproject.toml"))?;

insta::with_settings!({
filters => context.filters(),
}, {
assert_snapshot!(
pyproject_toml, @r###"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"local-simple-a>=1.2.3",
]
"###
);
});

let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock"))?;

insta::with_settings!({
filters => context.filters(),
}, {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
[[distribution]]
name = "local-simple-a"
version = "1.2.3+foo"
source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }
sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/local_simple_a-1.2.3+foo.tar.gz#sha256=ebd55c4a79d0a5759126657cb289ff97558902abcfb142e036b993781497edac", hash = "sha256:ebd55c4a79d0a5759126657cb289ff97558902abcfb142e036b993781497edac" }
wheels = [
{ url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/local_simple_a-1.2.3+foo-py3-none-any.whl#sha256=6f30e2e709b3e171cd734bb58705229a582587c29e0a7041227435583c7224cc", hash = "sha256:6f30e2e709b3e171cd734bb58705229a582587c29e0a7041227435583c7224cc" },
]
[[distribution]]
name = "project"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "local-simple-a" },
]
"###
);
});

Ok(())
}

0 comments on commit 257007c

Please sign in to comment.