diff options
| -rw-r--r-- | Doc/library/io.rst | 5 | ||||
| -rw-r--r-- | Lib/_pyio.py | 4 | ||||
| -rw-r--r-- | Lib/test/test_io.py | 44 | ||||
| -rw-r--r-- | Misc/NEWS | 3 | ||||
| -rw-r--r-- | Modules/_io/iobase.c | 4 | 
5 files changed, 45 insertions, 15 deletions
diff --git a/Doc/library/io.rst b/Doc/library/io.rst index e61aa90432..3e0c62dd0a 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -361,8 +361,9 @@ I/O Base Classes     .. method:: readinto(b) -      Read up to len(b) bytes into bytearray *b* and return the number of bytes -      read. +      Read up to len(b) bytes into bytearray *b* and return the number ofbytes +      read.  If the object is in non-blocking mode and no bytes are available, +      ``None`` is returned.     .. method:: write(b) diff --git a/Lib/_pyio.py b/Lib/_pyio.py index d6dce6d781..83bce709b5 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -544,6 +544,8 @@ class RawIOBase(IOBase):              return self.readall()          b = bytearray(n.__index__())          n = self.readinto(b) +        if n is None: +            return None          del b[n:]          return bytes(b) @@ -561,7 +563,7 @@ class RawIOBase(IOBase):          """Read up to len(b) bytes into b.          Returns number of bytes read (0 for EOF), or None if the object -        is set not to block as has no data to read. +        is set not to block and has no data to read.          """          self._unsupported("readinto") diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index c9f7582ede..dfddfb54a7 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -48,7 +48,9 @@ def _default_chunk_size():          return f._CHUNK_SIZE -class MockRawIO: +class MockRawIOWithoutRead: +    """A RawIO implementation without read(), so as to exercise the default +    RawIO.read() which calls readinto()."""      def __init__(self, read_stack=()):          self._read_stack = list(read_stack) @@ -56,14 +58,6 @@ class MockRawIO:          self._reads = 0          self._extraneous_reads = 0 -    def read(self, n=None): -        self._reads += 1 -        try: -            return self._read_stack.pop(0) -        except: -            self._extraneous_reads += 1 -            return b"" -      def write(self, b):          self._write_stack.append(bytes(b))          return len(b) @@ -110,6 +104,23 @@ class MockRawIO:      def truncate(self, pos=None):          return pos +class CMockRawIOWithoutRead(MockRawIOWithoutRead, io.RawIOBase): +    pass + +class PyMockRawIOWithoutRead(MockRawIOWithoutRead, pyio.RawIOBase): +    pass + + +class MockRawIO(MockRawIOWithoutRead): + +    def read(self, n=None): +        self._reads += 1 +        try: +            return self._read_stack.pop(0) +        except: +            self._extraneous_reads += 1 +            return b"" +  class CMockRawIO(MockRawIO, io.RawIOBase):      pass @@ -582,6 +593,19 @@ class IOTest(unittest.TestCase):          f.close()          self.assertRaises(ValueError, f.flush) +    def test_RawIOBase_read(self): +        # Exercise the default RawIOBase.read() implementation (which calls +        # readinto() internally). +        rawio = self.MockRawIOWithoutRead((b"abc", b"d", None, b"efg", None)) +        self.assertEqual(rawio.read(2), b"ab") +        self.assertEqual(rawio.read(2), b"c") +        self.assertEqual(rawio.read(2), b"d") +        self.assertEqual(rawio.read(2), None) +        self.assertEqual(rawio.read(2), b"ef") +        self.assertEqual(rawio.read(2), b"g") +        self.assertEqual(rawio.read(2), None) +        self.assertEqual(rawio.read(2), b"") +  class CIOTest(IOTest):      pass @@ -2590,7 +2614,7 @@ def test_main():      # Put the namespaces of the IO module we are testing and some useful mock      # classes in the __dict__ of each test.      mocks = (MockRawIO, MisbehavedRawIO, MockFileIO, CloseFailureIO, -             MockNonBlockWriterIO, MockUnseekableIO) +             MockNonBlockWriterIO, MockUnseekableIO, MockRawIOWithoutRead)      all_members = io.__all__ + ["IncrementalNewlineDecoder"]      c_io_ns = {name : getattr(io, name) for name in all_members}      py_io_ns = {name : getattr(pyio, name) for name in all_members} @@ -52,6 +52,9 @@ Core and Builtins  Library  ------- +- Issue #9854: The default read() implementation in io.RawIOBase now +  handles non-blocking readinto() returning None correctly. +  - Issue #1552: socket.socketpair() now returns regular socket.socket    objects supporting the whole socket API (rather than the "raw"    _socket.socket objects). diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index c74672df18..9b58137fb9 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -777,9 +777,9 @@ rawiobase_read(PyObject *self, PyObject *args)          return NULL;      res = PyObject_CallMethodObjArgs(self, _PyIO_str_readinto, b, NULL); -    if (res == NULL) { +    if (res == NULL || res == Py_None) {          Py_DECREF(b); -        return NULL; +        return res;      }      n = PyNumber_AsSsize_t(res, PyExc_ValueError);  | 
