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 @@ def read(self, size=None):
"""
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 @@ def close(self):
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 @@ def read(self, size=None):
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 @@ def _peek_unlocked(self, n=0):
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 @@ def __init__(self, reader, writer, buffer_size=DEFAULT_BUFFER_SIZE):
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 @@ def write(self, b):
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 @@ def peek(self, size=0):
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 @@ def test_read1(self):
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 @@ def test_read1(self):
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 @@ def test_read1(self):
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 @@ def test_io_after_close(self):
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 @@ def test_getbuffer(self):
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]*/