Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions extra_tests/snippets/regression_closed_issues.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
"""Regression tests for issues that were silently fixed but whose GitHub
issues remained open (no linking PR closed them). Each section below pins
the minimal reproduction from the original report, so future refactors
can't quietly re-introduce the bug.

Closing:
#4317 starmap over self-referential iteration crashed with SIGSEGV
#4859 repr() of an asyncio Task aborted with a Rust panic
#4860 deeply-nested XML tree segfaulted during refcount cleanup
#4861 reassigning a name after __del__ produced a null reference
#4863 __del__ calling type(self)() panicked instead of raising RecursionError
#5154 specific function body shape with `while/else` + trailing dict crashed
"""

import asyncio
import xml.etree.ElementTree as ET
from itertools import starmap

# --- #4317: starmap over zip of an empty list, repeated -------------------
#
# Original report: `b = []; for i in range(100000): b = starmap(i, zip(b, b));
# list(b)` segfaulted. The iterator was self-referential and the Rust stack
# 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

assert list(_b) == []


# --- #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

# repr() on it panicked. The call now returns a non-empty string.
async def _4859_coro(a, b):
return a + b


async def _4859_main():
task = asyncio.create_task(_4859_coro(1, b=1))
s = repr(task)
assert isinstance(s, str) and len(s) > 0
return await task


assert asyncio.run(_4859_main()) == 2


# --- #4860: deep XML tree cleanup ----------------------------------------
#
# Original report: building ~20000 nested XML SubElements then dropping
# references segfaulted during GC. Running the same pattern (smaller depth
# 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.

_node = _root
for _ in range(200):
_node = ET.SubElement(_node, "x")
_deep = _node # keep a separate reference to the deepest element
# Drop root first (the original trigger pattern), then the remaining refs.
del _root
del _node
del _deep


# --- #4861: reassignment of a name immediately after del ------------------
#
# Original report: reassigning a name held by an instance whose `__del__`
# runs during the reassignment produced a null reference panic. The bug
# was about the reassignment sequence, not about cascading destructors;
# the regression test exercises the same reassignment path but keeps the
# destructor trivial so the test doesn't depend on GC traversal time.
class _Cls4861:
called = [False]

def __del__(self):
_Cls4861.called[0] = True


_c = _Cls4861()
_c = range(10) # reassignment triggers __del__ on the original instance
del _c
assert _Cls4861.called[0]


# --- #4863: __del__ calling type(self)() ---------------------------------
#
# Original report: `class Dummy: def __del__(self): type(self)()` + `del d`
# panicked with `Result::unwrap() on an Err`. The regression exercises the
# `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.

fired = [False]

def __del__(self):
if _Dummy4863.fired[0]:
return
_Dummy4863.fired[0] = True
type(self)() # this is the exact shape that used to panic


_d = _Dummy4863()
del _d
assert _Dummy4863.fired[0]


# --- #5154: function body with `while/else` + trailing dict literal -------
#
# Original report (CReduced from scrapscript.py): a specific combination of
# `while/else` returning an undefined name followed by an unused dict
# literal crashed the compiler. The same source now compiles cleanly (we
# do not execute the function, since `x` is undefined — the point is that
# the code is accepted).
_src_5154 = """
class LeftParen:
pass


class RightParen:
pass


class Lexer:
def read_char(self):
return None

def read_one(self):
while self:
c = self.read_char()
break
else:
return x
{
"(": LeftParen,
")": RightParen,
}


def tokenize():
lexer = Lexer()
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.

Loading