Skip to content
Merged
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
Next Next commit
show todo
  • Loading branch information
youknowone committed Jan 24, 2026
commit 544ff283658aab68714bf9eefce7be70e0c1055f
192 changes: 189 additions & 3 deletions scripts/update_lib/show_todo.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,147 @@ def compute_todo_list(
return result


def get_all_tests(cpython_prefix: str = "cpython") -> list[str]:
"""Get all test module names from cpython/Lib/test/.

Returns:
Sorted list of test names (e.g., ["test_abc", "test_dis", ...])
"""
test_dir = pathlib.Path(cpython_prefix) / "Lib" / "test"
if not test_dir.exists():
return []

tests = set()
for entry in test_dir.iterdir():
# Skip private/internal and special directories
if entry.name.startswith(("_", ".")):
continue
# Skip non-test items
if not entry.name.startswith("test_"):
continue

if entry.is_file() and entry.suffix == ".py":
tests.add(entry.stem)
elif entry.is_dir() and (entry / "__init__.py").exists():
tests.add(entry.name)

return sorted(tests)


def is_test_up_to_date(
test_name: str, cpython_prefix: str = "cpython", lib_prefix: str = "Lib"
) -> bool:
"""Check if a test is up-to-date by comparing files."""
import filecmp

from update_lib.deps import _dircmp_is_same

# Try directory first, then file
cpython_dir = pathlib.Path(cpython_prefix) / "Lib" / "test" / test_name
cpython_file = pathlib.Path(cpython_prefix) / "Lib" / "test" / f"{test_name}.py"

if cpython_dir.exists():
cpython_path = cpython_dir
elif cpython_file.exists():
cpython_path = cpython_file
else:
return True # No cpython test, consider up-to-date

local_path = pathlib.Path(lib_prefix) / "test" / cpython_path.name

if not local_path.exists():
return False

if cpython_path.is_file():
return filecmp.cmp(cpython_path, local_path, shallow=False)
else:
dcmp = filecmp.dircmp(cpython_path, local_path)
return _dircmp_is_same(dcmp)


def compute_test_todo_list(
cpython_prefix: str = "cpython",
lib_prefix: str = "Lib",
include_done: bool = False,
lib_status: dict[str, bool] | None = None,
) -> list[dict]:
"""Compute prioritized list of tests to update.

Scoring:
- If corresponding lib is up-to-date: score = 0 (ready)
- If corresponding lib is NOT up-to-date: score = 1 (wait for lib)
- If no corresponding lib: score = -1 (independent)
Comment on lines +316 to +319
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Docstring does not match implementation.

The docstring states scores of 0, 1, and -1, but the implementation uses 0, 2, and 1:

  • score = 0: lib is up-to-date (matches)
  • score = 2: lib NOT up-to-date (docstring says 1)
  • score = 1: no corresponding lib (docstring says -1)
📝 Proposed fix
     """Compute prioritized list of tests to update.

     Scoring:
-        - If corresponding lib is up-to-date: score = 0 (ready)
-        - If corresponding lib is NOT up-to-date: score = 1 (wait for lib)
-        - If no corresponding lib: score = -1 (independent)
+        - If corresponding lib is up-to-date: score = 0 (ready to update)
+        - If no corresponding lib: score = 1 (independent test)
+        - If corresponding lib is NOT up-to-date: score = 2 (wait for lib first)

     Returns:
         List of dicts with test info, sorted by priority
     """
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Scoring:
- If corresponding lib is up-to-date: score = 0 (ready)
- If corresponding lib is NOT up-to-date: score = 1 (wait for lib)
- If no corresponding lib: score = -1 (independent)
Scoring:
- If corresponding lib is up-to-date: score = 0 (ready to update)
- If no corresponding lib: score = 1 (independent test)
- If corresponding lib is NOT up-to-date: score = 2 (wait for lib first)
🤖 Prompt for AI Agents
In `@scripts/update_lib/show_todo.py` around lines 253 - 256, The docstring and
implementation disagree on the scoring values; update the logic that assigns the
variable score in this module so it matches the docstring: set score = 0 when
the corresponding lib is up-to-date, set score = 1 when the corresponding lib
exists but is NOT up-to-date, and set score = -1 when there is no corresponding
lib (ensure any places that check or return score use these values consistently,
e.g., the score assignment sites and any functions that consume score).


Returns:
List of dicts with test info, sorted by priority
"""
all_tests = get_all_tests(cpython_prefix)

result = []
for test_name in all_tests:
up_to_date = is_test_up_to_date(test_name, cpython_prefix, lib_prefix)

if up_to_date and not include_done:
continue

# Extract lib name from test name (test_foo -> foo)
lib_name = test_name[5:] if test_name.startswith("test_") else test_name

# Check if corresponding lib is up-to-date
# Scoring: 0 = lib ready (highest priority), 1 = no lib, 2 = lib pending
if lib_status and lib_name in lib_status:
lib_up_to_date = lib_status[lib_name]
if lib_up_to_date:
score = 0 # Lib is ready, can update test
else:
score = 2 # Wait for lib first
else:
score = 1 # No corresponding lib (independent test)

result.append(
{
"name": test_name,
"lib_name": lib_name,
"score": score,
"up_to_date": up_to_date,
}
)

# Sort by score (ascending)
result.sort(key=lambda x: x["score"])

return result


def format_test_todo_list(
todo_list: list[dict],
limit: int | None = None,
) -> list[str]:
"""Format test todo list for display."""
lines = []

if limit:
todo_list = todo_list[:limit]

for item in todo_list:
name = item["name"]
done_mark = "[x]" if item["up_to_date"] else "[ ]"
lines.append(f"- {done_mark} {name}")

return lines


def format_todo_list(
todo_list: list[dict],
test_by_lib: dict[str, dict] | None = None,
limit: int | None = None,
verbose: bool = False,
) -> list[str]:
"""Format todo list for display.

Args:
todo_list: List from compute_todo_list()
test_by_lib: Dict mapping lib_name -> test info (optional)
limit: Maximum number of items to show
verbose: Show detailed dependency information

Expand Down Expand Up @@ -149,6 +281,12 @@ def format_todo_list(

lines.append(" ".join(parts))

# Show corresponding test if exists
if test_by_lib and name in test_by_lib:
test_info = test_by_lib[name]
test_done_mark = "[x]" if test_info["up_to_date"] else "[ ]"
lines.append(f" - {test_done_mark} {test_info['name']}")

# Verbose mode: show detailed dependency info
if verbose:
if item["reverse_deps"]:
Expand All @@ -161,16 +299,64 @@ def format_todo_list(
return lines


def format_all_todo(
cpython_prefix: str = "cpython",
lib_prefix: str = "Lib",
limit: int | None = None,
include_done: bool = False,
verbose: bool = False,
) -> list[str]:
"""Format prioritized list of modules and tests to update.

Returns:
List of formatted lines
"""
from update_lib.deps import is_up_to_date
from update_lib.show_deps import get_all_modules

lines = []

# Compute lib todo
lib_todo = compute_todo_list(cpython_prefix, lib_prefix, include_done)

# Build lib status map for test scoring
lib_status = {}
for name in get_all_modules(cpython_prefix):
lib_status[name] = is_up_to_date(name, cpython_prefix, lib_prefix)

# Compute test todo
test_todo = compute_test_todo_list(cpython_prefix, lib_prefix, include_done, lib_status)

# Build test_by_lib map (only for tests with corresponding lib)
test_by_lib = {}
no_lib_tests = []
for test in test_todo:
if test["score"] == 1: # no lib
no_lib_tests.append(test)
else:
test_by_lib[test["lib_name"]] = test

# Format lib todo with embedded tests
lines.extend(format_todo_list(lib_todo, test_by_lib, limit, verbose))

# Format "no lib" tests separately if any
if no_lib_tests:
lines.append("")
lines.append("## Standalone Tests")
lines.extend(format_test_todo_list(no_lib_tests, limit))

return lines


def show_todo(
cpython_prefix: str = "cpython",
lib_prefix: str = "Lib",
limit: int | None = None,
include_done: bool = False,
verbose: bool = False,
) -> None:
"""Show prioritized list of modules to update."""
todo_list = compute_todo_list(cpython_prefix, lib_prefix, include_done)
for line in format_todo_list(todo_list, limit, verbose):
"""Show prioritized list of modules and tests to update."""
for line in format_all_todo(cpython_prefix, lib_prefix, limit, include_done, verbose):
print(line)


Expand Down