diff --git a/Cargo.lock b/Cargo.lock index f9e51c47a..1e017dd86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5120,6 +5120,7 @@ dependencies = [ "futures", "indoc", "insta", + "itertools 0.14.0", "nanoid", "owo-colors", "reqwest", @@ -5153,6 +5154,7 @@ dependencies = [ "uv-pypi-types", "uv-redacted", "uv-types", + "uv-warnings", "uv-workspace", "walkdir", "zip", diff --git a/crates/uv-distribution/Cargo.toml b/crates/uv-distribution/Cargo.toml index 2c490590b..6a0a12bfd 100644 --- a/crates/uv-distribution/Cargo.toml +++ b/crates/uv-distribution/Cargo.toml @@ -35,12 +35,14 @@ uv-platform-tags = { workspace = true } uv-pypi-types = { workspace = true } uv-redacted = { workspace = true } uv-types = { workspace = true } +uv-warnings = { workspace = true } uv-workspace = { workspace = true } anyhow = { workspace = true } either = { workspace = true } fs-err = { workspace = true } futures = { workspace = true } +itertools = { workspace = true } nanoid = { workspace = true } owo-colors = { workspace = true } reqwest = { workspace = true } diff --git a/crates/uv-distribution/src/metadata/build_requires.rs b/crates/uv-distribution/src/metadata/build_requires.rs index 99b528017..74eb037fd 100644 --- a/crates/uv-distribution/src/metadata/build_requires.rs +++ b/crates/uv-distribution/src/metadata/build_requires.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use std::path::Path; use uv_configuration::SourceStrategy; @@ -106,6 +106,8 @@ impl BuildRequires { metadata.name.as_ref(), project_workspace.project_root(), project_sources, + // TODO: make this better lol + &mut BTreeSet::new(), project_indexes, extra.as_deref(), group, @@ -178,6 +180,8 @@ impl BuildRequires { None, workspace.install_path(), project_sources, + // TODO: make this better lol + &mut BTreeSet::new(), project_indexes, extra.as_deref(), group, diff --git a/crates/uv-distribution/src/metadata/dependency_groups.rs b/crates/uv-distribution/src/metadata/dependency_groups.rs index 7fb69b516..043b48554 100644 --- a/crates/uv-distribution/src/metadata/dependency_groups.rs +++ b/crates/uv-distribution/src/metadata/dependency_groups.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use std::path::{Path, PathBuf}; use uv_configuration::SourceStrategy; @@ -142,6 +142,8 @@ impl SourcedDependencyGroups { project.project_name(), project.root(), project_sources, + // TODO: make this better lol + &mut BTreeSet::new(), project_indexes, extra, Some(&group), diff --git a/crates/uv-distribution/src/metadata/lowering.rs b/crates/uv-distribution/src/metadata/lowering.rs index 330075842..3d2f6fa01 100644 --- a/crates/uv-distribution/src/metadata/lowering.rs +++ b/crates/uv-distribution/src/metadata/lowering.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use std::io; use std::path::{Path, PathBuf}; @@ -38,6 +38,7 @@ impl LoweredRequirement { project_name: Option<&'data PackageName>, project_dir: &'data Path, project_sources: &'data BTreeMap, + project_sources_used: &mut BTreeSet, project_indexes: &'data [Index], extra: Option<&ExtraName>, group: Option<&GroupName>, @@ -47,8 +48,10 @@ impl LoweredRequirement { ) -> impl Iterator> + use<'data> + 'data { // Identify the source from the `tool.uv.sources` table. let (sources, origin) = if let Some(source) = project_sources.get(&requirement.name) { + project_sources_used.insert(requirement.name.clone()); (Some(source), RequirementOrigin::Project) } else if let Some(source) = workspace.sources().get(&requirement.name) { + // TODO(jack): Track workplace sources used also. (Some(source), RequirementOrigin::Workspace) } else { (None, RequirementOrigin::Project) diff --git a/crates/uv-distribution/src/metadata/requires_dist.rs b/crates/uv-distribution/src/metadata/requires_dist.rs index e9f36f174..7657a74e0 100644 --- a/crates/uv-distribution/src/metadata/requires_dist.rs +++ b/crates/uv-distribution/src/metadata/requires_dist.rs @@ -1,13 +1,15 @@ -use std::collections::{BTreeMap, VecDeque}; +use std::collections::{BTreeMap, BTreeSet, VecDeque}; use std::path::Path; use std::slice; +use itertools::Itertools; use rustc_hash::FxHashSet; use uv_configuration::SourceStrategy; use uv_distribution_types::{IndexLocations, Requirement}; use uv_normalize::{ExtraName, GroupName, PackageName}; use uv_pep508::MarkerTree; +use uv_warnings::warn_user_once; use uv_workspace::dependency_groups::FlatDependencyGroups; use uv_workspace::pyproject::{Sources, ToolUvSources}; use uv_workspace::{DiscoveryOptions, MemberDiscovery, ProjectWorkspace, WorkspaceCache}; @@ -106,6 +108,8 @@ impl RequiresDist { .unwrap_or(&empty), SourceStrategy::Disabled => &empty, }; + // Keep track of which sources we use, so we can print a warning for unused ones. + let mut project_sources_used = BTreeSet::new(); let dependency_groups = FlatDependencyGroups::from_pyproject_toml( project_workspace.current_project().root(), @@ -133,6 +137,7 @@ impl RequiresDist { Some(&metadata.name), project_workspace.project_root(), project_sources, + &mut project_sources_used, project_indexes, extra, Some(&group), @@ -175,6 +180,7 @@ impl RequiresDist { Some(&metadata.name), project_workspace.project_root(), project_sources, + &mut project_sources_used, project_indexes, extra.as_deref(), group, @@ -194,6 +200,21 @@ impl RequiresDist { SourceStrategy::Disabled => requires_dist.into_iter().map(Requirement::from).collect(), }; + // Warn if we have any unused project sources. + let mut unused_source_names = Vec::new(); + for package_name in project_sources.keys() { + if !project_sources_used.contains(package_name) { + unused_source_names.push(package_name); + } + } + if !unused_source_names.is_empty() { + warn_user_once!( + "some {} don't match a direct dependency and have no effect: {}", + "[tool.uv.sources]".cyan(), + unused_source_names.iter().map(OwoColorize::cyan).join(", "), + ); + } + Ok(Self { name: metadata.name, requires_dist,