Skip to content

Commit 65d7dbb

Browse files
committed
fix: apply some suggestion
Signed-off-by: yihong0618 <zouzou0208@gmail.com>
1 parent a96757b commit 65d7dbb

File tree

2 files changed

+57
-40
lines changed

2 files changed

+57
-40
lines changed

Lib/test/test_io/test_textio.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,25 +1564,19 @@ def test_reentrant_detach_during_flush(self):
15641564
# gh-143008: Reentrant detach() during flush should raise RuntimeError
15651565
# instead of crashing.
15661566
wrapper = None
1567-
1568-
class BadRaw(self.RawIOBase):
1569-
def write(self, b): return len(b)
1570-
def read(self, n=-1): return b''
1571-
def readable(self): return True
1572-
def writable(self): return True
1573-
def seekable(self): return True
1574-
def seek(self, pos, whence=0): return 0
1575-
def tell(self): return 0
1567+
wrapper_ref = None
15761568

15771569
class EvilBuffer(self.BufferedRandom):
15781570
detach_on_write = False
15791571

15801572
def flush(self):
1573+
wrapper = wrapper_ref() if wrapper_ref is not None else None
15811574
if wrapper is not None and not self.detach_on_write:
15821575
wrapper.detach()
15831576
return super().flush()
15841577

15851578
def write(self, b):
1579+
wrapper = wrapper_ref() if wrapper_ref is not None else None
15861580
if wrapper is not None and self.detach_on_write:
15871581
wrapper.detach()
15881582
return len(b)
@@ -1597,16 +1591,20 @@ def write(self, b):
15971591
]
15981592
for name, method in tests:
15991593
with self.subTest(name):
1600-
wrapper = self.TextIOWrapper(EvilBuffer(BadRaw()), encoding='utf-8')
1594+
wrapper = self.TextIOWrapper(EvilBuffer(self.MockRawIO()), encoding='utf-8')
1595+
wrapper_ref = weakref.ref(wrapper)
16011596
self.assertRaisesRegex(RuntimeError, "reentrant", method)
1602-
wrapper = None
1597+
wrapper_ref = None
1598+
del wrapper
16031599

16041600
with self.subTest('read via writeflush'):
16051601
EvilBuffer.detach_on_write = True
1606-
wrapper = self.TextIOWrapper(EvilBuffer(BadRaw()), encoding='utf-8')
1602+
wrapper = self.TextIOWrapper(EvilBuffer(self.MockRawIO()), encoding='utf-8')
1603+
wrapper_ref = weakref.ref(wrapper)
16071604
wrapper.write('x')
16081605
self.assertRaisesRegex(RuntimeError, "reentrant", wrapper.read)
1609-
wrapper = None
1606+
wrapper_ref = None
1607+
del wrapper
16101608

16111609

16121610
class PyTextIOWrapperTest(TextIOWrapperTest, PyTestCase):

Modules/_io/textio.c

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,6 @@ struct textio
667667
PyObject_HEAD
668668
int ok; /* initialized? */
669669
int detached;
670-
int flushing; /* prevent reentrant detach during flush */
671670
Py_ssize_t chunk_size;
672671
PyObject *buffer;
673672
PyObject *encoding;
@@ -726,16 +725,6 @@ struct textio
726725

727726
#define textio_CAST(op) ((textio *)(op))
728727

729-
/* gh-143007 need to check for reentrant flush */
730-
static inline int
731-
_textiowrapper_flush(textio *self)
732-
{
733-
self->flushing = 1;
734-
int result = _PyFile_Flush((PyObject *)self);
735-
self->flushing = 0;
736-
return result;
737-
}
738-
739728
static void
740729
textiowrapper_set_decoded_chars(textio *self, PyObject *chars);
741730

@@ -905,6 +894,11 @@ _textiowrapper_set_decoder(textio *self, PyObject *codec_info,
905894
PyObject *res;
906895
int r;
907896

897+
if (self->detached > 0) {
898+
PyErr_SetString(PyExc_ValueError,
899+
"underlying buffer has been detached");
900+
return -1;
901+
}
908902
res = PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(readable));
909903
if (res == NULL)
910904
return -1;
@@ -961,6 +955,11 @@ _textiowrapper_set_encoder(textio *self, PyObject *codec_info,
961955
PyObject *res;
962956
int r;
963957

958+
if (self->detached > 0) {
959+
PyErr_SetString(PyExc_ValueError,
960+
"underlying buffer has been detached");
961+
return -1;
962+
}
964963
res = PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(writable));
965964
if (res == NULL)
966965
return -1;
@@ -1007,6 +1006,11 @@ _textiowrapper_fix_encoder_state(textio *self)
10071006

10081007
self->encoding_start_of_stream = 1;
10091008

1009+
if (self->detached > 0) {
1010+
PyErr_SetString(PyExc_ValueError,
1011+
"underlying buffer has been detached");
1012+
return -1;
1013+
}
10101014
PyObject *cookieObj = PyObject_CallMethodNoArgs(
10111015
self->buffer, &_Py_ID(tell));
10121016
if (cookieObj == NULL) {
@@ -1119,7 +1123,6 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer,
11191123

11201124
self->ok = 0;
11211125
self->detached = 0;
1122-
self->flushing = 0;
11231126

11241127
if (encoding == NULL) {
11251128
PyInterpreterState *interp = _PyInterpreterState_GET();
@@ -1434,7 +1437,7 @@ _io_TextIOWrapper_reconfigure_impl(textio *self, PyObject *encoding,
14341437
return NULL;
14351438
}
14361439

1437-
if (_textiowrapper_flush(self) < 0) {
1440+
if (_PyFile_Flush((PyObject *)self) < 0) {
14381441
return NULL;
14391442
}
14401443
self->b2cratio = 0;
@@ -1548,7 +1551,7 @@ _io_TextIOWrapper_closed_get_impl(textio *self);
15481551

15491552
#define CHECK_ATTACHED(self) \
15501553
CHECK_INITIALIZED(self); \
1551-
if (self->detached) { \
1554+
if (self->detached > 0) { \
15521555
PyErr_SetString(PyExc_ValueError, \
15531556
"underlying buffer has been detached"); \
15541557
return NULL; \
@@ -1559,13 +1562,12 @@ _io_TextIOWrapper_closed_get_impl(textio *self);
15591562
PyErr_SetString(PyExc_ValueError, \
15601563
"I/O operation on uninitialized object"); \
15611564
return -1; \
1562-
} else if (self->detached) { \
1565+
} else if (self->detached > 0) { \
15631566
PyErr_SetString(PyExc_ValueError, \
15641567
"underlying buffer has been detached"); \
15651568
return -1; \
15661569
}
15671570

1568-
15691571
/*[clinic input]
15701572
@critical_section
15711573
_io.TextIOWrapper.detach
@@ -1577,12 +1579,18 @@ _io_TextIOWrapper_detach_impl(textio *self)
15771579
{
15781580
PyObject *buffer;
15791581
CHECK_ATTACHED(self);
1580-
if (self->flushing) {
1582+
if (self->detached < 0) {
15811583
PyErr_SetString(PyExc_RuntimeError,
15821584
"reentrant call to detach() is not allowed");
15831585
return NULL;
15841586
}
1585-
if (_textiowrapper_flush(self) < 0) {
1587+
int entered = (self->detached == 0);
1588+
if (entered)
1589+
self->detached = -1;
1590+
if (_PyFile_Flush((PyObject *)self) < 0) {
1591+
if (entered && self->detached < 0) {
1592+
self->detached = 0;
1593+
}
15861594
return NULL;
15871595
}
15881596
buffer = self->buffer;
@@ -1653,11 +1661,15 @@ _textiowrapper_writeflush(textio *self)
16531661
Py_DECREF(pending);
16541662

16551663
PyObject *ret;
1656-
self->flushing = 1;
1664+
CHECK_ATTACHED_INT(self);
1665+
int entered = (self->detached == 0);
1666+
if (entered)
1667+
self->detached = -1;
16571668
do {
16581669
ret = PyObject_CallMethodOneArg(self->buffer, &_Py_ID(write), b);
16591670
} while (ret == NULL && _PyIO_trap_eintr());
1660-
self->flushing = 0;
1671+
if (entered && self->detached < 0)
1672+
self->detached = 0;
16611673
Py_DECREF(b);
16621674
// NOTE: We cleared buffer but we don't know how many bytes are actually written
16631675
// when an error occurred.
@@ -2602,7 +2614,7 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence)
26022614
goto fail;
26032615
}
26042616

2605-
if (_textiowrapper_flush(self) < 0) {
2617+
if (_PyFile_Flush((PyObject *)self) < 0) {
26062618
goto fail;
26072619
}
26082620

@@ -2649,7 +2661,7 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence)
26492661
goto fail;
26502662
}
26512663

2652-
if (_textiowrapper_flush(self) < 0) {
2664+
if (_PyFile_Flush((PyObject *)self) < 0) {
26532665
goto fail;
26542666
}
26552667

@@ -2776,7 +2788,7 @@ _io_TextIOWrapper_tell_impl(textio *self)
27762788

27772789
if (_textiowrapper_writeflush(self) < 0)
27782790
return NULL;
2779-
if (_textiowrapper_flush(self) < 0) {
2791+
if (_PyFile_Flush((PyObject *)self) < 0) {
27802792
goto fail;
27812793
}
27822794

@@ -2986,7 +2998,7 @@ _io_TextIOWrapper_truncate_impl(textio *self, PyObject *pos)
29862998
{
29872999
CHECK_ATTACHED(self)
29883000

2989-
if (_textiowrapper_flush(self) < 0) {
3001+
if (_PyFile_Flush((PyObject *)self) < 0) {
29903002
return NULL;
29913003
}
29923004

@@ -3142,7 +3154,14 @@ _io_TextIOWrapper_flush_impl(textio *self)
31423154
self->telling = self->seekable;
31433155
if (_textiowrapper_writeflush(self) < 0)
31443156
return NULL;
3145-
return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(flush));
3157+
int entered = (self->detached == 0);
3158+
if (entered) {
3159+
self->detached = -1;
3160+
}
3161+
PyObject *ret = PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(flush));
3162+
if (entered && self->detached < 0)
3163+
self->detached = 0;
3164+
return ret;
31463165
}
31473166

31483167
/*[clinic input]
@@ -3169,7 +3188,7 @@ _io_TextIOWrapper_close_impl(textio *self)
31693188
if (r > 0) {
31703189
Py_RETURN_NONE; /* stream already closed */
31713190
}
3172-
if (self->detached) {
3191+
if (self->detached > 0) {
31733192
Py_RETURN_NONE; /* gh-142594 null pointer issue */
31743193
}
31753194
else {
@@ -3184,7 +3203,7 @@ _io_TextIOWrapper_close_impl(textio *self)
31843203
PyErr_Clear();
31853204
}
31863205
}
3187-
if (_textiowrapper_flush(self) < 0) {
3206+
if (_PyFile_Flush((PyObject *)self) < 0) {
31883207
exc = PyErr_GetRaisedException();
31893208
}
31903209

0 commit comments

Comments
 (0)