Issue #23214: Implement optional BufferedReader, BytesIO read1() argument

This commit is contained in:
Martin Panter 2016-10-20 23:48:14 +00:00
parent ea8762cae6
commit ccb2c0e310
9 changed files with 81 additions and 39 deletions

View File

@ -477,7 +477,7 @@ I/O Base Classes
A :exc:`BlockingIOError` is raised if the underlying raw stream is in
non blocking-mode, and has no data available at the moment.
.. method:: read1(size=-1)
.. method:: read1([size])
Read and return up to *size* bytes, with at most one call to the
underlying raw stream's :meth:`~RawIOBase.read` (or
@ -485,6 +485,9 @@ I/O Base Classes
implementing your own buffering on top of a :class:`BufferedIOBase`
object.
If *size* is 1 (the default), an arbitrary number of bytes are
returned (more than zero unless EOF is reached).
.. method:: readinto(b)
Read bytes into a pre-allocated, writable
@ -628,13 +631,16 @@ than raw I/O does.
Return :class:`bytes` containing the entire contents of the buffer.
.. method:: read1()
.. method:: read1([size])
In :class:`BytesIO`, this is the same as :meth:`read`.
In :class:`BytesIO`, this is the same as :meth:`~BufferedIOBase.read`.
.. method:: readinto1()
.. versionchanged:: 3.7
The *size* argument is now optional.
In :class:`BytesIO`, this is the same as :meth:`readinto`.
.. method:: readinto1(b)
In :class:`BytesIO`, this is the same as :meth:`~BufferedIOBase.readinto`.
.. versionadded:: 3.5
@ -664,12 +670,15 @@ than raw I/O does.
Read and return *size* bytes, or if *size* is not given or negative, until
EOF or if the read call would block in non-blocking mode.
.. method:: read1(size)
.. method:: read1([size])
Read and return up to *size* bytes with only one call on the raw stream.
If at least one byte is buffered, only buffered bytes are returned.
Otherwise, one raw stream read call is made.
.. versionchanged:: 3.7
The *size* argument is now optional.
.. class:: BufferedWriter(raw, buffer_size=DEFAULT_BUFFER_SIZE)

View File

@ -635,7 +635,7 @@ class BufferedIOBase(IOBase):
implementation, but wrap one.
"""
def read(self, size=None):
def read(self, size=-1):
"""Read and return up to size bytes, where size is an int.
If the argument is omitted, None, or negative, reads and
@ -655,7 +655,7 @@ class BufferedIOBase(IOBase):
"""
self._unsupported("read")
def read1(self, size=None):
def read1(self, size=-1):
"""Read up to size bytes with at most one read() system call,
where size is an int.
"""
@ -863,7 +863,7 @@ class BytesIO(BufferedIOBase):
self._buffer.clear()
super().close()
def read(self, size=None):
def read(self, size=-1):
if self.closed:
raise ValueError("read from closed file")
if size is None:
@ -877,7 +877,7 @@ class BytesIO(BufferedIOBase):
self._pos = newpos
return bytes(b)
def read1(self, size):
def read1(self, size=-1):
"""This is the same as read.
"""
return self.read(size)
@ -1073,12 +1073,12 @@ class BufferedReader(_BufferedIOMixin):
self._read_pos = 0
return self._read_buf[self._read_pos:]
def read1(self, size):
def read1(self, size=-1):
"""Reads up to size bytes, with at most one read() system call."""
# Returns up to size bytes. If at least one byte is buffered, we
# only return buffered bytes. Otherwise, we do one raw read.
if size < 0:
raise ValueError("number of bytes to read must be positive")
size = self.buffer_size
if size == 0:
return b""
with self._read_lock:
@ -1270,7 +1270,7 @@ class BufferedRWPair(BufferedIOBase):
self.reader = BufferedReader(reader, buffer_size)
self.writer = BufferedWriter(writer, buffer_size)
def read(self, size=None):
def read(self, size=-1):
if size is None:
size = -1
return self.reader.read(size)
@ -1284,7 +1284,7 @@ class BufferedRWPair(BufferedIOBase):
def peek(self, size=0):
return self.reader.peek(size)
def read1(self, size):
def read1(self, size=-1):
return self.reader.read1(size)
def readinto1(self, b):
@ -1370,7 +1370,7 @@ class BufferedRandom(BufferedWriter, BufferedReader):
self.flush()
return BufferedReader.peek(self, size)
def read1(self, size):
def read1(self, size=-1):
self.flush()
return BufferedReader.read1(self, size)

View File

@ -1146,6 +1146,7 @@ class BufferedReaderTest(unittest.TestCase, CommonBufferedTests):
self.assertEqual(b"a", bufio.read(1))
self.assertEqual(b"b", bufio.read1(1))
self.assertEqual(rawio._reads, 1)
self.assertEqual(b"", bufio.read1(0))
self.assertEqual(b"c", bufio.read1(100))
self.assertEqual(rawio._reads, 1)
self.assertEqual(b"d", bufio.read1(100))
@ -1154,8 +1155,17 @@ class BufferedReaderTest(unittest.TestCase, CommonBufferedTests):
self.assertEqual(rawio._reads, 3)
self.assertEqual(b"", bufio.read1(100))
self.assertEqual(rawio._reads, 4)
# Invalid args
self.assertRaises(ValueError, bufio.read1, -1)
def test_read1_arbitrary(self):
rawio = self.MockRawIO((b"abc", b"d", b"efg"))
bufio = self.tp(rawio)
self.assertEqual(b"a", bufio.read(1))
self.assertEqual(b"bc", bufio.read1())
self.assertEqual(b"d", bufio.read1())
self.assertEqual(b"efg", bufio.read1(-1))
self.assertEqual(rawio._reads, 3)
self.assertEqual(b"", bufio.read1())
self.assertEqual(rawio._reads, 4)
def test_readinto(self):
rawio = self.MockRawIO((b"abc", b"d", b"efg"))
@ -1806,6 +1816,7 @@ class BufferedRWPairTest(unittest.TestCase):
pair = self.tp(self.BytesIO(b"abcdef"), self.MockRawIO())
self.assertEqual(pair.read1(3), b"abc")
self.assertEqual(pair.read1(), b"def")
def test_readinto(self):
for method in ("readinto", "readinto1"):
@ -3467,6 +3478,7 @@ class MiscIOTest(unittest.TestCase):
self.assertRaises(ValueError, f.read)
if hasattr(f, "read1"):
self.assertRaises(ValueError, f.read1, 1024)
self.assertRaises(ValueError, f.read1)
if hasattr(f, "readall"):
self.assertRaises(ValueError, f.readall)
if hasattr(f, "readinto"):

View File

@ -437,10 +437,8 @@ class PyBytesIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase):
def test_read1(self):
buf = self.buftype("1234567890")
memio = self.ioclass(buf)
self.assertRaises(TypeError, memio.read1)
self.assertEqual(memio.read(), buf)
self.assertEqual(self.ioclass(buf).read1(), buf)
self.assertEqual(self.ioclass(buf).read1(-1), buf)
def test_readinto(self):
buf = self.buftype("1234567890")

View File

@ -88,6 +88,10 @@ Core and Builtins
Library
-------
- Issue #23214: In the "io" module, the argument to BufferedReader and
BytesIO's read1() methods is now optional and can be -1, matching the
BufferedIOBase specification.
- Issue #28480: Fix error building socket module when multithreading is
disabled.

View File

@ -904,7 +904,7 @@ _io__Buffered_read_impl(buffered *self, Py_ssize_t n)
CHECK_INITIALIZED(self)
if (n < -1) {
PyErr_SetString(PyExc_ValueError,
"read length must be positive or -1");
"read length must be non-negative or -1");
return NULL;
}
@ -932,22 +932,20 @@ _io__Buffered_read_impl(buffered *self, Py_ssize_t n)
/*[clinic input]
_io._Buffered.read1
size as n: Py_ssize_t
size as n: Py_ssize_t = -1
/
[clinic start generated code]*/
static PyObject *
_io__Buffered_read1_impl(buffered *self, Py_ssize_t n)
/*[clinic end generated code: output=bcc4fb4e54d103a3 input=8d2869c18b983184]*/
/*[clinic end generated code: output=bcc4fb4e54d103a3 input=7d22de9630b61774]*/
{
Py_ssize_t have, r;
PyObject *res = NULL;
CHECK_INITIALIZED(self)
if (n < 0) {
PyErr_SetString(PyExc_ValueError,
"read length must be positive");
return NULL;
n = self->buffer_size;
}
CHECK_CLOSED(self, "read of closed file")

View File

@ -420,7 +420,7 @@ _io_BytesIO_read_impl(bytesio *self, PyObject *arg)
/*[clinic input]
_io.BytesIO.read1
size: object
size: object(c_default="Py_None") = -1
/
Read at most size bytes, returned as a bytes object.
@ -430,8 +430,8 @@ Return an empty bytes object at EOF.
[clinic start generated code]*/
static PyObject *
_io_BytesIO_read1(bytesio *self, PyObject *size)
/*[clinic end generated code: output=16021f5d0ac3d4e2 input=d4f40bb8f2f99418]*/
_io_BytesIO_read1_impl(bytesio *self, PyObject *size)
/*[clinic end generated code: output=a60d80c84c81a6b8 input=0951874bafee8e80]*/
{
return _io_BytesIO_read_impl(self, size);
}

View File

@ -140,23 +140,24 @@ exit:
}
PyDoc_STRVAR(_io__Buffered_read1__doc__,
"read1($self, size, /)\n"
"read1($self, size=-1, /)\n"
"--\n"
"\n");
#define _IO__BUFFERED_READ1_METHODDEF \
{"read1", (PyCFunction)_io__Buffered_read1, METH_O, _io__Buffered_read1__doc__},
{"read1", (PyCFunction)_io__Buffered_read1, METH_VARARGS, _io__Buffered_read1__doc__},
static PyObject *
_io__Buffered_read1_impl(buffered *self, Py_ssize_t n);
static PyObject *
_io__Buffered_read1(buffered *self, PyObject *arg)
_io__Buffered_read1(buffered *self, PyObject *args)
{
PyObject *return_value = NULL;
Py_ssize_t n;
Py_ssize_t n = -1;
if (!PyArg_Parse(arg, "n:read1", &n)) {
if (!PyArg_ParseTuple(args, "|n:read1",
&n)) {
goto exit;
}
return_value = _io__Buffered_read1_impl(self, n);
@ -475,4 +476,4 @@ _io_BufferedRandom___init__(PyObject *self, PyObject *args, PyObject *kwargs)
exit:
return return_value;
}
/*[clinic end generated code: output=a956f394ecde4cf9 input=a9049054013a1b77]*/
/*[clinic end generated code: output=490c97bfcfd92c51 input=a9049054013a1b77]*/

View File

@ -181,7 +181,7 @@ exit:
}
PyDoc_STRVAR(_io_BytesIO_read1__doc__,
"read1($self, size, /)\n"
"read1($self, size=-1, /)\n"
"--\n"
"\n"
"Read at most size bytes, returned as a bytes object.\n"
@ -190,7 +190,27 @@ PyDoc_STRVAR(_io_BytesIO_read1__doc__,
"Return an empty bytes object at EOF.");
#define _IO_BYTESIO_READ1_METHODDEF \
{"read1", (PyCFunction)_io_BytesIO_read1, METH_O, _io_BytesIO_read1__doc__},
{"read1", (PyCFunction)_io_BytesIO_read1, METH_VARARGS, _io_BytesIO_read1__doc__},
static PyObject *
_io_BytesIO_read1_impl(bytesio *self, PyObject *size);
static PyObject *
_io_BytesIO_read1(bytesio *self, PyObject *args)
{
PyObject *return_value = NULL;
PyObject *size = Py_None;
if (!PyArg_UnpackTuple(args, "read1",
0, 1,
&size)) {
goto exit;
}
return_value = _io_BytesIO_read1_impl(self, size);
exit:
return return_value;
}
PyDoc_STRVAR(_io_BytesIO_readline__doc__,
"readline($self, size=None, /)\n"
@ -428,4 +448,4 @@ _io_BytesIO___init__(PyObject *self, PyObject *args, PyObject *kwargs)
exit:
return return_value;
}
/*[clinic end generated code: output=6382e8eb578eea64 input=a9049054013a1b77]*/
/*[clinic end generated code: output=8f469431da1b3857 input=a9049054013a1b77]*/