Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cf4c749

Browse files
committed
Issue #5734: BufferedRWPair was poorly tested and had several glaring bugs.
Patch by Brian Quinlan.
1 parent 561f360 commit cf4c749

4 files changed

Lines changed: 137 additions & 29 deletions

File tree

Lib/_pyio.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,9 @@ class BufferedReader(_BufferedIOMixin):
839839
def __init__(self, raw, buffer_size=DEFAULT_BUFFER_SIZE):
840840
"""Create a new buffered reader using the given readable raw IO object.
841841
"""
842-
raw._checkReadable()
842+
if not raw.readable():
843+
raise IOError('"raw" argument must be readable.')
844+
843845
_BufferedIOMixin.__init__(self, raw)
844846
if buffer_size <= 0:
845847
raise ValueError("invalid buffer size")
@@ -970,7 +972,9 @@ class BufferedWriter(_BufferedIOMixin):
970972

971973
def __init__(self, raw,
972974
buffer_size=DEFAULT_BUFFER_SIZE, max_buffer_size=None):
973-
raw._checkWritable()
975+
if not raw.writable():
976+
raise IOError('"raw" argument must be writable.')
977+
974978
_BufferedIOMixin.__init__(self, raw)
975979
if buffer_size <= 0:
976980
raise ValueError("invalid buffer size")
@@ -1076,8 +1080,13 @@ def __init__(self, reader, writer,
10761080
"""
10771081
if max_buffer_size is not None:
10781082
warnings.warn("max_buffer_size is deprecated", DeprecationWarning, 2)
1079-
reader._checkReadable()
1080-
writer._checkWritable()
1083+
1084+
if not reader.readable():
1085+
raise IOError('"reader" argument must be readable.')
1086+
1087+
if not writer.writable():
1088+
raise IOError('"writer" argument must be writable.')
1089+
10811090
self.reader = BufferedReader(reader, buffer_size)
10821091
self.writer = BufferedWriter(writer, buffer_size)
10831092

Lib/test/test_io.py

Lines changed: 97 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,13 +1051,11 @@ class PyBufferedWriterTest(BufferedWriterTest):
10511051

10521052
class BufferedRWPairTest(unittest.TestCase):
10531053

1054-
def test_basic(self):
1055-
r = self.MockRawIO(())
1056-
w = self.MockRawIO()
1057-
pair = self.tp(r, w)
1054+
def test_constructor(self):
1055+
pair = self.tp(self.MockRawIO(), self.MockRawIO())
10581056
self.assertFalse(pair.closed)
10591057

1060-
def test_max_buffer_size_deprecation(self):
1058+
def test_constructor_max_buffer_size_deprecation(self):
10611059
with support.check_warnings() as w:
10621060
warnings.simplefilter("always", DeprecationWarning)
10631061
self.tp(self.MockRawIO(), self.MockRawIO(), 8, 12)
@@ -1067,7 +1065,100 @@ def test_max_buffer_size_deprecation(self):
10671065
self.assertEqual(str(warning.message),
10681066
"max_buffer_size is deprecated")
10691067

1070-
# XXX More Tests
1068+
def test_constructor_with_not_readable(self):
1069+
class NotReadable(MockRawIO):
1070+
def readable(self):
1071+
return False
1072+
1073+
self.assertRaises(IOError, self.tp, NotReadable(), self.MockRawIO())
1074+
1075+
def test_constructor_with_not_writeable(self):
1076+
class NotWriteable(MockRawIO):
1077+
def writable(self):
1078+
return False
1079+
1080+
self.assertRaises(IOError, self.tp, self.MockRawIO(), NotWriteable())
1081+
1082+
def test_read(self):
1083+
pair = self.tp(self.BytesIO(b"abcdef"), self.MockRawIO())
1084+
1085+
self.assertEqual(pair.read(3), b"abc")
1086+
self.assertEqual(pair.read(1), b"d")
1087+
self.assertEqual(pair.read(), b"ef")
1088+
1089+
def test_read1(self):
1090+
# .read1() is delegated to the underlying reader object, so this test
1091+
# can be shallow.
1092+
pair = self.tp(self.BytesIO(b"abcdef"), self.MockRawIO())
1093+
1094+
self.assertEqual(pair.read1(3), b"abc")
1095+
1096+
def test_readinto(self):
1097+
pair = self.tp(self.BytesIO(b"abcdef"), self.MockRawIO())
1098+
1099+
data = bytearray(5)
1100+
self.assertEqual(pair.readinto(data), 5)
1101+
self.assertEqual(data, b"abcde")
1102+
1103+
def test_write(self):
1104+
w = self.MockRawIO()
1105+
pair = self.tp(self.MockRawIO(), w)
1106+
1107+
pair.write(b"abc")
1108+
pair.flush()
1109+
pair.write(b"def")
1110+
pair.flush()
1111+
self.assertEqual(w._write_stack, [b"abc", b"def"])
1112+
1113+
def test_peek(self):
1114+
pair = self.tp(self.BytesIO(b"abcdef"), self.MockRawIO())
1115+
1116+
self.assertTrue(pair.peek(3).startswith(b"abc"))
1117+
self.assertEqual(pair.read(3), b"abc")
1118+
1119+
def test_readable(self):
1120+
pair = self.tp(self.MockRawIO(), self.MockRawIO())
1121+
self.assertTrue(pair.readable())
1122+
1123+
def test_writeable(self):
1124+
pair = self.tp(self.MockRawIO(), self.MockRawIO())
1125+
self.assertTrue(pair.writable())
1126+
1127+
def test_seekable(self):
1128+
# BufferedRWPairs are never seekable, even if their readers and writers
1129+
# are.
1130+
pair = self.tp(self.MockRawIO(), self.MockRawIO())
1131+
self.assertFalse(pair.seekable())
1132+
1133+
# .flush() is delegated to the underlying writer object and has been
1134+
# tested in the test_write method.
1135+
1136+
def test_close_and_closed(self):
1137+
pair = self.tp(self.MockRawIO(), self.MockRawIO())
1138+
self.assertFalse(pair.closed)
1139+
pair.close()
1140+
self.assertTrue(pair.closed)
1141+
1142+
def test_isatty(self):
1143+
class SelectableIsAtty(MockRawIO):
1144+
def __init__(self, isatty):
1145+
MockRawIO.__init__(self)
1146+
self._isatty = isatty
1147+
1148+
def isatty(self):
1149+
return self._isatty
1150+
1151+
pair = self.tp(SelectableIsAtty(False), SelectableIsAtty(False))
1152+
self.assertFalse(pair.isatty())
1153+
1154+
pair = self.tp(SelectableIsAtty(True), SelectableIsAtty(False))
1155+
self.assertTrue(pair.isatty())
1156+
1157+
pair = self.tp(SelectableIsAtty(False), SelectableIsAtty(True))
1158+
self.assertTrue(pair.isatty())
1159+
1160+
pair = self.tp(SelectableIsAtty(True), SelectableIsAtty(True))
1161+
self.assertTrue(pair.isatty())
10711162

10721163
class CBufferedRWPairTest(BufferedRWPairTest):
10731164
tp = io.BufferedRWPair

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ Core and Builtins
7272
Library
7373
-------
7474

75+
- Issue #5734: BufferedRWPair was poorly tested and had several glaring
76+
bugs. Patch by Brian Quinlan.
77+
7578
- Issue #1161031: fix readwrite select flag handling: POLLPRI now
7679
results in a handle_expt_event call, not handle_read_event, and POLLERR
7780
and POLLNVAL now call handle_close, not handle_expt_event. Also,

Modules/_io/bufferedio.c

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1846,7 +1846,7 @@ typedef struct {
18461846

18471847
static int
18481848
BufferedRWPair_init(BufferedRWPairObject *self, PyObject *args,
1849-
PyObject *kwds)
1849+
PyObject *kwds)
18501850
{
18511851
PyObject *reader, *writer;
18521852
Py_ssize_t buffer_size = DEFAULT_BUFFER_SIZE;
@@ -1865,29 +1865,18 @@ BufferedRWPair_init(BufferedRWPairObject *self, PyObject *args,
18651865
if (_PyIOBase_checkWritable(writer, Py_True) == NULL)
18661866
return -1;
18671867

1868-
args = Py_BuildValue("(n)", buffer_size);
1869-
if (args == NULL) {
1870-
Py_CLEAR(self->reader);
1871-
return -1;
1872-
}
1873-
self->reader = (BufferedObject *)PyType_GenericNew(
1874-
&PyBufferedReader_Type, args, NULL);
1875-
Py_DECREF(args);
1868+
self->reader = (BufferedObject *) PyObject_CallFunction(
1869+
(PyObject *) &PyBufferedReader_Type, "On", reader, buffer_size);
18761870
if (self->reader == NULL)
18771871
return -1;
18781872

1879-
args = Py_BuildValue("(n)", buffer_size);
1880-
if (args == NULL) {
1881-
Py_CLEAR(self->reader);
1882-
return -1;
1883-
}
1884-
self->writer = (BufferedObject *)PyType_GenericNew(
1885-
&PyBufferedWriter_Type, args, NULL);
1886-
Py_DECREF(args);
1873+
self->writer = (BufferedObject *) PyObject_CallFunction(
1874+
(PyObject *) &PyBufferedWriter_Type, "On", writer, buffer_size);
18871875
if (self->writer == NULL) {
18881876
Py_CLEAR(self->reader);
18891877
return -1;
18901878
}
1879+
18911880
return 0;
18921881
}
18931882

@@ -1951,6 +1940,12 @@ BufferedRWPair_read1(BufferedRWPairObject *self, PyObject *args)
19511940
return _forward_call(self->reader, "read1", args);
19521941
}
19531942

1943+
static PyObject *
1944+
BufferedRWPair_readinto(BufferedRWPairObject *self, PyObject *args)
1945+
{
1946+
return _forward_call(self->reader, "readinto", args);
1947+
}
1948+
19541949
static PyObject *
19551950
BufferedRWPair_write(BufferedRWPairObject *self, PyObject *args)
19561951
{
@@ -2000,12 +1995,17 @@ BufferedRWPair_isatty(BufferedRWPairObject *self, PyObject *args)
20001995
return _forward_call(self->reader, "isatty", args);
20011996
}
20021997

1998+
static PyObject *
1999+
BufferedRWPair_closed_get(BufferedRWPairObject *self, void *context)
2000+
{
2001+
return PyObject_GetAttr((PyObject *) self->writer, _PyIO_str_closed);
2002+
}
20032003

20042004
static PyMethodDef BufferedRWPair_methods[] = {
20052005
{"read", (PyCFunction)BufferedRWPair_read, METH_VARARGS},
20062006
{"peek", (PyCFunction)BufferedRWPair_peek, METH_VARARGS},
20072007
{"read1", (PyCFunction)BufferedRWPair_read1, METH_VARARGS},
2008-
{"readinto", (PyCFunction)Buffered_readinto, METH_VARARGS},
2008+
{"readinto", (PyCFunction)BufferedRWPair_readinto, METH_VARARGS},
20092009

20102010
{"write", (PyCFunction)BufferedRWPair_write, METH_VARARGS},
20112011
{"flush", (PyCFunction)BufferedRWPair_flush, METH_NOARGS},
@@ -2019,6 +2019,11 @@ static PyMethodDef BufferedRWPair_methods[] = {
20192019
{NULL, NULL}
20202020
};
20212021

2022+
static PyGetSetDef BufferedRWPair_getset[] = {
2023+
{"closed", (getter)BufferedRWPair_closed_get, NULL, NULL},
2024+
{0}
2025+
};
2026+
20222027
PyTypeObject PyBufferedRWPair_Type = {
20232028
PyVarObject_HEAD_INIT(NULL, 0)
20242029
"_io.BufferedRWPair", /*tp_name*/
@@ -2050,7 +2055,7 @@ PyTypeObject PyBufferedRWPair_Type = {
20502055
0, /* tp_iternext */
20512056
BufferedRWPair_methods, /* tp_methods */
20522057
0, /* tp_members */
2053-
0, /* tp_getset */
2058+
BufferedRWPair_getset, /* tp_getset */
20542059
0, /* tp_base */
20552060
0, /* tp_dict */
20562061
0, /* tp_descr_get */

0 commit comments

Comments
 (0)