Use parsed URLs for conflicting URL error message (#14380)

## Summary

There's a good example of the downside of using verbatim URLs here:
https://github.com/astral-sh/uv/pull/14197#discussion_r2163599625 (we
show two relative paths that point to the same directory, but it's not
clear from the error message).

The diff:

```
    2     2 │ ----- stdout -----
    3     3 │
    4     4 │ ----- stderr -----
    5     5 │ error: Requirements contain conflicting URLs for package `library` in all marker environments:
    6       │-- ../../library
    7       │-- ./library
          6 │+- file://[TEMP_DIR]/library
          7 │+- file://[TEMP_DIR]/library (editable)
```
This commit is contained in:
Charlie Marsh 2025-07-01 08:18:01 -04:00 committed by GitHub
parent b1812d111a
commit 3774a656d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 23 additions and 19 deletions

View file

@ -17,6 +17,8 @@ use uv_normalize::{ExtraName, InvalidNameError, PackageName};
use uv_pep440::{LocalVersionSlice, LowerBound, Version, VersionSpecifier};
use uv_pep508::{MarkerEnvironment, MarkerExpression, MarkerTree, MarkerValueVersion};
use uv_platform_tags::Tags;
use uv_pypi_types::ParsedUrl;
use uv_redacted::DisplaySafeUrl;
use uv_static::EnvVars;
use crate::candidate_selector::CandidateSelector;
@ -56,11 +58,14 @@ pub enum ResolveError {
} else {
format!(" in {env}")
},
urls.join("\n- "),
urls.iter()
.map(|url| format!("{}{}", DisplaySafeUrl::from(url.clone()), if url.is_editable() { " (editable)" } else { "" }))
.collect::<Vec<_>>()
.join("\n- ")
)]
ConflictingUrls {
package_name: PackageName,
urls: Vec<String>,
urls: Vec<ParsedUrl>,
env: ResolverEnvironment,
},
@ -71,11 +76,14 @@ pub enum ResolveError {
} else {
format!(" in {env}")
},
indexes.join("\n- "),
indexes.iter()
.map(std::string::ToString::to_string)
.collect::<Vec<_>>()
.join("\n- ")
)]
ConflictingIndexesForEnvironment {
package_name: PackageName,
indexes: Vec<String>,
indexes: Vec<IndexUrl>,
env: ResolverEnvironment,
},

View file

@ -24,7 +24,7 @@ impl ForkIndexes {
) -> Result<(), ResolveError> {
if let Some(previous) = self.0.insert(package_name.clone(), index.clone()) {
if &previous != index {
let mut conflicts = vec![previous.url.to_string(), index.url.to_string()];
let mut conflicts = vec![previous.url, index.url.clone()];
conflicts.sort();
return Err(ResolveError::ConflictingIndexesForEnvironment {
package_name: package_name.clone(),

View file

@ -2,7 +2,6 @@ use std::collections::hash_map::Entry;
use rustc_hash::FxHashMap;
use uv_distribution_types::Verbatim;
use uv_normalize::PackageName;
use uv_pypi_types::VerbatimParsedUrl;
@ -34,10 +33,8 @@ impl ForkUrls {
match self.0.entry(package_name.clone()) {
Entry::Occupied(previous) => {
if previous.get() != url {
let mut conflicting_url = vec![
previous.get().verbatim.verbatim().to_string(),
url.verbatim.verbatim().to_string(),
];
let mut conflicting_url =
vec![previous.get().parsed_url.clone(), url.parsed_url.clone()];
conflicting_url.sort();
return Err(ResolveError::ConflictingUrls {
package_name: package_name.clone(),

View file

@ -4,7 +4,6 @@ use same_file::is_same_file;
use tracing::debug;
use uv_cache_key::CanonicalUrl;
use uv_distribution_types::Verbatim;
use uv_git::GitResolver;
use uv_normalize::PackageName;
use uv_pep508::{MarkerTree, VerbatimUrl};
@ -170,8 +169,8 @@ impl Urls {
let [allowed_url] = matching_urls.as_slice() else {
let mut conflicting_urls: Vec<_> = matching_urls
.into_iter()
.map(|parsed_url| parsed_url.verbatim.verbatim().to_string())
.chain(std::iter::once(verbatim_url.verbatim().to_string()))
.map(|parsed_url| parsed_url.parsed_url.clone())
.chain(std::iter::once(parsed_url.clone()))
.collect();
conflicting_urls.sort();
return Err(ResolveError::ConflictingUrls {

View file

@ -2909,16 +2909,16 @@ fn incompatible_narrowed_url_dependency() -> Result<()> {
"})?;
uv_snapshot!(context.filters(), context.pip_compile()
.arg("requirements.in"), @r###"
.arg("requirements.in"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: Requirements contain conflicting URLs for package `uv-public-pypackage`:
- git+https://github.com/astral-test/uv-public-pypackage@b270df1a2fb5d012294e9aaf05e7e0bab1e6a389
- git+https://github.com/astral-test/uv-public-pypackage@test-branch
"###
- git+https://github.com/astral-test/uv-public-pypackage@b270df1a2fb5d012294e9aaf05e7e0bab1e6a389
"
);
Ok(())

View file

@ -1515,16 +1515,16 @@ fn install_editable_incompatible_constraint_url() -> Result<()> {
.arg("-e")
.arg(context.workspace_root.join("scripts/packages/black_editable"))
.arg("--constraint")
.arg("constraints.txt"), @r###"
.arg("constraints.txt"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: Requirements contain conflicting URLs for package `black`:
- [WORKSPACE]/scripts/packages/black_editable
- file://[WORKSPACE]/scripts/packages/black_editable (editable)
- https://files.pythonhosted.org/packages/0f/89/294c9a6b6c75a08da55e9d05321d0707e9418735e3062b12ef0f54c33474/black-24.4.2-py3-none-any.whl
"###
"
);
Ok(())