Allow duplicate URLs that resolve to the same canonical URL (#1877)

Closes https://github.com/astral-sh/uv/issues/1865.

Closes https://github.com/astral-sh/uv/issues/1866.
This commit is contained in:
Charlie Marsh 2024-02-22 11:44:36 -05:00 committed by GitHub
parent 12a96ad422
commit 6afbb02798
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 51 additions and 15 deletions

View File

@ -28,11 +28,15 @@ impl Urls {
if let Some(pep508_rs::VersionOrUrl::Url(url)) = &requirement.version_or_url {
if let Some(previous) = urls.insert(requirement.name.clone(), url.clone()) {
return Err(ResolveError::ConflictingUrlsDirect(
requirement.name.clone(),
previous.verbatim().to_string(),
url.verbatim().to_string(),
));
if cache_key::CanonicalUrl::new(previous.raw())
!= cache_key::CanonicalUrl::new(url.raw())
{
return Err(ResolveError::ConflictingUrlsDirect(
requirement.name.clone(),
previous.verbatim().to_string(),
url.verbatim().to_string(),
));
}
}
}
}
@ -42,21 +46,29 @@ impl Urls {
if let Some(previous) =
urls.insert(metadata.name.clone(), editable_requirement.url.clone())
{
return Err(ResolveError::ConflictingUrlsDirect(
metadata.name.clone(),
previous.verbatim().to_string(),
editable_requirement.url.verbatim().to_string(),
));
if cache_key::CanonicalUrl::new(previous.raw())
!= cache_key::CanonicalUrl::new(editable_requirement.raw())
{
return Err(ResolveError::ConflictingUrlsDirect(
metadata.name.clone(),
previous.verbatim().to_string(),
editable_requirement.url.verbatim().to_string(),
));
}
}
for req in &metadata.requires_dist {
if let Some(pep508_rs::VersionOrUrl::Url(url)) = &req.version_or_url {
if let Some(previous) = urls.insert(req.name.clone(), url.clone()) {
return Err(ResolveError::ConflictingUrlsDirect(
req.name.clone(),
previous.verbatim().to_string(),
url.verbatim().to_string(),
));
if cache_key::CanonicalUrl::new(previous.raw())
!= cache_key::CanonicalUrl::new(url.raw())
{
return Err(ResolveError::ConflictingUrlsDirect(
req.name.clone(),
previous.verbatim().to_string(),
url.verbatim().to_string(),
));
}
}
}
}

View File

@ -1363,6 +1363,30 @@ fn conflicting_transitive_url_dependency() -> Result<()> {
Ok(())
}
/// Request Werkzeug via two different URLs which resolve to the same canonical version.
#[test]
fn compatible_repeated_url_dependency() -> Result<()> {
let context = TestContext::new("3.12");
let requirements_in = context.temp_dir.child("requirements.in");
requirements_in.write_str("werkzeug @ git+https://github.com/pallets/werkzeug.git@2.0.0\nwerkzeug @ git+https://github.com/pallets/werkzeug@2.0.0")?;
uv_snapshot!(context.compile()
.arg("requirements.in"), @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2023-11-18T12:00:00Z requirements.in
werkzeug @ git+https://github.com/pallets/werkzeug@af160e0b6b7ddd81c22f1652c728ff5ac72d5c74
----- stderr -----
Resolved 1 package in [TIME]
"###
);
Ok(())
}
/// Request `transitive_url_dependency`, which depends on `git+https://github.com/pallets/werkzeug@2.0.0`.
/// Since this URL isn't declared upfront, we should reject it.
#[test]