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

handles non-blocking readinto() returning None correctly.
This commit is contained in:
Antoine Pitrou 2010-09-14 18:37:24 +00:00
parent 9e0b864ac0
commit 328ec7455f
5 changed files with 45 additions and 15 deletions

View File

@ -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)

View File

@ -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")

View File

@ -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}

View File

@ -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).

View File

@ -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);