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 A :exc:`BlockingIOError` is raised if the underlying raw stream is in
non blocking-mode, and has no data available at the moment. 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 Read and return up to *size* bytes, with at most one call to the
underlying raw stream's :meth:`~RawIOBase.read` (or 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` implementing your own buffering on top of a :class:`BufferedIOBase`
object. object.
If *size* is 1 (the default), an arbitrary number of bytes are
returned (more than zero unless EOF is reached).
.. method:: readinto(b) .. method:: readinto(b)
Read bytes into a pre-allocated, writable 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. 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 .. 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 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. 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. 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. If at least one byte is buffered, only buffered bytes are returned.
Otherwise, one raw stream read call is made. 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) .. class:: BufferedWriter(raw, buffer_size=DEFAULT_BUFFER_SIZE)

View file

@ -635,7 +635,7 @@ class BufferedIOBase(IOBase):
implementation, but wrap one. 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. """Read and return up to size bytes, where size is an int.
If the argument is omitted, None, or negative, reads and If the argument is omitted, None, or negative, reads and
@ -655,7 +655,7 @@ class BufferedIOBase(IOBase):
""" """
self._unsupported("read") 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, """Read up to size bytes with at most one read() system call,
where size is an int. where size is an int.
""" """
@ -863,7 +863,7 @@ class BytesIO(BufferedIOBase):
self._buffer.clear() self._buffer.clear()
super().close() super().close()
def read(self, size=None): def read(self, size=-1):
if self.closed: if self.closed:
raise ValueError("read from closed file") raise ValueError("read from closed file")
if size is None: if size is None:
@ -877,7 +877,7 @@ class BytesIO(BufferedIOBase):
self._pos = newpos self._pos = newpos
return bytes(b) return bytes(b)
def read1(self, size): def read1(self, size=-1):
"""This is the same as read. """This is the same as read.
""" """
return self.read(size) return self.read(size)
@ -1073,12 +1073,12 @@ class BufferedReader(_BufferedIOMixin):
self._read_pos = 0 self._read_pos = 0
return self._read_buf[self._read_pos:] 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.""" """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 # Returns up to size bytes. If at least one byte is buffered, we
# only return buffered bytes. Otherwise, we do one raw read. # only return buffered bytes. Otherwise, we do one raw read.
if size < 0: if size < 0:
raise ValueError("number of bytes to read must be positive") size = self.buffer_size
if size == 0: if size == 0:
return b"" return b""
with self._read_lock: with self._read_lock:
@ -1270,7 +1270,7 @@ class BufferedRWPair(BufferedIOBase):
self.reader = BufferedReader(reader, buffer_size) self.reader = BufferedReader(reader, buffer_size)
self.writer = BufferedWriter(writer, buffer_size) self.writer = BufferedWriter(writer, buffer_size)
def read(self, size=None): def read(self, size=-1):
if size is None: if size is None:
size = -1 size = -1
return self.reader.read(size) return self.reader.read(size)
@ -1284,7 +1284,7 @@ class BufferedRWPair(BufferedIOBase):
def peek(self, size=0): def peek(self, size=0):
return self.reader.peek(size) return self.reader.peek(size)
def read1(self, size): def read1(self, size=-1):
return self.reader.read1(size) return self.reader.read1(size)
def readinto1(self, b): def readinto1(self, b):
@ -1370,7 +1370,7 @@ class BufferedRandom(BufferedWriter, BufferedReader):
self.flush() self.flush()
return BufferedReader.peek(self, size) return BufferedReader.peek(self, size)
def read1(self, size): def read1(self, size=-1):
self.flush() self.flush()
return BufferedReader.read1(self, size) 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"a", bufio.read(1))
self.assertEqual(b"b", bufio.read1(1)) self.assertEqual(b"b", bufio.read1(1))
self.assertEqual(rawio._reads, 1) self.assertEqual(rawio._reads, 1)
self.assertEqual(b"", bufio.read1(0))
self.assertEqual(b"c", bufio.read1(100)) self.assertEqual(b"c", bufio.read1(100))
self.assertEqual(rawio._reads, 1) self.assertEqual(rawio._reads, 1)
self.assertEqual(b"d", bufio.read1(100)) self.assertEqual(b"d", bufio.read1(100))
@ -1154,8 +1155,17 @@ class BufferedReaderTest(unittest.TestCase, CommonBufferedTests):
self.assertEqual(rawio._reads, 3) self.assertEqual(rawio._reads, 3)
self.assertEqual(b"", bufio.read1(100)) self.assertEqual(b"", bufio.read1(100))
self.assertEqual(rawio._reads, 4) 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): def test_readinto(self):
rawio = self.MockRawIO((b"abc", b"d", b"efg")) 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()) pair = self.tp(self.BytesIO(b"abcdef"), self.MockRawIO())
self.assertEqual(pair.read1(3), b"abc") self.assertEqual(pair.read1(3), b"abc")
self.assertEqual(pair.read1(), b"def")
def test_readinto(self): def test_readinto(self):
for method in ("readinto", "readinto1"): for method in ("readinto", "readinto1"):
@ -3467,6 +3478,7 @@ class MiscIOTest(unittest.TestCase):
self.assertRaises(ValueError, f.read) self.assertRaises(ValueError, f.read)
if hasattr(f, "read1"): if hasattr(f, "read1"):
self.assertRaises(ValueError, f.read1, 1024) self.assertRaises(ValueError, f.read1, 1024)
self.assertRaises(ValueError, f.read1)
if hasattr(f, "readall"): if hasattr(f, "readall"):
self.assertRaises(ValueError, f.readall) self.assertRaises(ValueError, f.readall)
if hasattr(f, "readinto"): if hasattr(f, "readinto"):

View file

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

View file

@ -88,6 +88,10 @@ Core and Builtins
Library 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 - Issue #28480: Fix error building socket module when multithreading is
disabled. disabled.

View file

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

View file

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

View file

@ -140,23 +140,24 @@ exit:
} }
PyDoc_STRVAR(_io__Buffered_read1__doc__, PyDoc_STRVAR(_io__Buffered_read1__doc__,
"read1($self, size, /)\n" "read1($self, size=-1, /)\n"
"--\n" "--\n"
"\n"); "\n");
#define _IO__BUFFERED_READ1_METHODDEF \ #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 * static PyObject *
_io__Buffered_read1_impl(buffered *self, Py_ssize_t n); _io__Buffered_read1_impl(buffered *self, Py_ssize_t n);
static PyObject * static PyObject *
_io__Buffered_read1(buffered *self, PyObject *arg) _io__Buffered_read1(buffered *self, PyObject *args)
{ {
PyObject *return_value = NULL; 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; goto exit;
} }
return_value = _io__Buffered_read1_impl(self, n); return_value = _io__Buffered_read1_impl(self, n);
@ -475,4 +476,4 @@ _io_BufferedRandom___init__(PyObject *self, PyObject *args, PyObject *kwargs)
exit: exit:
return return_value; 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__, PyDoc_STRVAR(_io_BytesIO_read1__doc__,
"read1($self, size, /)\n" "read1($self, size=-1, /)\n"
"--\n" "--\n"
"\n" "\n"
"Read at most size bytes, returned as a bytes object.\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."); "Return an empty bytes object at EOF.");
#define _IO_BYTESIO_READ1_METHODDEF \ #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__, PyDoc_STRVAR(_io_BytesIO_readline__doc__,
"readline($self, size=None, /)\n" "readline($self, size=None, /)\n"
@ -428,4 +448,4 @@ _io_BytesIO___init__(PyObject *self, PyObject *args, PyObject *kwargs)
exit: exit:
return return_value; return return_value;
} }
/*[clinic end generated code: output=6382e8eb578eea64 input=a9049054013a1b77]*/ /*[clinic end generated code: output=8f469431da1b3857 input=a9049054013a1b77]*/