Skip to content

Commit 32edb42

Browse files
committed
show hard deps on todo/deps
1 parent 2922862 commit 32edb42

File tree

3 files changed

+134
-25
lines changed

3 files changed

+134
-25
lines changed

scripts/update_lib/deps.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,32 @@ def clear_import_graph_caches() -> None:
100100
"test_userlist.py",
101101
],
102102
},
103+
"__future__": {
104+
"test": [
105+
"test___future__.py",
106+
"test_future_stmt.py",
107+
],
108+
},
109+
"site": {
110+
"hard_deps": ["_sitebuiltins.py"],
111+
},
112+
"opcode": {
113+
"hard_deps": ["_opcode_metadata.py"],
114+
"test": [
115+
"test_opcode.py",
116+
"test__opcode.py",
117+
"test_opcodes.py",
118+
],
119+
},
120+
"pickle": {
121+
"hard_deps": ["_compat_pickle.py"],
122+
},
123+
"re": {
124+
"hard_deps": ["sre_compile.py", "sre_constants.py", "sre_parse.py"],
125+
},
126+
"weakref": {
127+
"hard_deps": ["_weakrefset.py"],
128+
},
103129
"codecs": {
104130
"test": [
105131
"test_codecs.py",
@@ -145,6 +171,7 @@ def clear_import_graph_caches() -> None:
145171
},
146172
# test_htmlparser tests html.parser
147173
"html": {
174+
"hard_deps": ["_markupbase.py"],
148175
"test": ["test_html.py", "test_htmlparser.py"],
149176
},
150177
"xml": {
@@ -257,6 +284,7 @@ def clear_import_graph_caches() -> None:
257284
],
258285
},
259286
"threading": {
287+
"hard_deps": ["_threading_local.py"],
260288
"test": [
261289
"test_threading.py",
262290
"test_threadedtempfile.py",
@@ -309,11 +337,29 @@ def clear_import_graph_caches() -> None:
309337
],
310338
},
311339
"datetime": {
340+
"hard_deps": ["_strptime.py"],
312341
"test": [
313342
"test_datetime.py",
314343
"test_strptime.py",
315344
],
316345
},
346+
"concurrent": {
347+
"test": [
348+
"test_concurrent_futures",
349+
],
350+
},
351+
"locale": {
352+
"test": [
353+
"test_locale.py",
354+
"test__locale.py",
355+
],
356+
},
357+
"numbers": {
358+
"test": [
359+
"test_numbers.py",
360+
"test_abstract_numbers.py",
361+
],
362+
},
317363
"file": {
318364
"lib": [],
319365
"test": [
@@ -328,6 +374,13 @@ def clear_import_graph_caches() -> None:
328374
"test_ioctl.py",
329375
],
330376
},
377+
"select": {
378+
"lib": [],
379+
"test": [
380+
"test_select.py",
381+
"test_poll.py",
382+
],
383+
},
331384
"xmlrpc": {
332385
"test": [
333386
"test_xmlrpc.py",
@@ -535,6 +588,33 @@ def get_lib_paths(
535588
return tuple(paths)
536589

537590

591+
def get_all_hard_deps(name: str, cpython_prefix: str) -> list[str]:
592+
"""Get all hard_deps for a module (explicit + auto-detected).
593+
594+
Args:
595+
name: Module name (e.g., "decimal", "datetime")
596+
cpython_prefix: CPython directory prefix
597+
598+
Returns:
599+
List of hard_dep names (without .py extension)
600+
"""
601+
dep_info = DEPENDENCIES.get(name, {})
602+
hard_deps = set()
603+
604+
# Explicit hard_deps from DEPENDENCIES
605+
for hd in dep_info.get("hard_deps", []):
606+
# Remove .py extension if present
607+
hard_deps.add(hd[:-3] if hd.endswith(".py") else hd)
608+
609+
# Auto-detect _py{module}.py or _py_{module}.py patterns
610+
for pattern in [f"_py{name}.py", f"_py_{name}.py"]:
611+
auto_path = construct_lib_path(cpython_prefix, pattern)
612+
if auto_path.exists():
613+
hard_deps.add(auto_path.stem)
614+
615+
return sorted(hard_deps)
616+
617+
538618
@functools.cache
539619
def get_test_paths(
540620
name: str, cpython_prefix: str

scripts/update_lib/show_deps.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,9 @@ def get_all_modules(cpython_prefix: str) -> list[str]:
4646
else:
4747
continue
4848

49-
# Skip private modules that are hard_deps of other modules
50-
# e.g., _pydatetime is a hard_dep of datetime, so skip it
51-
if (
52-
name.startswith("_")
53-
and resolve_hard_dep_parent(name, cpython_prefix) is not None
54-
):
49+
# Skip modules that are hard_deps of other modules
50+
# e.g., _pydatetime is a hard_dep of datetime, pydoc_data is a hard_dep of pydoc
51+
if resolve_hard_dep_parent(name, cpython_prefix) is not None:
5552
continue
5653

5754
modules.add(name)

scripts/update_lib/show_todo.py

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@ def compute_todo_list(
3232
Returns:
3333
List of dicts with module info, sorted by priority
3434
"""
35-
from update_lib.deps import get_rust_deps, get_soft_deps, is_up_to_date
35+
from update_lib.deps import (
36+
get_all_hard_deps,
37+
get_rust_deps,
38+
get_soft_deps,
39+
is_up_to_date,
40+
)
3641
from update_lib.show_deps import get_all_modules
3742

3843
all_modules = get_all_modules(cpython_prefix)
@@ -44,11 +49,19 @@ def compute_todo_list(
4449
native_deps = get_rust_deps(name, cpython_prefix)
4550
up_to_date = is_up_to_date(name, cpython_prefix, lib_prefix)
4651

52+
# Get hard_deps and check their status
53+
hard_deps = get_all_hard_deps(name, cpython_prefix)
54+
hard_deps_status = {
55+
hd: is_up_to_date(hd, cpython_prefix, lib_prefix)
56+
for hd in hard_deps
57+
}
58+
4759
module_data[name] = {
4860
"name": name,
4961
"soft_deps": soft_deps,
5062
"native_deps": native_deps,
5163
"up_to_date": up_to_date,
64+
"hard_deps_status": hard_deps_status,
5265
}
5366

5467
# Build reverse dependency map: who depends on this module
@@ -61,8 +74,11 @@ def compute_todo_list(
6174
# Compute scores and filter
6275
result = []
6376
for name, data in module_data.items():
64-
# Skip already up-to-date modules (unless --done)
65-
if data["up_to_date"] and not include_done:
77+
hard_deps_status = data["hard_deps_status"]
78+
has_outdated_hard_deps = any(not ok for ok in hard_deps_status.values())
79+
80+
# Include if: not up-to-date, or has outdated hard_deps, or --done
81+
if data["up_to_date"] and not has_outdated_hard_deps and not include_done:
6682
continue
6783

6884
soft_deps = data["soft_deps"]
@@ -90,6 +106,7 @@ def compute_todo_list(
90106
"native_deps": data["native_deps"],
91107
"soft_deps": soft_deps,
92108
"up_to_date": data["up_to_date"],
109+
"hard_deps_status": hard_deps_status,
93110
}
94111
)
95112

@@ -202,13 +219,15 @@ def get_original_files(
202219
cpython_prefix: str,
203220
lib_prefix: str,
204221
) -> list[str]:
205-
"""Get files that exist in our Lib but not in cpython/Lib.
222+
"""Get top-level files/modules that exist in our Lib but not in cpython/Lib.
206223
207224
These are RustPython-original files that don't come from CPython.
225+
Modules that exist in cpython are handled by the library todo (even if
226+
they have additional local files), so they are excluded here.
208227
Excludes test/ directory (handled separately).
209228
210229
Returns:
211-
Sorted list of relative paths (e.g., ["_rustpython_tokenize.py"])
230+
Sorted list of top-level names (e.g., ["_dummy_thread.py"])
212231
"""
213232
cpython_lib = pathlib.Path(cpython_prefix) / "Lib"
214233
local_lib = pathlib.Path(lib_prefix)
@@ -218,26 +237,26 @@ def get_original_files(
218237

219238
original = []
220239

221-
for local_file in local_lib.rglob("*"):
222-
# Skip directories
223-
if local_file.is_dir():
224-
continue
240+
# Only check top-level entries
241+
for entry in local_lib.iterdir():
242+
name = entry.name
225243

226-
# Get relative path from Lib/
227-
rel_path = local_file.relative_to(local_lib)
244+
# Skip hidden files and __pycache__
245+
if name.startswith(".") or name == "__pycache__":
246+
continue
228247

229248
# Skip test/ directory (handled separately)
230-
if rel_path.parts and rel_path.parts[0] == "test":
249+
if name == "test":
231250
continue
232251

233-
# Skip __pycache__ directories
234-
if "__pycache__" in rel_path.parts:
252+
# Skip site-packages (not a module)
253+
if name == "site-packages":
235254
continue
236255

237-
# Check if exists in cpython lib
238-
cpython_file = cpython_lib / rel_path
239-
if not cpython_file.exists():
240-
original.append(str(rel_path))
256+
# Only include if it doesn't exist in cpython at all
257+
cpython_entry = cpython_lib / name
258+
if not cpython_entry.exists():
259+
original.append(name)
241260

242261
return sorted(original)
243262

@@ -559,12 +578,25 @@ def format_todo_list(
559578

560579
rev_str = f"{rev_count} dependents" if rev_count else ""
561580

562-
parts = ["-", done_mark, f"[{score_str}]", name]
581+
parts = ["-", done_mark, f"[{score_str}]", f"`{name}`"]
563582
if rev_str:
564583
parts.append(f"({rev_str})")
565584

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

587+
# Show hard_deps:
588+
# - Normal mode: only show if lib is up-to-date but hard_deps are not
589+
# - Verbose mode: always show all hard_deps with their status
590+
hard_deps_status = item.get("hard_deps_status", {})
591+
if verbose and hard_deps_status:
592+
for hd in sorted(hard_deps_status.keys()):
593+
hd_mark = "[x]" if hard_deps_status[hd] else "[ ]"
594+
lines.append(f" - {hd_mark} {hd} (hard_dep)")
595+
elif item["up_to_date"]:
596+
for hd, ok in sorted(hard_deps_status.items()):
597+
if not ok:
598+
lines.append(f" - [ ] {hd} (hard_dep)")
599+
568600
# Show corresponding tests if exist
569601
if test_by_lib and name in test_by_lib:
570602
for test_info in test_by_lib[name]:

0 commit comments

Comments
 (0)