Commit 328ec745 authored by Antoine Pitrou's avatar Antoine Pitrou

Issue #9854: The default read() implementation in io.RawIOBase now

handles non-blocking readinto() returning None correctly.
parent 9e0b864a
...@@ -361,8 +361,9 @@ I/O Base Classes ...@@ -361,8 +361,9 @@ I/O Base Classes
.. method:: readinto(b) .. method:: readinto(b)
Read up to len(b) bytes into bytearray *b* and return the number of bytes Read up to len(b) bytes into bytearray *b* and return the number ofbytes
read. read. If the object is in non-blocking mode and no bytes are available,
``None`` is returned.
.. method:: write(b) .. method:: write(b)
......
...@@ -544,6 +544,8 @@ class RawIOBase(IOBase): ...@@ -544,6 +544,8 @@ class RawIOBase(IOBase):
return self.readall() return self.readall()
b = bytearray(n.__index__()) b = bytearray(n.__index__())
n = self.readinto(b) n = self.readinto(b)
if n is None:
return None
del b[n:] del b[n:]
return bytes(b) return bytes(b)
...@@ -561,7 +563,7 @@ class RawIOBase(IOBase): ...@@ -561,7 +563,7 @@ class RawIOBase(IOBase):
"""Read up to len(b) bytes into b. """Read up to len(b) bytes into b.
Returns number of bytes read (0 for EOF), or None if the object 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") self._unsupported("readinto")
......
...@@ -48,7 +48,9 @@ def _default_chunk_size(): ...@@ -48,7 +48,9 @@ def _default_chunk_size():
return f._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=()): def __init__(self, read_stack=()):
self._read_stack = list(read_stack) self._read_stack = list(read_stack)
...@@ -56,14 +58,6 @@ class MockRawIO: ...@@ -56,14 +58,6 @@ class MockRawIO:
self._reads = 0 self._reads = 0
self._extraneous_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): def write(self, b):
self._write_stack.append(bytes(b)) self._write_stack.append(bytes(b))
return len(b) return len(b)
...@@ -110,6 +104,23 @@ class MockRawIO: ...@@ -110,6 +104,23 @@ class MockRawIO:
def truncate(self, pos=None): def truncate(self, pos=None):
return pos 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): class CMockRawIO(MockRawIO, io.RawIOBase):
pass pass
...@@ -582,6 +593,19 @@ class IOTest(unittest.TestCase): ...@@ -582,6 +593,19 @@ class IOTest(unittest.TestCase):
f.close() f.close()
self.assertRaises(ValueError, f.flush) 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): class CIOTest(IOTest):
pass pass
...@@ -2590,7 +2614,7 @@ def test_main(): ...@@ -2590,7 +2614,7 @@ def test_main():
# Put the namespaces of the IO module we are testing and some useful mock # Put the namespaces of the IO module we are testing and some useful mock
# classes in the __dict__ of each test. # classes in the __dict__ of each test.
mocks = (MockRawIO, MisbehavedRawIO, MockFileIO, CloseFailureIO, mocks = (MockRawIO, MisbehavedRawIO, MockFileIO, CloseFailureIO,
MockNonBlockWriterIO, MockUnseekableIO) MockNonBlockWriterIO, MockUnseekableIO, MockRawIOWithoutRead)
all_members = io.__all__ + ["IncrementalNewlineDecoder"] all_members = io.__all__ + ["IncrementalNewlineDecoder"]
c_io_ns = {name : getattr(io, name) for name in all_members} c_io_ns = {name : getattr(io, name) for name in all_members}
py_io_ns = {name : getattr(pyio, 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 ...@@ -52,6 +52,9 @@ Core and Builtins
Library 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 - Issue #1552: socket.socketpair() now returns regular socket.socket
objects supporting the whole socket API (rather than the "raw" objects supporting the whole socket API (rather than the "raw"
_socket.socket objects). _socket.socket objects).
......
...@@ -777,9 +777,9 @@ rawiobase_read(PyObject *self, PyObject *args) ...@@ -777,9 +777,9 @@ rawiobase_read(PyObject *self, PyObject *args)
return NULL; return NULL;
res = PyObject_CallMethodObjArgs(self, _PyIO_str_readinto, b, NULL); res = PyObject_CallMethodObjArgs(self, _PyIO_str_readinto, b, NULL);
if (res == NULL) { if (res == NULL || res == Py_None) {
Py_DECREF(b); Py_DECREF(b);
return NULL; return res;
} }
n = PyNumber_AsSsize_t(res, PyExc_ValueError); n = PyNumber_AsSsize_t(res, PyExc_ValueError);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment