[Python-checkins] cpython: Issue #23214: Implement optional BufferedReader, BytesIO read1() argument

martin.panter python-checkins at python.org
Thu Oct 20 20:16:55 EDT 2016


https://hg.python.org/cpython/rev/b6886ac88e28
changeset:   104597:b6886ac88e28
user:        Martin Panter <vadmium+py at gmail.com>
date:        Thu Oct 20 23:48:14 2016 +0000
summary:
  Issue #23214: Implement optional BufferedReader, BytesIO read1() argument

files:
  Doc/library/io.rst                |  21 ++++++++++----
  Lib/_pyio.py                      |  18 ++++++------
  Lib/test/test_io.py               |  16 ++++++++++-
  Lib/test/test_memoryio.py         |   6 +--
  Misc/NEWS                         |   4 ++
  Modules/_io/bufferedio.c          |  10 ++----
  Modules/_io/bytesio.c             |   6 ++--
  Modules/_io/clinic/bufferedio.c.h |  13 +++++----
  Modules/_io/clinic/bytesio.c.h    |  26 ++++++++++++++++--
  9 files changed, 81 insertions(+), 39 deletions(-)


diff --git a/Doc/library/io.rst b/Doc/library/io.rst
--- a/Doc/library/io.rst
+++ b/Doc/library/io.rst
@@ -477,7 +477,7 @@
       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 @@
       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 @@
       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 @@
       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)
 
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -635,7 +635,7 @@
     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 @@
         """
         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 @@
         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 @@
         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 @@
                 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 @@
         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 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 @@
         self.flush()
         return BufferedReader.peek(self, size)
 
-    def read1(self, size):
+    def read1(self, size=-1):
         self.flush()
         return BufferedReader.read1(self, size)
 
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -1146,6 +1146,7 @@
         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 @@
         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 @@
         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 @@
             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"):
diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py
--- a/Lib/test/test_memoryio.py
+++ b/Lib/test/test_memoryio.py
@@ -437,10 +437,8 @@
 
     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")
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -88,6 +88,10 @@
 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.
 
diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c
--- a/Modules/_io/bufferedio.c
+++ b/Modules/_io/bufferedio.c
@@ -904,7 +904,7 @@
     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 @@
 
 /*[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")
diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c
--- a/Modules/_io/bytesio.c
+++ b/Modules/_io/bytesio.c
@@ -420,7 +420,7 @@
 
 /*[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 @@
 [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);
 }
diff --git a/Modules/_io/clinic/bufferedio.c.h b/Modules/_io/clinic/bufferedio.c.h
--- a/Modules/_io/clinic/bufferedio.c.h
+++ b/Modules/_io/clinic/bufferedio.c.h
@@ -140,23 +140,24 @@
 }
 
 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 @@
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=a956f394ecde4cf9 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=490c97bfcfd92c51 input=a9049054013a1b77]*/
diff --git a/Modules/_io/clinic/bytesio.c.h b/Modules/_io/clinic/bytesio.c.h
--- a/Modules/_io/clinic/bytesio.c.h
+++ b/Modules/_io/clinic/bytesio.c.h
@@ -181,7 +181,7 @@
 }
 
 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 @@
 "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 @@
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=6382e8eb578eea64 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=8f469431da1b3857 input=a9049054013a1b77]*/

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list