Skip to content

Commit cbcd21a

Browse files
committed
remove recursion. Add make_text helper and DetachOnce
1 parent c2cb455 commit cbcd21a

1 file changed

Lines changed: 31 additions & 28 deletions

File tree

Lib/test/test_io/test_textio.py

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import weakref
1212
from collections import UserList
1313
from test import support
14-
from test.support import set_recursion_limit
1514
from test.support import os_helper, threading_helper
1615
from test.support.script_helper import assert_python_ok
1716
from .utils import CTestCase, PyTestCase
@@ -1563,21 +1562,35 @@ def closed(self):
15631562

15641563
def test_reentrant_detach_during_flush(self):
15651564
# gh-143008: Reentrant detach() during flush should not crash.
1566-
wrapper = None
15671565

1568-
class DetachOnFlush(self.BufferedRandom):
1566+
class DetachOnce(self.BufferedRandom):
1567+
wrapper = None
1568+
1569+
def detach_once(self):
1570+
original = self.wrapper
1571+
self.wrapper = None
1572+
if original is not None:
1573+
original.detach()
1574+
original.flush()
1575+
1576+
class DetachOnFlush(DetachOnce):
15691577
def flush(self):
1570-
if wrapper is not None:
1571-
wrapper.detach()
1572-
return super().flush()
1578+
self.detach_once()
15731579

1574-
class DetachOnWrite(self.BufferedRandom):
1580+
class DetachOnWrite(DetachOnce):
15751581
def write(self, b):
1576-
if wrapper is not None:
1577-
wrapper.detach()
1582+
self.detach_once()
15781583
return len(b)
15791584

1580-
# Used to crash; now will run out of stack.
1585+
# Separate reference for after detach_once.
1586+
wrapper = None
1587+
1588+
def make_text(buffer):
1589+
nonlocal wrapper
1590+
buffer.wrapper = self.TextIOWrapper(buffer, encoding='utf-8')
1591+
wrapper = buffer.wrapper
1592+
1593+
# Many calls could result in the same null self->buffer crash.
15811594
tests = [
15821595
('truncate', lambda: wrapper.truncate(0)),
15831596
('close', lambda: wrapper.close()),
@@ -1586,26 +1599,16 @@ def write(self, b):
15861599
('tell', lambda: wrapper.tell()),
15871600
('reconfigure', lambda: wrapper.reconfigure(line_buffering=True)),
15881601
]
1589-
for name, call in tests:
1590-
with self.subTest(name), set_recursion_limit(100):
1591-
buffer = DetachOnFlush(self.MockRawIO())
1592-
wrapper = self.TextIOWrapper(buffer, encoding='utf-8')
1593-
try:
1594-
self.assertRaises(RecursionError, call)
1595-
finally:
1596-
# Disarm before GC so finalization succeeds.
1597-
wrapper = None
1602+
for name, method in tests:
1603+
with self.subTest(name):
1604+
make_text(DetachOnFlush(self.MockRawIO()))
1605+
self.assertRaisesRegex(ValueError, "detached", method)
15981606

1599-
# Used to crash.
1607+
# Should not crash.
16001608
with self.subTest('read via writeflush'):
1601-
buffer = DetachOnWrite(self.MockRawIO())
1602-
wrapper = self.TextIOWrapper(buffer, encoding='utf-8')
1603-
try:
1604-
wrapper.write('x')
1605-
self.assertRaisesRegex(ValueError, "detached", wrapper.read)
1606-
finally:
1607-
# Disarm before GC so finalization succeeds.
1608-
wrapper = None
1609+
make_text(DetachOnWrite(self.MockRawIO()))
1610+
wrapper.write('x')
1611+
self.assertRaisesRegex(ValueError, "detached", wrapper.read)
16091612

16101613

16111614
class PyTextIOWrapperTest(TextIOWrapperTest, PyTestCase):

0 commit comments

Comments
 (0)