diff --git a/rimport b/rimport index 29fb9e4..1d7dcba 100755 --- a/rimport +++ b/rimport @@ -311,6 +311,7 @@ def expand_directories( named_paths = set(paths) named_by_path: dict[Path, bool] = {} skips: List[Skip] = [] + empty: List[Path] = [] n_dirs = 0 expanded_files: set[Path] = set() @@ -335,7 +336,10 @@ def expand_directories( found, walk_skips = walk_files(path) skips.extend(walk_skips) if not found and not walk_skips: - logger.warning("rimport: no files found under %s", path) + # Collected, not reported: every warning this function emits is logged + # below the count. A walk that returned nothing because it could not be + # read is not an empty directory, and is reported as a skip instead. + empty.append(path) for found_path in found: expanded_files.add(found_path) named_by_path.setdefault(found_path, False) @@ -351,11 +355,17 @@ def expand_directories( deduped.setdefault(skip.path, Skip(skip.path, skip.reason, skip.path in named_paths)) skips = list(deduped.values()) + # Everything below is logged after the count, and indented under it. The count is the + # blast radius -- the one line worth reading before a large run is left to finish -- so + # it stays at the top of the output however many directories turn out to warn. if n_dirs: logger.info( "rimport: expanded %d director(ies) to %d file(s)", n_dirs, len(expanded_files) ) + for path in empty: + logger.warning("%srimport: no files found under %s", INDENT, path) + for skip in skips: # Report a DISCOVERED skip here, during expansion, where the run reached it; the # end-of-run summary repeats it. A skip the user NAMED is not reported here at all: diff --git a/tests/rimport/test_expand_directories.py b/tests/rimport/test_expand_directories.py index 7bb0029..93f5168 100644 --- a/tests/rimport/test_expand_directories.py +++ b/tests/rimport/test_expand_directories.py @@ -7,6 +7,8 @@ import importlib.util from importlib.machinery import SourceFileLoader +from shared import INDENT + # Import rimport module from file without .py extension rimport_path = os.path.join( @@ -276,6 +278,31 @@ def test_expansion_count_is_logged_before_any_skip_warning(tmp_path, caplog): assert caplog.text.index("expanded 1 director(ies)") < caplog.text.index("skipping") +def test_expansion_count_is_logged_before_any_emptiness_warning(tmp_path, caplog): + """The count line is the blast radius, and it belongs at the top where it cannot be + pushed down the screen by one warning per empty directory named. Both warnings this + function emits sit below it, and both are indented to say so.""" + empty = tmp_path / "empty" + empty.mkdir() + d = tmp_path / "d" + d.mkdir() + (d / "a.nc").write_text("a") + locked = d / "locked" + locked.mkdir() + os.chmod(locked, 0o000) + + try: + with caplog.at_level(logging.INFO, logger="rimport_relink"): + rimport.expand_directories([empty, d], tmp_path) + finally: + os.chmod(locked, 0o700) + + assert caplog.text.index("expanded 2 director(ies)") < caplog.text.index("no files found") + assert caplog.text.index("no files found") < caplog.text.index("skipping") + assert f"{INDENT}rimport: no files found under {empty}" in caplog.text + assert f"{INDENT}rimport: skipping '{locked}'" in caplog.text + + def test_directory_named_twice_is_reported_once(tmp_path, caplog): """Naming the same directory twice is one directory, not two, so the blast-radius line counts it once.""" @@ -289,6 +316,19 @@ def test_directory_named_twice_is_reported_once(tmp_path, caplog): assert "expanded 1 director(ies) to 1 file(s)" in caplog.text +def test_empty_directory_named_twice_warns_once(tmp_path, caplog): + """One empty directory is one warning, however many of the named arguments reach it. + The count line and the skips are already pinned against duplicate arguments; without + this the emptiness warning is the one report that could double.""" + d = tmp_path / "empty" + d.mkdir() + + with caplog.at_level(logging.WARNING, logger="rimport_relink"): + rimport.expand_directories([d, d], tmp_path) + + assert caplog.text.count("no files found") == 1 + + def test_duplicate_arguments_do_not_duplicate_a_skip(tmp_path, caplog): """One unreadable directory is one skip, however many of the named arguments reach it. Otherwise it is warned about twice, listed twice in the end-of-run summary, and counted