Warn on empty index directory (#13940)

Close #13922

## Summary

Add a warning if the directory given by the `--index` argument is empty.

## Test Plan

Added test case `add_index_empty_directory` in `edit.rs`
This commit is contained in:
Aaron Ang 2025-06-18 01:48:21 -07:00 committed by GitHub
parent 499c8aa808
commit 0cac73dc1f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 57 additions and 25 deletions

View file

@ -511,30 +511,23 @@ impl<'a> IndexUrls {
/// iterator.
pub fn defined_indexes(&'a self) -> impl Iterator<Item = &'a Index> + 'a {
if self.no_index {
Either::Left(std::iter::empty())
} else {
Either::Right(
{
let mut seen = FxHashSet::default();
self.indexes
.iter()
.filter(move |index| {
index.name.as_ref().is_none_or(|name| seen.insert(name))
})
.filter(|index| !index.default)
}
.chain({
let mut seen = FxHashSet::default();
self.indexes
.iter()
.filter(move |index| {
index.name.as_ref().is_none_or(|name| seen.insert(name))
})
.find(|index| index.default)
.into_iter()
}),
)
return Either::Left(std::iter::empty());
}
let mut seen = FxHashSet::default();
let (non_default, default) = self
.indexes
.iter()
.filter(move |index| {
if let Some(name) = &index.name {
seen.insert(name)
} else {
true
}
})
.partition::<Vec<_>, _>(|index| !index.default);
Either::Right(non_default.into_iter().chain(default))
}
/// Return the `--no-index` flag.

View file

@ -512,8 +512,9 @@ pub(crate) async fn add(
)?;
// Validate any indexes that were provided on the command-line to ensure
// they point to existing directories when using path URLs.
for index in &indexes {
// they point to existing non-empty directories when using path URLs.
let mut valid_indexes = Vec::with_capacity(indexes.len());
for index in indexes {
if let IndexUrl::Path(url) = &index.url {
let path = url
.to_file_path()
@ -521,8 +522,14 @@ pub(crate) async fn add(
if !path.is_dir() {
bail!("Directory not found for index: {url}");
}
if fs_err::read_dir(&path)?.next().is_none() {
warn_user_once!("Index directory `{url}` is empty, skipping");
continue;
}
}
valid_indexes.push(index);
}
let indexes = valid_indexes;
// Add any indexes that were provided on the command-line, in priority order.
if !raw {

View file

@ -9434,6 +9434,38 @@ fn add_index_with_non_existent_relative_path_with_same_name_as_index() -> Result
Ok(())
}
#[test]
fn add_index_empty_directory() -> 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 = []
"#})?;
let packages = context.temp_dir.child("test-index");
packages.create_dir_all()?;
uv_snapshot!(context.filters(), context.add().arg("iniconfig").arg("--index").arg("test-index"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
warning: Index directory `file://[TEMP_DIR]/test-index` is empty, skipping
Resolved 2 packages in [TIME]
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
+ iniconfig==2.0.0
");
Ok(())
}
/// Add a PyPI requirement.
#[test]
fn add_group_comment() -> Result<()> {