Skip to content

Conversation

@moreal
Copy link
Contributor

@moreal moreal commented Jan 16, 2026

The command was improved based on the process of updating the json module using the /update-pylib command. Additionally, the script was modified to add import unittest if it is missing.

I worked on #6743 with this pull request's changes.

Summary by CodeRabbit

  • Documentation

    • Added a detailed, step-by-step upgrade workflow for migrating tests, including examples, diff-review guidance, explicit restoration criteria for environment-specific markers, and a new example showing how to restore changes.
  • New Features

    • Enhanced test-upgrade tooling to support single-file and directory upgrades, a verification flow to run and mark tests based on outcomes, and automatic insertion of missing test imports when applying patches.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 16, 2026

📝 Walkthrough

Walkthrough

Adds a detailed multi-step test-upgrade workflow document and updates scripts/lib_updater.py to detect and insert a missing import unittest into the import block when applying modifications that require it.

Changes

Cohort / File(s) Summary
Documentation & Workflow
.claude/commands/upgrade-pylib.md
Replaced a single-line "Upgrade tests" entry with a comprehensive step-by-step upgrade workflow: quick-upgrade for single files or directories, diff review and restoration criteria for RUSTPYTHON-specific changes, verification/run guidance with marker application, and an example for restoring RUSTPYTHON edits.
Import Detection & Insertion
scripts/lib_updater.py
Added has_unittest_import(tree: ast.Module) -> bool and `find_import_insert_line(tree: ast.Module, lines: list[str]

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

skip:ci

Suggested reviewers

  • ShaharNaveh
  • youknowone
  • reactive-firewall

Poem

🐰 I hopped through tests and tidy lines,
Placed an import where it now shines,
Docs guide the hop, diffs show the trail,
Patches snug—no rabbit fail,
A tiny drumbeat: patch and cheer!

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Complement upgrade-pylib Claude Code command' is partially related to the changeset, referring to improvements to the upgrade-pylib workflow, but it is vague and doesn't specify the main changes (adding import unittest detection and enhanced upgrade documentation). Consider a more specific title such as 'Add unittest import detection to upgrade-pylib' or 'Enhance upgrade-pylib command with automated import handling and improved docs'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings


📜 Recent review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6679e0e and 95f822d.

📒 Files selected for processing (2)
  • .claude/commands/upgrade-pylib.md
  • scripts/lib_updater.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.py: Follow PEP 8 style for custom Python code
Use ruff for linting Python code

Files:

  • scripts/lib_updater.py
🧠 Learnings (13)
📚 Learning: 2026-01-14T14:52:10.778Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.778Z
Learning: Applies to Lib/**/*.py : Add a `# TODO: RUSTPYTHON` comment when modifications are made to Lib/ directory files

Applied to files:

  • .claude/commands/upgrade-pylib.md
📚 Learning: 2026-01-14T14:52:10.778Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.778Z
Learning: Applies to Lib/**/*.py : Minimize modifications to CPython standard library files in the Lib/ directory; these files should be edited very conservatively and modifications should be minimal and only to work around RustPython limitations

Applied to files:

  • .claude/commands/upgrade-pylib.md
📚 Learning: 2026-01-14T14:52:10.778Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.778Z
Learning: Applies to Lib/test/**/*.py : Use `unittest.skip("TODO: RustPython <reason>")` or `unittest.expectedFailure` with `# TODO: RUSTPYTHON <reason>` comment when marking tests in Lib/ that cannot run

Applied to files:

  • .claude/commands/upgrade-pylib.md
  • scripts/lib_updater.py
📚 Learning: 2025-08-30T14:40:05.858Z
Learnt from: ShaharNaveh
Repo: RustPython/RustPython PR: 6089
File: scripts/lib_updater.py:292-297
Timestamp: 2025-08-30T14:40:05.858Z
Learning: In scripts/lib_updater.py, the --inplace flag intentionally writes to orig_file (not remote_file) even though patches are applied to remote_file content. This workflow allows updating the original RustPython test file with patches applied to new upstream CPython content.

Applied to files:

  • .claude/commands/upgrade-pylib.md
📚 Learning: 2026-01-14T14:52:10.779Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.779Z
Learning: Do not edit the Lib/ directory directly except for copying files from CPython to work around RustPython limitations

Applied to files:

  • .claude/commands/upgrade-pylib.md
📚 Learning: 2026-01-14T14:52:10.779Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.779Z
Learning: When comparing behavior with CPython, use `python` command to explicitly run CPython and `cargo run -- script.py` to run RustPython

Applied to files:

  • .claude/commands/upgrade-pylib.md
📚 Learning: 2026-01-14T14:52:10.779Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.779Z
Learning: Use `cargo run -- script.py` instead of `python script.py` when testing Python code with RustPython

Applied to files:

  • .claude/commands/upgrade-pylib.md
📚 Learning: 2026-01-14T14:52:10.779Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.779Z
Learning: Run a full clean build when modifying bytecode instructions using: `rm -r target/debug/build/rustpython-* && find . | grep -E "\.pyc$" | xargs rm -r`

Applied to files:

  • .claude/commands/upgrade-pylib.md
📚 Learning: 2026-01-14T14:52:10.779Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.779Z
Learning: In most cases, Python code should not be edited; bug fixes should be made through Rust code modifications only

Applied to files:

  • .claude/commands/upgrade-pylib.md
📚 Learning: 2025-09-07T05:38:31.690Z
Learnt from: ShaharNaveh
Repo: RustPython/RustPython PR: 6089
File: scripts/lib_updater.py:198-202
Timestamp: 2025-09-07T05:38:31.690Z
Learning: In scripts/lib_updater.py, the iter_patches function intentionally does not handle SyntaxError from ast.parse(contents). The author confirmed this behavior is fine and intended - the tool should fail fast on unparseable files rather than silently skip processing.

Applied to files:

  • scripts/lib_updater.py
📚 Learning: 2026-01-14T14:52:10.778Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.778Z
Learning: Applies to **/test*.py : Only remove `unittest.expectedFailure` decorators and upper TODO comments from tests when tests actually pass, or add these decorators when tests cannot be fixed

Applied to files:

  • scripts/lib_updater.py
📚 Learning: 2026-01-14T14:52:10.778Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.778Z
Learning: Applies to **/test*.py : NEVER comment out or delete any test code lines except for removing `unittest.expectedFailure` decorators and upper TODO comments

Applied to files:

  • scripts/lib_updater.py
📚 Learning: 2026-01-14T14:52:10.778Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.778Z
Learning: Applies to **/test*.py : When a test cannot pass due to missing language features, keep it as expectedFailure and document the reason instead of modifying the test

Applied to files:

  • scripts/lib_updater.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Run snippets and cpython tests (windows-2025)
🔇 Additional comments (4)
scripts/lib_updater.py (2)

273-321: Solid import detection + insertion-point logic.

The helpers correctly avoid aliased imports and respect docstring/shebang/encoding placement.


330-338: Import insertion is appropriately gated.

Only adds import unittest when patches exist and it’s missing, keeping output minimal.

.claude/commands/upgrade-pylib.md (2)

18-73: Clear, actionable upgrade workflow.

The expanded steps and failure/marker guidance read well.


82-108: Helpful restoration example and notes.

Nice concrete diff/restore example and crisp reminders about marker usage.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

Code has been automatically formatted

The code in this PR has been formatted using:

  • ruff format
    Please pull the latest changes before pushing again:
git pull origin enhance-upgrade-pylib

@moreal moreal force-pushed the enhance-upgrade-pylib branch from fab8624 to 708f227 Compare January 16, 2026 11:27
@moreal moreal marked this pull request as ready for review January 16, 2026 11:32
for alias in node.names:
if alias.name == UT:
return True
elif isinstance(node, ast.ImportFrom):
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we should remove this elif since we want to bring unittest into scope

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right! 😓 I'll fix it now...

Copy link
Collaborator

Choose a reason for hiding this comment

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

we can maybe adjust the decorator to __import__('unittest').expectedFailure and not needing this patch at all?

Copy link
Contributor Author

@moreal moreal Jan 16, 2026

Choose a reason for hiding this comment

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

That would certainly allow us to fully resolve the question of where to place the imports, but the code does feel a bit awkward to read, and I also wonder whether calling __import__ every time—while not significant—might slightly slow down the tests. I don’t have strong convictions or a firm opinion on this.

I’ll follow the maintainers’ decision.

Copy link
Collaborator

Choose a reason for hiding this comment

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

In CPython this will have very minimal performance cost due to modules being cached. I ain't sure about RustPython:/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that’s right. I think it’s probably well optimized. It just feels a bit awkward to look at code like the example below.

@__import__('unittest').expectedFailure
def test_xxx():
  # Something

Functionally, there’s no major issue, so I’m mainly interested in hearing the maintainers’ (i.e., the people primarily responsible for maintenance) opinions. Would you be able to share your thoughts on this thread or on this PR? @youknowone

Copy link
Collaborator

Choose a reason for hiding this comment

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

We can maybe check if there's a import unittest node in the AST and switch between @unittest.X or __import__('unittest').X, as I feel like most tests do import unittest

Copy link
Member

Choose a reason for hiding this comment

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

Well, while that will work perfectly, we still need to keep them human friendly.
Most of people with python-background are familiar with @unittest.X. So let's stick on it.

Performance doesn't matter.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@scripts/lib_updater.py`:
- Around line 273-283: has_unittest_import currently treats both "from unittest
import ..." and aliased imports as if the name unittest is available, which is
incorrect for decorators using `@unittest`.*; update has_unittest_import to only
return True when there is an ast.Import whose alias.name equals UT and
alias.asname is None (i.e., a plain "import unittest"), and do not treat
ast.ImportFrom (from unittest import ...) or imports with asname (import
unittest as ut) as satisfying the requirement; reference the has_unittest_import
function and the UT symbol when making this change.
- Around line 286-309: The current logic inserts "import unittest" at line 0
when no imports exist, which can place it before a module docstring or
encoding/shebang; update the insertion logic so it picks a safe line after any
shebang/encoding comments and the module docstring: either enhance
find_import_insert_line to detect and return the end line of the module
docstring (use ast.get_docstring(tree, clean=False) or inspect tree.body[0] if
it's an ast.Expr/ast.Constant string node) and use that end_lineno if > 0, and
also scan the top of the original file lines for a shebang (#!) and PEP-263
encoding comment and advance the insertion line past them; apply this change
where apply_patches calls find_import_insert_line (and keep the check using
modifications, has_unittest_import, and iter_patch_lines intact).
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 746e71a and 708f227.

📒 Files selected for processing (2)
  • .claude/commands/upgrade-pylib.md
  • scripts/lib_updater.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.py: Follow PEP 8 style for custom Python code
Use ruff for linting Python code

Files:

  • scripts/lib_updater.py
🧠 Learnings (12)
📓 Common learnings
Learnt from: ShaharNaveh
Repo: RustPython/RustPython PR: 6089
File: scripts/lib_updater.py:292-297
Timestamp: 2025-08-30T14:40:05.858Z
Learning: In scripts/lib_updater.py, the --inplace flag intentionally writes to orig_file (not remote_file) even though patches are applied to remote_file content. This workflow allows updating the original RustPython test file with patches applied to new upstream CPython content.
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.778Z
Learning: Applies to Lib/test/**/*.py : Use `unittest.skip("TODO: RustPython <reason>")` or `unittest.expectedFailure` with `# TODO: RUSTPYTHON <reason>` comment when marking tests in Lib/ that cannot run
📚 Learning: 2026-01-14T14:52:10.778Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.778Z
Learning: Applies to Lib/**/*.py : Minimize modifications to CPython standard library files in the Lib/ directory; these files should be edited very conservatively and modifications should be minimal and only to work around RustPython limitations

Applied to files:

  • .claude/commands/upgrade-pylib.md
📚 Learning: 2026-01-14T14:52:10.778Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.778Z
Learning: Applies to Lib/**/*.py : Add a `# TODO: RUSTPYTHON` comment when modifications are made to Lib/ directory files

Applied to files:

  • .claude/commands/upgrade-pylib.md
📚 Learning: 2025-08-30T14:40:05.858Z
Learnt from: ShaharNaveh
Repo: RustPython/RustPython PR: 6089
File: scripts/lib_updater.py:292-297
Timestamp: 2025-08-30T14:40:05.858Z
Learning: In scripts/lib_updater.py, the --inplace flag intentionally writes to orig_file (not remote_file) even though patches are applied to remote_file content. This workflow allows updating the original RustPython test file with patches applied to new upstream CPython content.

Applied to files:

  • .claude/commands/upgrade-pylib.md
  • scripts/lib_updater.py
📚 Learning: 2026-01-14T14:52:10.778Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.778Z
Learning: Applies to Lib/test/**/*.py : Use `unittest.skip("TODO: RustPython <reason>")` or `unittest.expectedFailure` with `# TODO: RUSTPYTHON <reason>` comment when marking tests in Lib/ that cannot run

Applied to files:

  • .claude/commands/upgrade-pylib.md
📚 Learning: 2026-01-14T14:52:10.779Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.779Z
Learning: Do not edit the Lib/ directory directly except for copying files from CPython to work around RustPython limitations

Applied to files:

  • .claude/commands/upgrade-pylib.md
📚 Learning: 2026-01-14T14:52:10.779Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.779Z
Learning: When comparing behavior with CPython, use `python` command to explicitly run CPython and `cargo run -- script.py` to run RustPython

Applied to files:

  • .claude/commands/upgrade-pylib.md
📚 Learning: 2026-01-14T14:52:10.779Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.779Z
Learning: Use `cargo run -- script.py` instead of `python script.py` when testing Python code with RustPython

Applied to files:

  • .claude/commands/upgrade-pylib.md
📚 Learning: 2026-01-14T14:52:10.779Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.779Z
Learning: Run a full clean build when modifying bytecode instructions using: `rm -r target/debug/build/rustpython-* && find . | grep -E "\.pyc$" | xargs rm -r`

Applied to files:

  • .claude/commands/upgrade-pylib.md
📚 Learning: 2026-01-14T14:52:10.779Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.779Z
Learning: In most cases, Python code should not be edited; bug fixes should be made through Rust code modifications only

Applied to files:

  • .claude/commands/upgrade-pylib.md
📚 Learning: 2025-09-07T05:38:31.690Z
Learnt from: ShaharNaveh
Repo: RustPython/RustPython PR: 6089
File: scripts/lib_updater.py:198-202
Timestamp: 2025-09-07T05:38:31.690Z
Learning: In scripts/lib_updater.py, the iter_patches function intentionally does not handle SyntaxError from ast.parse(contents). The author confirmed this behavior is fine and intended - the tool should fail fast on unparseable files rather than silently skip processing.

Applied to files:

  • scripts/lib_updater.py
📚 Learning: 2026-01-14T14:52:10.778Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-14T14:52:10.778Z
Learning: Applies to **/test*.py : Only remove `unittest.expectedFailure` decorators and upper TODO comments from tests when tests actually pass, or add these decorators when tests cannot be fixed

Applied to files:

  • scripts/lib_updater.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Run rust tests (windows-2025)
  • GitHub Check: Run rust tests (macos-latest)
  • GitHub Check: Run rust tests (ubuntu-latest)
  • GitHub Check: Run snippets and cpython tests (windows-2025)
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Ensure compilation on various targets
  • GitHub Check: Check Rust code with clippy
🔇 Additional comments (1)
.claude/commands/upgrade-pylib.md (1)

18-107: Documentation updates are clear and actionable.

The expanded workflow and examples align well with the updated tooling and should reduce operator confusion.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@moreal moreal force-pushed the enhance-upgrade-pylib branch from 708f227 to 6679e0e Compare January 16, 2026 11:42
@moreal moreal marked this pull request as draft January 16, 2026 11:43
@moreal moreal force-pushed the enhance-upgrade-pylib branch from 6679e0e to 95f822d Compare January 16, 2026 12:01
@moreal moreal marked this pull request as ready for review January 16, 2026 12:21
@youknowone
Copy link
Member

@ShaharNaveh As the major author of lib_updater, please be a major reviewer of this PR. I will merge it once you approve it. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants