Repository: astral-sh/uv
File: crates/uv-build-backend/src/lib.rs (Rust)

Code before:
/// * `["aaa", "aaa"] -> ["aaa"]`
/// * `["bbb", "aaa", "bbb"] -> ["aaa", "bbb"]`
///
/// Names with more specific paths are removed in favour of more general paths:
///
/// * `["aaa.foo", "aaa"] -> ["aaa"]`
/// * `["bbb", "aaa", "bbb.foo", "ccc.foo", "ccc.foo.bar", "aaa"] -> ["aaa", "bbb.foo", "ccc.foo"]`
///
/// This does not preserve the order of the elements.
///
/// This function prints warnings for duplicate module names.
fn prune_redundant_modules(mut names: Vec<String>) -> Vec<String> {
    names.sort();
    let mut pruned = Vec::with_capacity(names.len());
    for name in names {
        if let Some(last) = pruned.last() {
            if name == *last {
                warn_user_once!("Ignoring duplicate module name {name}");
                continue;
            }
            // This is a more specific (narrow) module name than what came before
            if name
                .strip_prefix(last)
                .is_some_and(|suffix| suffix.starts_with('.'))
            {
                warn_user_once!("Ignoring module name {name}: already including {last}");
                continue;
            }
        }
        pruned.push(name);
    }
    pruned
}

/// Returns the source root and the module path(s) with the `__init__.py[i]`  below to it while
/// checking the project layout and names.
///
/// Some target platforms have case-sensitive filesystems, while others have case-insensitive
/// filesystems. We always lower case the package name, our default for the module, while some
/// users want uppercase letters in their module names. For example, the package name is `pil_util`,
/// but the module `PIL_util`. To make the behavior as consistent as possible across platforms as
/// possible, we require that an upper case name is given explicitly through
/// `tool.uv.build-backend.module-name`.
///
/// By default, the dist-info-normalized package name is the module name. For
/// dist-info-normalization, the rules are lowercasing, replacing `.` with `_` and
/// replace `-` with `_`. Since `.` and `-` are not allowed in identifiers, we can use a string
/// comparison with the module name.
///
/// While we recommend one module per package, it is possible to declare a list of modules.
fn find_roots(

Review comment (suggestion):
We only show this warning if it's a source distribution the user controls, not for e.g. arbitrary packages from PyPI. The best proxy to use for this is `SourceStrategy`, if it's enabled we consider it something to warn about.

Code after:
/// Duplicate elements are removed:
///
/// * `["aaa", "aaa"] -> ["aaa"]`
/// * `["bbb", "aaa", "bbb"] -> ["aaa", "bbb"]`
///
/// Names with more specific paths are removed in favour of more general paths:
///
/// * `["aaa.foo", "aaa"] -> ["aaa"]`
/// * `["bbb", "aaa", "bbb.foo", "ccc.foo", "ccc.foo.bar", "aaa"] -> ["aaa", "bbb.foo", "ccc.foo"]`
///
/// This does not preserve the order of the elements.
fn prune_redundant_modules(mut names: Vec<String>) -> Vec<String> {
    names.sort();
    let mut pruned = Vec::with_capacity(names.len());
    for name in names {
        if let Some(last) = pruned.last() {
            if name == *last {
                continue;
            }
            // This is a more specific (narrow) module name than what came before
            if name
                .strip_prefix(last)
                .is_some_and(|suffix| suffix.starts_with('.'))
            {
                continue;
            }
        }
        pruned.push(name);
    }
    pruned
}

/// Wraps [`prune_redundant_modules`] with a conditional warning when modules are ignored
fn prune_redundant_modules_warn(names: &[String], show_warnings: bool) -> Vec<String> {
    let pruned = prune_redundant_modules(names.to_vec());
    if show_warnings && names.len() != pruned.len() {
        let mut pruned: HashSet<_> = pruned.iter().collect();
        let ignored: Vec<_> = names.iter().filter(|name| !pruned.remove(name)).collect();
        let s = if ignored.len() == 1 { "" } else { "s" };
        warn_user_once!(
            "Ignoring redundant module name{s} in `tool.uv.build-backend.module-name`: `{}`",
            ignored.into_iter().join("`, `")
        );
    }
    pruned
}

/// Returns the source root and the module path(s) with the `__init__.py[i]`  below to it while
/// checking the project layout and names.
///
/// Some target platforms have case-sensitive filesystems, while others have case-insensitive