[update_lib] todo shows last updated date (#7053)

This commit is contained in:
Jeong, YunWon authored and GitHub committed 2026-02-09 09:39:01 +09:00
1 parent 15cd2e3764
commit ea1b917712
2 files changed
+160 -4

No files matched your search

+46 -4
View File
@@ -15,6 +15,9 @@ sys.path.insert(0, str(pathlib.Path(__file__).parent.parent))
from update_lib.deps import (
count_test_todos,
get_module_diff_stat,
get_module_last_updated,
get_test_last_updated,
is_test_tracked,
is_test_up_to_date,
)
@@ -368,6 +371,18 @@ def compute_test_todo_list(
return result
def _format_meta_suffix(item: dict) -> str:
"""Format metadata suffix (last updated date and diff count)."""
parts = []
last_updated = item.get("last_updated")
diff_lines = item.get("diff_lines", 0)
if last_updated:
parts.append(last_updated)
if diff_lines > 0:
parts.append(f"Δ{diff_lines}")
return f" | {' '.join(parts)}" if parts else ""
def _format_test_suffix(item: dict) -> str:
"""Format suffix for test item (TODO count or untracked)."""
tracked = item.get("tracked", True)
@@ -410,13 +425,15 @@ def format_test_todo_list(
primary = tests[0]
done_mark = "[x]" if primary["up_to_date"] else "[ ]"
suffix = _format_test_suffix(primary)
lines.append(f"- {done_mark} {primary['name']}{suffix}")
meta = _format_meta_suffix(primary)
lines.append(f"- {done_mark} {primary['name']}{suffix}{meta}")
# Rest are indented
for item in tests[1:]:
done_mark = "[x]" if item["up_to_date"] else "[ ]"
suffix = _format_test_suffix(item)
lines.append(f" - {done_mark} {item['name']}{suffix}")
meta = _format_meta_suffix(item)
lines.append(f" - {done_mark} {item['name']}{suffix}{meta}")
return lines
@@ -462,7 +479,8 @@ def format_todo_list(
if rev_str:
parts.append(f"({rev_str})")
lines.append(" ".join(parts))
line = " ".join(parts) + _format_meta_suffix(item)
lines.append(line)
# Show hard_deps:
# - Normal mode: only show if lib is up-to-date but hard_deps are not
@@ -482,7 +500,8 @@ def format_todo_list(
for test_info in test_by_lib[name]:
test_done_mark = "[x]" if test_info["up_to_date"] else "[ ]"
suffix = _format_test_suffix(test_info)
lines.append(f" - {test_done_mark} {test_info['name']}{suffix}")
meta = _format_meta_suffix(test_info)
lines.append(f" - {test_done_mark} {test_info['name']}{suffix}{meta}")
# Verbose mode: show detailed dependency info
if verbose:
@@ -556,6 +575,29 @@ def format_all_todo(
if include_done or lib_not_done or has_pending_test:
lib_todo.append(item)
# Add metadata (last updated date and diff stat) to lib items
for item in lib_todo:
item["last_updated"] = get_module_last_updated(
item["name"], cpython_prefix, lib_prefix
)
item["diff_lines"] = (
0
if item["up_to_date"]
else get_module_diff_stat(item["name"], cpython_prefix, lib_prefix)
)
# Add last_updated to displayed test items (verbose only - slow)
if verbose:
for tests in test_by_lib.values():
for test in tests:
test["last_updated"] = get_test_last_updated(
test["name"], cpython_prefix, lib_prefix
)
for test in no_lib_tests:
test["last_updated"] = get_test_last_updated(
test["name"], cpython_prefix, lib_prefix
)
# Format lib todo with embedded tests
lines.extend(format_todo_list(lib_todo, test_by_lib, limit, verbose))
+114
View File
@@ -8,6 +8,7 @@ Handles:
"""
import ast
import difflib
import functools
import pathlib
import re
@@ -1011,6 +1012,119 @@ def is_up_to_date(name: str, cpython_prefix: str, lib_prefix: str) -> bool:
return found_any
def _count_file_diff(file_a: pathlib.Path, file_b: pathlib.Path) -> int:
"""Count changed lines between two text files using difflib."""
a_content = safe_read_text(file_a)
b_content = safe_read_text(file_b)
if a_content is None or b_content is None:
return 0
if a_content == b_content:
return 0
a_lines = a_content.splitlines()
b_lines = b_content.splitlines()
count = 0
for line in difflib.unified_diff(a_lines, b_lines, lineterm=""):
if (line.startswith("+") and not line.startswith("+++")) or (
line.startswith("-") and not line.startswith("---")
):
count += 1
return count
def _count_path_diff(path_a: pathlib.Path, path_b: pathlib.Path) -> int:
"""Count changed lines between two paths (file or directory, *.py only)."""
if path_a.is_file() and path_b.is_file():
return _count_file_diff(path_a, path_b)
if path_a.is_dir() and path_b.is_dir():
total = 0
a_files = {f.relative_to(path_a) for f in path_a.rglob("*.py")}
b_files = {f.relative_to(path_b) for f in path_b.rglob("*.py")}
for rel in a_files & b_files:
total += _count_file_diff(path_a / rel, path_b / rel)
for rel in a_files - b_files:
content = safe_read_text(path_a / rel)
if content:
total += len(content.splitlines())
for rel in b_files - a_files:
content = safe_read_text(path_b / rel)
if content:
total += len(content.splitlines())
return total
return 0
def get_module_last_updated(
name: str, cpython_prefix: str, lib_prefix: str
) -> str | None:
"""Get the last git commit date for a module's Lib files."""
local_paths = []
for cpython_path in get_lib_paths(name, cpython_prefix):
if not cpython_path.exists():
continue
try:
rel_path = cpython_path.relative_to(cpython_prefix)
local_path = pathlib.Path(lib_prefix) / rel_path.relative_to("Lib")
if local_path.exists():
local_paths.append(str(local_path))
except ValueError:
continue
if not local_paths:
return None
try:
result = subprocess.run(
["git", "log", "-1", "--format=%cd", "--date=short", "--"] + local_paths,
capture_output=True,
text=True,
timeout=10,
)
if result.returncode == 0 and result.stdout.strip():
return result.stdout.strip()
except Exception:
pass
return None
def get_module_diff_stat(name: str, cpython_prefix: str, lib_prefix: str) -> int:
"""Count differing lines between cpython and local Lib for a module."""
total = 0
for cpython_path in get_lib_paths(name, cpython_prefix):
if not cpython_path.exists():
continue
try:
rel_path = cpython_path.relative_to(cpython_prefix)
local_path = pathlib.Path(lib_prefix) / rel_path.relative_to("Lib")
except ValueError:
continue
if not local_path.exists():
continue
total += _count_path_diff(cpython_path, local_path)
return total
def get_test_last_updated(
test_name: str, cpython_prefix: str, lib_prefix: str
) -> str | None:
"""Get the last git commit date for a test's files."""
cpython_path = _get_cpython_test_path(test_name, cpython_prefix)
if cpython_path is None:
return None
local_path = _get_local_test_path(cpython_path, lib_prefix)
if not local_path.exists():
return None
try:
result = subprocess.run(
["git", "log", "-1", "--format=%cd", "--date=short", "--", str(local_path)],
capture_output=True,
text=True,
timeout=10,
)
if result.returncode == 0 and result.stdout.strip():
return result.stdout.strip()
except Exception:
pass
return None
def get_test_dependencies(
test_path: pathlib.Path,
) -> dict[str, list[pathlib.Path]]: