Add regression tests for six silently-fixed issues (close #4317 #4859 #4860 #4861 #4863 #5154)#7635
Conversation
📝 WalkthroughWalkthroughAdd a new regression test script with six independent snippets exercising starmap self-referential iteration, asyncio Task repr/await, deep ElementTree creation/deletion, two destructor (del) behaviors, and a compiler stability compile() case. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
extra_tests/snippets/regression_closed_issues.py (1)
27-27: Resolve the Ruff warnings without changing the regression shapes.The
starmap(zip(...))shape is intentional for#4317, so suppress those exact lint rules inline. The class-level list flags can be plain booleans to avoid RUF012 while preserving destructor behavior. As per coding guidelines, use Ruff for linting Python code.Proposed Ruff-compatible adjustments
for _i in range(1000): - _b = starmap(_i, zip(_b, _b)) + _b = starmap(_i, zip(_b, _b)) # noqa: B905, RUF058 - exact `#4317` shape assert list(_b) == []class _Cls4861: - called = [False] + called = False def __del__(self): - _Cls4861.called[0] = True + _Cls4861.called = True _c = _Cls4861() _c = range(10) # reassignment triggers __del__ on the original instance del _c -assert _Cls4861.called[0] +assert _Cls4861.calledclass _Dummy4863: - fired = [False] + fired = False def __del__(self): - if _Dummy4863.fired[0]: + if _Dummy4863.fired: return - _Dummy4863.fired[0] = True + _Dummy4863.fired = True type(self)() # this is the exact shape that used to panic _d = _Dummy4863() del _d -assert _Dummy4863.fired[0] +assert _Dummy4863.firedAlso applies to: 71-81, 91-103
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extra_tests/snippets/regression_closed_issues.py` at line 27, Suppress the specific Ruff lint warnings inline for the intentional starmap(zip(...)) pattern around the expression `_b = starmap(_i, zip(_b, _b))` (use per-line noqa comments for the exact rules raised) so the shape required for the regression test remains unchanged; also change class-level list "flag" attributes to plain booleans (replace any top-level empty-list flags with False) to avoid RUF012 while preserving destructor behavior, and apply the same inline noqa and boolean-flag fixes to the other instances referenced (around lines 71-81 and 91-103) so Ruff passes without altering runtime behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@extra_tests/snippets/regression_closed_issues.py`:
- Around line 55-61: The snippet leaves _node referencing the deepest Element so
the tree isn't fully released; before deleting _root and clearing _deep, remove
the local reference to the deepest node (e.g., del _node or set _node = None) so
that no local variables hold onto any Element; ensure you reference the same
variables used in the snippet (_root, _node, _deep) when removing the reference.
---
Nitpick comments:
In `@extra_tests/snippets/regression_closed_issues.py`:
- Line 27: Suppress the specific Ruff lint warnings inline for the intentional
starmap(zip(...)) pattern around the expression `_b = starmap(_i, zip(_b, _b))`
(use per-line noqa comments for the exact rules raised) so the shape required
for the regression test remains unchanged; also change class-level list "flag"
attributes to plain booleans (replace any top-level empty-list flags with False)
to avoid RUF012 while preserving destructor behavior, and apply the same inline
noqa and boolean-flag fixes to the other instances referenced (around lines
71-81 and 91-103) so Ruff passes without altering runtime behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 60a5e75d-17c3-49f0-8ebb-fe6297989745
📒 Files selected for processing (1)
extra_tests/snippets/regression_closed_issues.py
74692eb to
8c61747
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (2)
extra_tests/snippets/regression_closed_issues.py (2)
25-28: Suppress intentional ruff violations with# noqato keep lint clean.This section faithfully reproduces the original
#4317report, so the ruff findings (RUF058starmap-on-zip,B905missingstrict=) are deliberate — the self-referentialstarmap(_i, zip(_b, _b))shape is the bug trigger and must not be rewritten tomap/strict=True. The same applies to the mutable class attributes on lines 75 and 95 (RUF012), which are used as cross-invocation flags for__del__. Add targeted# noqacodes so CI ruff runs stay green while preserving the regression shape.As per coding guidelines: "Use ruff for linting Python code".
Proposed suppressions
_b = [] for _i in range(1000): - _b = starmap(_i, zip(_b, _b)) + _b = starmap(_i, zip(_b, _b)) # noqa: RUF058, B905 — exact shape from `#4317` assert list(_b) == []class _Cls4861: - called = [False] + called = [False] # noqa: RUF012 — used as a destructor-visible flagclass _Dummy4863: - fired = [False] + fired = [False] # noqa: RUF012 — used as a one-shot __del__ guard🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extra_tests/snippets/regression_closed_issues.py` around lines 25 - 28, The test intentionally reproduces the ruff issues: add targeted noqa suppressions so CI stays green while preserving the regression shape — append "# noqa: RUF058,B905" to the line with the starmap call (the line using starmap(_i, zip(_b, _b)) where _b and _i are used) and append "# noqa: RUF012" to the two mutable class attribute declarations used as cross-invocation __del__ flags (the mutable attributes referenced around the class definitions). Ensure you only add these specific codes and do not change the starmap/zip pattern or the mutable attributes themselves.
67-84: The regression test correctly exercises the reassignment path. RustPython uses immediate refcount-driven finalization matching CPython: when_c = range(10)reassigns, the previous_Cls4861()instance's refcount drops to 0, triggeringPyObjectRef::drop()which synchronously invokesdrop_slow_inner()and__del__before the assignment completes. The assertion at line 84 passes for the correct reason—finalization occurs during reassignment, not deferred todel _cor shutdown.The proposed assertion tightening is optional but reasonable as defensive documentation:
Optional improvement
_c = _Cls4861() _c = range(10) # reassignment triggers __del__ on the original instance +assert _Cls4861.called[0], "__del__ did not run during reassignment" del _c assert _Cls4861.called[0]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extra_tests/snippets/regression_closed_issues.py` around lines 67 - 84, The test currently verifies that _Cls4861.__del__ ran by asserting _Cls4861.called[0] after `del _c`; make this explicit by asserting the flag immediately after the reassignment to document and lock the intended behavior: after `_c = range(10)` assert `_Cls4861.called[0]` to show finalization occurred during reassignment (refer to the class _Cls4861, its __del__ method, and the variable _c); this is an optional defensive change that clarifies the test's intent without changing semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@extra_tests/snippets/regression_closed_issues.py`:
- Around line 25-28: The test intentionally reproduces the ruff issues: add
targeted noqa suppressions so CI stays green while preserving the regression
shape — append "# noqa: RUF058,B905" to the line with the starmap call (the line
using starmap(_i, zip(_b, _b)) where _b and _i are used) and append "# noqa:
RUF012" to the two mutable class attribute declarations used as cross-invocation
__del__ flags (the mutable attributes referenced around the class definitions).
Ensure you only add these specific codes and do not change the starmap/zip
pattern or the mutable attributes themselves.
- Around line 67-84: The test currently verifies that _Cls4861.__del__ ran by
asserting _Cls4861.called[0] after `del _c`; make this explicit by asserting the
flag immediately after the reassignment to document and lock the intended
behavior: after `_c = range(10)` assert `_Cls4861.called[0]` to show
finalization occurred during reassignment (refer to the class _Cls4861, its
__del__ method, and the variable _c); this is an optional defensive change that
clarifies the test's intent without changing semantics.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 799b2aa6-fdf0-4e79-a0eb-334000f42ccb
📒 Files selected for processing (1)
extra_tests/snippets/regression_closed_issues.py
| # unwound until SIGSEGV. Evaluation now terminates cleanly. | ||
| _b = [] | ||
| for _i in range(1000): | ||
| _b = starmap(_i, zip(_b, _b)) |
There was a problem hiding this comment.
this is not worth to add. we have starmap tests in Lib/test/test_itertools.py
Closing the test will be enoguh
|
|
||
| # --- #4859: repr() of an asyncio Task ------------------------------------- | ||
| # | ||
| # Original report: inside `asyncio.run`, constructing a Task and calling |
There was a problem hiding this comment.
stdlib_asyncio.py will be better
| # here to keep the snippet fast) now completes cleanly when all element | ||
| # references are released within the test — relying on module teardown | ||
| # would let a cleanup-path regression hide from this snippet. | ||
| _root = ET.Element("x") |
There was a problem hiding this comment.
I expect test_xml famliy covers this.
| # `type(self)()` pattern inside `__del__` exactly once; a one-shot guard | ||
| # prevents the constructor chain from cascading (which CPython handles via | ||
| # its recursion limit but at noticeable cost). | ||
| class _Dummy4863: |
There was a problem hiding this comment.
This must be covered by some Lib/test tests, but if not sure, builtin_type.py please.
| while lexer.read_one(): | ||
| pass | ||
| ''' | ||
| compile(_src_5154, "<#5154>", "exec") |
There was a problem hiding this comment.
extra_tests/snippets/syntax_ will be the right place.
Each section in the new extra_tests/snippets/regression_closed_issues.py pins a minimal reproduction of a bug that upstream code has since fixed but whose GitHub issue was never linked by a closing PR. Running the snippet exercises every pattern without crashing, which future refactors will be unable to regress without a visible test failure. Tests and issues closed: * RustPython#4317 starmap over self-referential zip used to SIGSEGV after a few thousand iterations; the loop now completes cleanly with an empty list. * RustPython#4859 repr() of an asyncio.Task panicked inside asyncio.run; a non-empty string is now returned. * RustPython#4860 deeply-nested xml.etree SubElement trees used to segfault when the root reference was dropped; 200-level trees now unwind without error. * RustPython#4861 reassigning a name held by an instance while the instance's __del__ ran produced a null reference panic; the regression verifies the __del__ fires and the process continues normally. * RustPython#4863 __del__ method calling type(self)() panicked with a Result::unwrap on Err; the pattern now runs cleanly when guarded against infinite cascades. * RustPython#5154 the while/else + trailing dict-literal shape CReduced from scrapscript.py no longer crashes the compiler; compile() on the reduced source succeeds. Closes RustPython#4317. Closes RustPython#4859. Closes RustPython#4860. Closes RustPython#4861. Closes RustPython#4863. Closes RustPython#5154. Verified: - ./target/release/rustpython extra_tests/snippets/regression_closed_issues.py exits 0 immediately. - python3 (CPython 3.13) on the same file exits 0 immediately — no test depends on RustPython-specific behavior. - cargo fmt --check passes.
8c61747 to
4cca5dc
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
extra_tests/snippets/regression_closed_issues.py (1)
74-74: Replace mutable list attributes with plain booleans to resolve Ruff RUF012 violations.At lines 74 and 94,
called = [False]andfired = [False]trigger Ruff's RUF012 warning for mutable class attributes. These list wrappers are unnecessary; the attributes can be plain booleans with the same behavior.Proposed changes
class _Cls4861: - called = [False] + called = False def __del__(self): - _Cls4861.called[0] = True + _Cls4861.called = True -assert _Cls4861.called[0] +assert _Cls4861.called class _Dummy4863: - fired = [False] + fired = False def __del__(self): - if _Dummy4863.fired[0]: + if _Dummy4863.fired: return - _Dummy4863.fired[0] = True + _Dummy4863.fired = True type(self)() # this is the exact shape that used to panic -assert _Dummy4863.fired[0] +assert _Dummy4863.fired🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extra_tests/snippets/regression_closed_issues.py` at line 74, Replace the mutable list attributes used as flags (called = [False] and fired = [False]) with plain booleans (called = False, fired = False) and update all usages that currently mutate the list (e.g., called[0] = True or if called[0]:) to use direct boolean assignment and checks (called = True; if called:). Locate the flags by the identifiers called and fired in the file and modify any test helpers or closures that relied on the list indirection accordingly so behavior remains identical while removing the mutable-list wrapper.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@extra_tests/snippets/regression_closed_issues.py`:
- Line 74: Replace the mutable list attributes used as flags (called = [False]
and fired = [False]) with plain booleans (called = False, fired = False) and
update all usages that currently mutate the list (e.g., called[0] = True or if
called[0]:) to use direct boolean assignment and checks (called = True; if
called:). Locate the flags by the identifiers called and fired in the file and
modify any test helpers or closures that relied on the list indirection
accordingly so behavior remains identical while removing the mutable-list
wrapper.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: e80f7ff2-057f-48f0-98f1-456a174e46e5
📒 Files selected for processing (1)
extra_tests/snippets/regression_closed_issues.py
|
thanks for the correct it's wrong shape |
Summary
Six GitHub issues report crashes / panics that the upstream code base has since fixed, but whose issues were never linked by a closing PR and remain open years later. This PR adds a single snippet file that pins each minimal reproduction so future refactors cannot regress any of them without a visible test failure, and closes the open issues via the PR body.
Issues closed
starmap(i, zip(b, b))repeated ~100k times on an empty list — SIGSEGV[]cleanlyrepr(asyncio.Task)insideasyncio.run— Rust panicxml.etreeSubElements — segfault on cleanup__del__runs — null reference panic__del__fires; reassignment completes__del__callingtype(self)()—Result::unwrap on Errpanictype(self)()returns normally in a guarded__del__scrapscript.py(while/else + trailing dict literal)compile()accepts the sourceChanges
A single new file:
extra_tests/snippets/regression_closed_issues.py(+143 lines).Each section has a comment block referencing the original issue, explaining what the old crash was, and running the minimal repro. Where the original report used an unbounded
__del__-cascade or very large iteration count purely to surface the crash, the regression test uses the same shape with a bounded inner loop or one-shot guard — the bug class is exercised without depending on runtime cost at garbage-collection time.Verification
Both interpreters pass immediately — no test depends on RustPython-specific timing or GC traversal cost.
Scope
crates/is touched.Why bundle as one PR
Six issues from the same class ("pre-existing SIGSEGV / panic, fixed implicitly, never closed") in one PR keeps reviewer surface small (one file, all test-only) and makes the intent unambiguous — the PR is an issue-tracker cleanup, not a feature. Similar to #7625 which unmasked two
@unittest.expectedFailuremarkers in the same session that introduced the underlying fix.Summary by CodeRabbit
Closes #4317.
Closes #4859.
Closes #4860.
Closes #4861.
Closes #4863.
Closes #5154.