Check existing source by normalized name before add and remove (#8359)
Resolves #8328 Resolves #8330
This commit is contained in:
@@ -131,6 +131,7 @@ impl PyProjectTomlMut {
|
||||
};
|
||||
Ok(doc)
|
||||
}
|
||||
|
||||
/// Adds a dependency to `project.dependencies`.
|
||||
///
|
||||
/// Returns `true` if the dependency was added, `false` if it was updated.
|
||||
@@ -431,7 +432,11 @@ impl PyProjectTomlMut {
|
||||
.as_table_mut()
|
||||
.ok_or(Error::MalformedSources)?;
|
||||
|
||||
if let Some(key) = find_source(name, sources) {
|
||||
sources.remove(&key);
|
||||
}
|
||||
add_source(name, source, sources)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -532,7 +537,9 @@ impl PyProjectTomlMut {
|
||||
.map(|sources| sources.as_table_mut().ok_or(Error::MalformedSources))
|
||||
.transpose()?
|
||||
{
|
||||
sources.remove(name.as_ref());
|
||||
if let Some(key) = find_source(name, sources) {
|
||||
sources.remove(&key);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -766,6 +773,16 @@ fn find_dependencies(
|
||||
to_replace
|
||||
}
|
||||
|
||||
/// Returns the key in `tool.uv.sources` that matches the given package name.
|
||||
fn find_source(name: &PackageName, sources: &Table) -> Option<String> {
|
||||
for (key, _) in sources {
|
||||
if PackageName::from_str(key).is_ok_and(|ref key| key == name) {
|
||||
return Some(key.to_string());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// Add a source to `tool.uv.sources`.
|
||||
fn add_source(req: &PackageName, source: &Source, sources: &mut Table) -> Result<(), Error> {
|
||||
// Serialize as an inline table.
|
||||
|
||||
@@ -2717,6 +2717,175 @@ fn update_source_replace_url() -> Result<()> {
|
||||
);
|
||||
});
|
||||
|
||||
// Change the source again. The existing source should be replaced.
|
||||
uv_snapshot!(context.filters(), context.add().arg("requests @ git+https://github.com/psf/requests").arg("--tag=v2.32.2"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 6 packages in [TIME]
|
||||
Prepared 2 packages in [TIME]
|
||||
Uninstalled 2 packages in [TIME]
|
||||
Installed 2 packages in [TIME]
|
||||
~ project==0.1.0 (from file://[TEMP_DIR]/)
|
||||
- requests==2.32.3 (from git+https://github.com/psf/requests@0e322af87745eff34caffe4df68456ebc20d9068)
|
||||
+ requests==2.32.2 (from git+https://github.com/psf/requests@88dce9d854797c05d0ff296b70e0430535ef8aaf)
|
||||
"###);
|
||||
|
||||
let pyproject_toml = context.read("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 = [
|
||||
"requests[security]",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=42"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.uv.sources]
|
||||
requests = { git = "https://github.com/psf/requests", tag = "v2.32.2" }
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// If a source defined in `tool.uv.sources` but its name is not normalized, `uv add` should not
|
||||
/// add the same source again.
|
||||
#[test]
|
||||
#[cfg(feature = "git")]
|
||||
fn add_non_normalized_source() -> 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 = [
|
||||
"uv-public-pypackage"
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=42"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.uv.sources]
|
||||
uv_public_pypackage = { git = "https://github.com/astral-test/uv-public-pypackage", tag = "0.0.1" }
|
||||
"#})?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.add().arg("uv-public-pypackage @ git+https://github.com/astral-test/uv-public-pypackage@0.0.1"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 2 packages in [TIME]
|
||||
Prepared 2 packages in [TIME]
|
||||
Installed 2 packages in [TIME]
|
||||
+ project==0.1.0 (from file://[TEMP_DIR]/)
|
||||
+ uv-public-pypackage==0.1.0 (from git+https://github.com/astral-test/uv-public-pypackage@0dacfd662c64cb4ceb16e6cf65a157a8b715b979)
|
||||
"###);
|
||||
|
||||
let pyproject_toml = context.read("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 = [
|
||||
"uv-public-pypackage",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=42"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.uv.sources]
|
||||
uv-public-pypackage = { git = "https://github.com/astral-test/uv-public-pypackage", rev = "0.0.1" }
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// If a source defined in `tool.uv.sources` but its name is not normalized, `uv remove` should
|
||||
/// remove the source.
|
||||
#[test]
|
||||
#[cfg(feature = "git")]
|
||||
fn remove_non_normalized_source() -> 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 = [
|
||||
"uv-public-pypackage"
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=42"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.uv.sources]
|
||||
uv_public_pypackage = { git = "https://github.com/astral-test/uv-public-pypackage", tag = "0.0.1" }
|
||||
"#})?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.remove().arg("uv-public-pypackage"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 1 package in [TIME]
|
||||
Prepared 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ project==0.1.0 (from file://[TEMP_DIR]/)
|
||||
"###);
|
||||
|
||||
let pyproject_toml = context.read("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 = []
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=42"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.uv.sources]
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user