Introduce /upgrade-pylib-next (#6827)

* Simplify upgrade-pylib.md by removing manual copy steps handled by quick command

* Let upgrade-pylib run auto-mark instead marking manually

* Let upgrade-pylib a separate commit from updating module

* Correct commands' permissions

* Add open PR check to upgrade-pylib-next command

* Fix dependency pattern example in upgrade-pylib-next command

* Let upgrade-pylib command to invetigate failing tests

* Add pre-commit review to invetigate-test-failure command

* Use deps command to get dependent tests in upgrade-pylib workflow
This commit is contained in:
Lee Dogeon
2026-01-31 07:40:39 +09:00
committed by GitHub
parent f6d562e2ee
commit 0c174624b4
3 changed files with 142 additions and 32 deletions

View File

@@ -0,0 +1,49 @@
---
allowed-tools: Bash(python3:*), Bash(cargo run:*), Read, Grep, Glob, Bash(git add:*), Bash(git commit:*), Bash(cargo fmt:*), Bash(git diff:*), Task
---
# Investigate Test Failure
Investigate why a specific test is failing and determine if it can be fixed or needs an issue.
## Arguments
- `$ARGUMENTS`: Failed test identifier (e.g., `test_inspect.TestGetSourceBase.test_getsource_reload`)
## Steps
1. **Analyze failure cause**
- Read the test code
- Analyze failure message/traceback
- Check related RustPython code
2. **Verify behavior in CPython**
- Run the test with `python3 -m unittest` to confirm expected behavior
- Document the expected output
3. **Determine fix feasibility**
- **Simple fix** (import issues, small logic bugs): Fix code → Run `cargo fmt --all` → Pre-commit review → Commit
- **Complex fix** (major unimplemented features): Collect issue info and report to user
**Pre-commit review process**:
- Run `git diff` to see the changes
- Use Task tool with `general-purpose` subagent to review:
- Compare implementation against cpython/ source code
- Verify the fix aligns with CPython behavior
- Check for any missed edge cases
- Proceed to commit only after review passes
4. **For complex issues - Collect issue information**
Following `.github/ISSUE_TEMPLATE/report-incompatibility.md` format:
- **Feature**: Description of missing/broken Python feature
- **Minimal reproduction code**: Smallest code that reproduces the issue
- **CPython behavior**: Result when running with python3
- **RustPython behavior**: Result when running with cargo run
- **Python Documentation link**: Link to relevant CPython docs
Report collected information to the user. Issue creation is done only upon user request.
Example issue creation command:
```
gh issue create --template report-incompatibility.md --title "..." --body "..."
```

View File

@@ -0,0 +1,33 @@
---
allowed-tools: Skill(upgrade-pylib), Bash(gh pr list:*)
---
# Upgrade Next Python Library
Find the next Python library module ready for upgrade and run `/upgrade-pylib` for it.
## Current TODO Status
!`cargo run --release -- scripts/update_lib todo 2>/dev/null`
## Open Upgrade PRs
!`gh pr list --search "Update in:title" --json number,title --template '{{range .}}#{{.number}} {{.title}}{{"\n"}}{{end}}'`
## Instructions
From the TODO list above, find modules matching these patterns (in priority order):
1. `[ ] [no deps]` - Modules with no dependencies (can be upgraded immediately)
2. `[ ] [0/n]` - Modules where all dependencies are already upgraded (e.g., `[0/3]`, `[0/5]`)
These patterns indicate modules that are ready to upgrade without blocking dependencies.
**Important**: Skip any modules that already have an open PR in the "Open Upgrade PRs" list above.
**After identifying a suitable module**, run:
```
/upgrade-pylib <module_name>
```
If no modules match these criteria, inform the user that all eligible modules have dependencies that need to be upgraded first.

View File

@@ -1,3 +1,7 @@
---
allowed-tools: Bash(git add:*), Bash(git commit:*), Bash(python3 scripts/update_lib quick:*), Bash(python3 scripts/update_lib auto-mark:*)
---
# Upgrade Python Library from CPython
Upgrade a Python standard library module from CPython to RustPython.
@@ -23,24 +27,19 @@ This helps improve the tooling for future upgrades.
## Steps
1. **Delete existing library in Lib/**
- If `Lib/$ARGUMENTS.py` exists, delete it
- If `Lib/$ARGUMENTS/` directory exists, delete it
2. **Copy from cpython/Lib/**
- If `cpython/Lib/$ARGUMENTS.py` exists, copy it to `Lib/$ARGUMENTS.py`
- If `cpython/Lib/$ARGUMENTS/` directory exists, copy it to `Lib/$ARGUMENTS/`
3. **Upgrade tests (quick upgrade with update_lib)**
- Run: `python3 scripts/update_lib quick cpython/Lib/test/test_$ARGUMENTS.py` (single file)
- Or: `python3 scripts/update_lib quick cpython/Lib/test/test_$ARGUMENTS/` (directory)
1. **Run quick upgrade with update_lib**
- Run: `python3 scripts/update_lib quick $ARGUMENTS` (module name)
- Or: `python3 scripts/update_lib quick cpython/Lib/$ARGUMENTS.py` (library file path)
- Or: `python3 scripts/update_lib quick cpython/Lib/$ARGUMENTS/` (library directory path)
- This will:
- Copy library files (delete existing `Lib/$ARGUMENTS.py` or `Lib/$ARGUMENTS/`, then copy from `cpython/Lib/`)
- Patch test files preserving existing RustPython markers
- Run tests and auto-mark new test failures (not regressions)
- Remove `@unittest.expectedFailure` from tests that now pass
- **Handle warnings**: If you see warnings like `WARNING: TestCFoo does not exist in remote file`, it means the class structure changed and markers couldn't be transferred automatically. These need to be manually restored in step 4 or added in step 5.
- Create a git commit with the changes
- **Handle warnings**: If you see warnings like `WARNING: TestCFoo does not exist in remote file`, it means the class structure changed and markers couldn't be transferred automatically. These need to be manually restored in step 2 or added in step 3.
4. **Review git diff and restore RUSTPYTHON-specific changes**
2. **Review git diff and restore RUSTPYTHON-specific changes**
- Run `git diff Lib/test/test_$ARGUMENTS` to review all changes
- **Only restore changes that have explicit `RUSTPYTHON` comments**. Look for:
- `# XXX: RUSTPYTHON` or `# XXX RUSTPYTHON` - Comments marking RustPython-specific code modifications
@@ -49,13 +48,46 @@ This helps improve the tooling for future upgrades.
- **Do NOT restore other diff changes** - these are likely upstream CPython changes, not RustPython-specific modifications
- When restoring, preserve the original context and formatting
5. **Verify tests**
- Run: `cargo run --release -- -m test test_$ARGUMENTS -v`
- The `-v` flag shows detailed output to identify which tests fail and why
- For each new failure, add appropriate markers based on the failure type:
- **Test assertion failure** → `@unittest.expectedFailure` with `# TODO: RUSTPYTHON` comment
- **Panic/crash** → `@unittest.skip("TODO: RUSTPYTHON; <panic message>")`
- **Class-specific markers**: If a test fails only in the C implementation (TestCFoo) but passes in the Python implementation (TestPyFoo), or vice versa, add the marker to the specific subclass, not the base class:
3. **Investigate test failures with subagent**
- First, get dependent tests using the deps command:
```
cargo run --release -- scripts/update_lib deps $ARGUMENTS
```
- Look for the line `- [ ] $ARGUMENTS: test_xxx test_yyy ...` to get the direct dependent tests
- Run those tests to collect failures:
```
cargo run --release -- -m test test_xxx test_yyy ... 2>&1 | grep -E "^(FAIL|ERROR):"
```
- For example, if deps output shows `- [ ] linecache: test_bdb test_inspect test_linecache test_traceback test_zipimport`, run:
```
cargo run --release -- -m test test_bdb test_inspect test_linecache test_traceback test_zipimport 2>&1 | grep -E "^(FAIL|ERROR):"
```
- For each failure, use the Task tool with `general-purpose` subagent to investigate:
- Subagent should follow the `/investigate-test-failure` skill workflow
- Pass the failed test identifier as the argument (e.g., `test_inspect.TestGetSourceBase.test_getsource_reload`)
- If subagent can fix the issue easily: fix and commit
- If complex issue: subagent collects issue info and reports back (issue creation on user request only)
- Using subagent prevents context pollution in the main conversation
4. **Mark remaining test failures with auto-mark**
- Run: `python3 scripts/update_lib auto-mark Lib/test/test_$ARGUMENTS.py --mark-failure`
- Or for directory: `python3 scripts/update_lib auto-mark Lib/test/test_$ARGUMENTS/ --mark-failure`
- This will:
- Run tests and mark ALL failing tests with `@unittest.expectedFailure`
- Remove `@unittest.expectedFailure` from tests that now pass
- **Note**: The `--mark-failure` flag marks all failures including regressions. Review the changes before committing.
5. **Handle panics manually**
- If any tests cause panics/crashes (not just assertion failures), they need `@unittest.skip` instead:
```python
@unittest.skip("TODO: RUSTPYTHON; panics with 'index out of bounds'")
def test_crashes(self):
...
```
- auto-mark cannot detect panics automatically - check the test output for crash messages
6. **Handle class-specific failures**
- If a test fails only in the C implementation (TestCFoo) but passes in the Python implementation (TestPyFoo), or vice versa, move the marker to the specific subclass:
```python
# Base class - no marker here
class TestFoo:
@@ -70,25 +102,21 @@ This helps improve the tooling for future upgrades.
def test_something(self):
return super().test_something()
```
- **New tests from CPython**: The upgrade may bring in entirely new tests that didn't exist before. These won't have any RUSTPYTHON markers in the diff - they just need to be tested and marked if they fail.
- Example markers:
```python
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_something(self):
...
# TODO: RUSTPYTHON
@unittest.skip("TODO: RUSTPYTHON; panics with 'index out of bounds'")
def test_crashes(self):
...
```
7. **Commit the test fixes**
- Run: `git add -u && git commit -m "Mark failing tests"`
- This creates a separate commit for the test markers added in steps 2-6
## Example Usage
```
# Using module names (recommended)
/upgrade-pylib inspect
/upgrade-pylib json
/upgrade-pylib asyncio
# Using library paths (alternative)
/upgrade-pylib cpython/Lib/inspect.py
/upgrade-pylib cpython/Lib/json/
```
## Example: Restoring RUSTPYTHON changes