]> granicus.if.org Git - python/commitdiff
Issue #23214: Implement optional BufferedReader, BytesIO read1() argument
authorMartin Panter <vadmium+py@gmail.com>
Thu, 20 Oct 2016 23:48:14 +0000 (23:48 +0000)
committerMartin Panter <vadmium+py@gmail.com>
Thu, 20 Oct 2016 23:48:14 +0000 (23:48 +0000)
Doc/library/io.rst
Lib/_pyio.py
Lib/test/test_io.py
Lib/test/test_memoryio.py
Misc/NEWS
Modules/_io/bufferedio.c
Modules/_io/bytesio.c
Modules/_io/clinic/bufferedio.c.h
Modules/_io/clinic/bytesio.c.h

index 4da6e095d17993b8c5b802e503dbb78dd119ae34..da7a681fa7cdcd328e1ccb24b77480b890f9a190 100644 (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:`~BufferedIOBase.read`.
 
-      In :class:`BytesIO`, this is the same as :meth:`read`.
+      .. versionchanged:: 3.7
+         The *size* argument is now optional.
 
-   .. method:: readinto1()
+   .. method:: readinto1(b)
 
-      In :class:`BytesIO`, this is the same as :meth:`readinto`.
+      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)
 
index d0947f06d518e8ffb383cccfbae5c0f71258c472..569527bc379d2b0e28a863f2607332ef29623848 100644 (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)
 
index 8a2111cbd71c6285301d1349b6b3b313ca3d5f53..877d3b5d97d6705f7b4e3b361c0b573a79abfe70 100644 (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"):
index 55b693e564710c9511ef980b282b1c2783ad8f1c..80055ce1e7d8d16413b25ca0c636742cd20d6740 100644 (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")
index 12ef2ceb61d18627b64188f957f5000748f80ceb..756b771f149e48e213faa5787f46db809a9acf32 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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.
 
index cbe7425eaefa6552352e6ca3c333b74c737e80e3..c760522616ad72c6ab5f7468b8b853173c6b07f8 100644 (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")
index a1ba121e2622ff8d2f85f8f3a56ca9a55f907b15..96be0f454139f57b0484331394fb9cf9fc5bdb38 100644 (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);
 }
index 58144a4015498b855831b07a850e319d48185ef1..dc69c48557d448e3930ab7f64820ec472e684310 100644 (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]*/
index c64ce5c77c3bc72395bbb3e7e062e87e234885d0..1434782925e8ccaee6ad0c9d95a60dcf666855f9 100644 (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]*/