Skip to content

Commit 58c8043

Browse files
authored
[update_lib] show deps (RustPython#6821)
* show_deps * soft deps tree * show deps CI
1 parent 274e8b4 commit 58c8043

File tree

11 files changed

+981
-95
lines changed

11 files changed

+981
-95
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
name: Lib Dependencies Check
2+
3+
on:
4+
pull_request_target:
5+
types: [opened, synchronize, reopened]
6+
paths:
7+
- 'Lib/**'
8+
9+
concurrency:
10+
group: lib-deps-${{ github.event.pull_request.number }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
check_deps:
15+
permissions:
16+
pull-requests: write
17+
runs-on: ubuntu-latest
18+
timeout-minutes: 10
19+
steps:
20+
- name: Checkout base branch
21+
uses: actions/checkout@v6.0.1
22+
with:
23+
# Use base branch for scripts (security: don't run PR code with elevated permissions)
24+
ref: ${{ github.event.pull_request.base.ref }}
25+
fetch-depth: 0
26+
27+
- name: Fetch PR head
28+
run: |
29+
git fetch origin ${{ github.event.pull_request.head.sha }}
30+
31+
- name: Checkout CPython
32+
run: |
33+
git clone --depth 1 --branch v3.14.2 https://github.com/python/cpython.git cpython
34+
35+
- name: Get changed Lib files
36+
id: changed-files
37+
run: |
38+
# Get the list of changed files under Lib/
39+
changed=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} -- 'Lib/*.py' 'Lib/**/*.py' | head -50)
40+
echo "Changed files:"
41+
echo "$changed"
42+
43+
# Extract unique module names (top-level only, skip test/)
44+
modules=""
45+
for file in $changed; do
46+
# Skip test files
47+
if [[ "$file" == Lib/test/* ]]; then
48+
continue
49+
fi
50+
# Extract module name: Lib/foo.py -> foo, Lib/foo/__init__.py -> foo
51+
module=$(echo "$file" | sed -E 's|^Lib/||; s|/__init__\.py$||; s|\.py$||; s|/.*||')
52+
if [[ -n "$module" && ! " $modules " =~ " $module " ]]; then
53+
modules="$modules $module"
54+
fi
55+
done
56+
57+
modules=$(echo "$modules" | xargs) # trim whitespace
58+
echo "Detected modules: $modules"
59+
echo "modules=$modules" >> $GITHUB_OUTPUT
60+
61+
- name: Setup Python
62+
if: steps.changed-files.outputs.modules != ''
63+
uses: actions/setup-python@v6.1.0
64+
with:
65+
python-version: "3.12"
66+
67+
- name: Run deps check
68+
if: steps.changed-files.outputs.modules != ''
69+
id: deps-check
70+
run: |
71+
# Run deps for all modules at once
72+
python scripts/update_lib deps ${{ steps.changed-files.outputs.modules }} --depth 2 > /tmp/deps_output.txt 2>&1 || true
73+
74+
# Read output for GitHub Actions
75+
echo "deps_output<<EOF" >> $GITHUB_OUTPUT
76+
cat /tmp/deps_output.txt >> $GITHUB_OUTPUT
77+
echo "EOF" >> $GITHUB_OUTPUT
78+
79+
# Check if there's any meaningful output
80+
if [ -s /tmp/deps_output.txt ]; then
81+
echo "has_output=true" >> $GITHUB_OUTPUT
82+
else
83+
echo "has_output=false" >> $GITHUB_OUTPUT
84+
fi
85+
86+
- name: Post comment
87+
if: steps.deps-check.outputs.has_output == 'true'
88+
uses: marocchino/sticky-pull-request-comment@v2
89+
with:
90+
header: lib-deps-check
91+
number: ${{ github.event.pull_request.number }}
92+
message: |
93+
## 📦 Library Dependencies
94+
95+
The following Lib/ modules were modified. Here are their dependencies:
96+
97+
<details>
98+
<summary>Click to expand dependency information</summary>
99+
100+
```
101+
${{ steps.deps-check.outputs.deps_output }}
102+
```
103+
104+
</details>
105+
106+
**Legend:**
107+
- `[+]` path exists, `[-]` path missing
108+
- `[x]` up-to-date, `[ ]` outdated
109+
- `native:` Rust/C extension modules
110+
111+
- name: Remove comment if no Lib changes
112+
if: steps.changed-files.outputs.modules == ''
113+
uses: marocchino/sticky-pull-request-comment@v2
114+
with:
115+
header: lib-deps-check
116+
number: ${{ github.event.pull_request.number }}
117+
delete: true

scripts/update_lib/__main__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ def main(argv: list[str] | None = None) -> int:
4949
help="Copy library file/directory from CPython (delete existing first)",
5050
add_help=False,
5151
)
52+
subparsers.add_parser(
53+
"deps",
54+
help="Show dependency information for a module",
55+
add_help=False,
56+
)
5257

5358
args, remaining = parser.parse_known_args(argv)
5459

@@ -77,6 +82,11 @@ def main(argv: list[str] | None = None) -> int:
7782

7883
return auto_mark_main(remaining)
7984

85+
if args.command == "deps":
86+
from update_lib.show_deps import main as show_deps_main
87+
88+
return show_deps_main(remaining)
89+
8090
return 0
8191

8292

scripts/update_lib/auto_mark.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -455,15 +455,13 @@ def extract_test_methods(contents: str) -> set[tuple[str, str]]:
455455
Returns:
456456
Set of (class_name, method_name) tuples
457457
"""
458-
import ast
458+
from update_lib.io_utils import safe_parse_ast
459+
from update_lib.patch_spec import iter_tests
459460

460-
try:
461-
tree = ast.parse(contents)
462-
except SyntaxError:
461+
tree = safe_parse_ast(contents)
462+
if tree is None:
463463
return set()
464464

465-
from update_lib.patch_spec import iter_tests
466-
467465
return {(cls_node.name, fn_node.name) for cls_node, fn_node in iter_tests(tree)}
468466

469467

scripts/update_lib/copy_lib.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,14 @@ def copy_lib(
6464

6565
# Extract module name and cpython prefix from path
6666
path_str = str(src_path).replace("\\", "/")
67-
if "/Lib/" in path_str:
68-
cpython_prefix, after_lib = path_str.split("/Lib/", 1)
69-
# Get module name (first component, without .py)
70-
name = after_lib.split("/")[0]
71-
if name.endswith(".py"):
72-
name = name[:-3]
73-
else:
74-
# Fallback: just copy the single file
75-
lib_path = parse_lib_path(src_path)
76-
_copy_single(src_path, lib_path, verbose)
77-
return
67+
if "/Lib/" not in path_str:
68+
raise ValueError(f"Path must contain '/Lib/' (got: {src_path})")
69+
70+
cpython_prefix, after_lib = path_str.split("/Lib/", 1)
71+
# Get module name (first component, without .py)
72+
name = after_lib.split("/")[0]
73+
if name.endswith(".py"):
74+
name = name[:-3]
7875

7976
# Get all paths to copy from DEPENDENCIES table
8077
all_src_paths = get_lib_paths(name, cpython_prefix)

0 commit comments

Comments
 (0)