Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Consolidate test module directories in deps impact output
Add consolidate_test_paths() to group test_*/ directory contents into
single entries (e.g., test_sqlite3/test_dbapi.py → test_sqlite3).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
  • Loading branch information
2 people authored and youknowone committed Jan 22, 2026
commit e4acc382d99f43aa28261adb3247310573395926
35 changes: 35 additions & 0 deletions scripts/update_lib/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -755,3 +755,38 @@ def find_tests_importing_module(
result.add(test_dir / f"{file_key}.py")

return frozenset(result)


def consolidate_test_paths(
test_paths: frozenset[pathlib.Path],
test_dir: pathlib.Path,
) -> frozenset[str]:
"""Consolidate test paths by grouping test_*/ directory contents into a single entry.

Args:
test_paths: Frozenset of absolute paths to test files
test_dir: Path to the test directory (e.g., Lib/test)

Returns:
Frozenset of consolidated test names:
- "test_foo" for Lib/test/test_foo.py
- "test_sqlite3" for any file in Lib/test/test_sqlite3/
"""
consolidated: set[str] = set()

for path in test_paths:
try:
rel_path = path.relative_to(test_dir)
parts = rel_path.parts

if len(parts) == 1:
# test_foo.py -> test_foo
consolidated.add(rel_path.stem)
else:
# test_sqlite3/test_dbapi.py -> test_sqlite3
consolidated.add(parts[0])
except ValueError:
# Path not relative to test_dir, use stem
consolidated.add(path.stem)

return frozenset(consolidated)
36 changes: 8 additions & 28 deletions scripts/update_lib/show_deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,10 @@ def format_deps(
"""
from update_lib.deps import (
DEPENDENCIES,
consolidate_test_paths,
find_tests_importing_module,
get_lib_paths,
get_test_paths,
get_transitive_imports,
)

if _visited is None:
Expand Down Expand Up @@ -201,33 +201,13 @@ def format_deps(
# Show impact (reverse dependencies) if requested
if show_impact:
impacted_tests = find_tests_importing_module(name, lib_prefix)
transitive_importers = get_transitive_imports(name, lib_prefix)

if impacted_tests:
lines.append(f"[+] impact: ({len(impacted_tests)} tests depend on {name})")
# Sort tests and show with dependency info
test_dir = pathlib.Path(lib_prefix) / "test"
for test_path in sorted(impacted_tests, key=lambda p: str(p)):
# Get relative path from test directory for display
try:
display_name = str(test_path.relative_to(test_dir))
except ValueError:
display_name = test_path.name
# Determine if direct or via which module
test_content = test_path.read_text(errors="ignore")
from update_lib.deps import parse_lib_imports

test_imports = parse_lib_imports(test_content)
if name in test_imports:
lines.append(f" - {display_name} (direct)")
else:
# Find which transitive module is imported
via_modules = test_imports & transitive_importers
if via_modules:
via_str = ", ".join(sorted(via_modules))
lines.append(f" - {display_name} (via {via_str})")
else:
lines.append(f" - {display_name}")
test_dir = pathlib.Path(lib_prefix) / "test"
consolidated = consolidate_test_paths(impacted_tests, test_dir)

if consolidated:
lines.append(f"[+] impact: ({len(consolidated)} tests depend on {name})")
for test_name in sorted(consolidated):
lines.append(f" - {test_name}")
else:
lines.append(f"[+] impact: (no tests depend on {name})")

Expand Down
59 changes: 59 additions & 0 deletions scripts/update_lib/tests/test_deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import unittest

from update_lib.deps import (
consolidate_test_paths,
find_tests_importing_module,
get_data_paths,
get_lib_paths,
Expand Down Expand Up @@ -740,5 +741,63 @@ def test_both_top_level_and_module_directory_tests_found(self):
self.assertIn(test_bar_dir / "test_sub.py", result)


class TestConsolidateTestPaths(unittest.TestCase):
"""Tests for consolidate_test_paths function."""

def test_top_level_test_file(self):
"""Top-level test_*.py -> test_* (without .py)."""
with tempfile.TemporaryDirectory() as tmpdir:
test_dir = pathlib.Path(tmpdir)
test_file = test_dir / "test_foo.py"
test_file.write_text("# test")

result = consolidate_test_paths(frozenset({test_file}), test_dir)
self.assertEqual(result, frozenset({"test_foo"}))

def test_module_directory_tests_consolidated(self):
"""Multiple files in test_*/ directory -> single directory name."""
with tempfile.TemporaryDirectory() as tmpdir:
test_dir = pathlib.Path(tmpdir)
module_dir = test_dir / "test_sqlite3"
module_dir.mkdir()
(module_dir / "test_dbapi.py").write_text("# test")
(module_dir / "test_backup.py").write_text("# test")

result = consolidate_test_paths(
frozenset({module_dir / "test_dbapi.py", module_dir / "test_backup.py"}),
test_dir,
)
self.assertEqual(result, frozenset({"test_sqlite3"}))

def test_mixed_top_level_and_module_directory(self):
"""Both top-level and module directory tests handled correctly."""
with tempfile.TemporaryDirectory() as tmpdir:
test_dir = pathlib.Path(tmpdir)
# Top-level test
(test_dir / "test_foo.py").write_text("# test")
# Module directory tests
module_dir = test_dir / "test_sqlite3"
module_dir.mkdir()
(module_dir / "test_dbapi.py").write_text("# test")
(module_dir / "test_backup.py").write_text("# test")

result = consolidate_test_paths(
frozenset({
test_dir / "test_foo.py",
module_dir / "test_dbapi.py",
module_dir / "test_backup.py",
}),
test_dir,
)
self.assertEqual(result, frozenset({"test_foo", "test_sqlite3"}))

def test_empty_input(self):
"""Empty input -> empty frozenset."""
with tempfile.TemporaryDirectory() as tmpdir:
test_dir = pathlib.Path(tmpdir)
result = consolidate_test_paths(frozenset(), test_dir)
self.assertEqual(result, frozenset())


if __name__ == "__main__":
unittest.main()