Skip to content

Add regression tests for six silently-fixed issues (close #4317 #4859 #4860 #4861 #4863 #5154)#7635

Closed
changjoon-park wants to merge 1 commit intoRustPython:mainfrom
changjoon-park:close-silently-fixed-issues
Closed

Add regression tests for six silently-fixed issues (close #4317 #4859 #4860 #4861 #4863 #5154)#7635
changjoon-park wants to merge 1 commit intoRustPython:mainfrom
changjoon-park:close-silently-fixed-issues

Conversation

@changjoon-park
Copy link
Copy Markdown
Contributor

@changjoon-park changjoon-park commented Apr 20, 2026

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

# Original symptom Current status
#4317 starmap(i, zip(b, b)) repeated ~100k times on an empty list — SIGSEGV Returns [] cleanly
#4859 repr(asyncio.Task) inside asyncio.run — Rust panic Returns a non-empty string
#4860 Deeply-nested xml.etree SubElements — segfault on cleanup Tree is freed normally
#4861 Reassigning a name while __del__ runs — null reference panic __del__ fires; reassignment completes
#4863 __del__ calling type(self)()Result::unwrap on Err panic type(self)() returns normally in a guarded __del__
#5154 Compiler crash on reduced scrapscript.py (while/else + trailing dict literal) compile() accepts the source

Changes

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

$ ./target/release/rustpython extra_tests/snippets/regression_closed_issues.py
$ echo $?
0

$ python3 extra_tests/snippets/regression_closed_issues.py
$ echo $?
0

$ cargo fmt --check

Both interpreters pass immediately — no test depends on RustPython-specific timing or GC traversal cost.

Scope

  • In: regression coverage that locks in already-working behavior.
  • Out: any functional code change. The file is test-only; no crate under 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.expectedFailure markers in the same session that introduced the underlying fix.

Summary by CodeRabbit

  • Tests
    • Added a regression test suite that re-validates several previously fixed issues: self-referential iterator termination in corner cases; asyncio task creation, representation and awaited results; robust cleanup of deeply nested XML element trees during teardown; two destructor/finalization regression scenarios (flag-setting and guarded re-entry); and a compiler-stability check for a previously problematic source pattern.

Closes #4317.
Closes #4859.
Closes #4860.
Closes #4861.
Closes #4863.
Closes #5154.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 20, 2026

📝 Walkthrough

Walkthrough

Add 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

Cohort / File(s) Summary
Regression Test Snippets
extra_tests/snippets/regression_closed_issues.py
New file containing six standalone regression cases: (1) repeated starmap/zip self-referential iteration materializes to an empty list, (2) asyncio.create_task repr and awaited result, (3) deep xml.etree.ElementTree nesting and del to exercise teardown, (4) destructor that sets a flag in __del__ and verifies it, (5) guarded __del__ calling type(self)() and asserting guard executed, (6) compile(..., "exec") on a while/else + trailing dict literal source succeeds.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 I hopped through tests and chased each bug,
starmap loops and tasks gave me a tug,
XML roots I plucked with care,
destructors sprang surprises there,
compile waved — all fixed with a shrug.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically describes the main change: adding regression tests for six silently-fixed issues with clear issue references.
Linked Issues check ✅ Passed All six linked issues (#4317, #4859, #4860, #4861, #4863, #5154) have corresponding test cases in the new file covering their crash scenarios.
Out of Scope Changes check ✅ Passed The PR adds only a single regression test file with no modifications to crates or core functionality, remaining squarely within scope.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Copy link
Copy Markdown
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: 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.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
 
 
 _d = _Dummy4863()
 del _d
-assert _Dummy4863.fired[0]
+assert _Dummy4863.fired

Also 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

📥 Commits

Reviewing files that changed from the base of the PR and between b18b71b and 0551354.

📒 Files selected for processing (1)
  • extra_tests/snippets/regression_closed_issues.py

Comment thread extra_tests/snippets/regression_closed_issues.py Outdated
@changjoon-park changjoon-park force-pushed the close-silently-fixed-issues branch from 74692eb to 8c61747 Compare April 20, 2026 12:52
Copy link
Copy Markdown
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.

🧹 Nitpick comments (2)
extra_tests/snippets/regression_closed_issues.py (2)

25-28: Suppress intentional ruff violations with # noqa to keep lint clean.

This section faithfully reproduces the original #4317 report, so the ruff findings (RUF058 starmap-on-zip, B905 missing strict=) are deliberate — the self-referential starmap(_i, zip(_b, _b)) shape is the bug trigger and must not be rewritten to map/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 # noqa codes 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 flag
 class _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, triggering PyObjectRef::drop() which synchronously invokes drop_slow_inner() and __del__ before the assignment completes. The assertion at line 84 passes for the correct reason—finalization occurs during reassignment, not deferred to del _c or 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

📥 Commits

Reviewing files that changed from the base of the PR and between 74692eb and 8c61747.

📒 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))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.
@changjoon-park changjoon-park force-pushed the close-silently-fixed-issues branch from 8c61747 to 4cca5dc Compare April 20, 2026 14:05
Copy link
Copy Markdown
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.

🧹 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] and fired = [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

📥 Commits

Reviewing files that changed from the base of the PR and between 8c61747 and 4cca5dc.

📒 Files selected for processing (1)
  • extra_tests/snippets/regression_closed_issues.py

@changjoon-park
Copy link
Copy Markdown
Contributor Author

thanks for the correct it's wrong shape
i'm closing this and will audit against Lib/test before any targeted follow-ups :)

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

Labels

None yet

Projects

None yet

2 participants