diff options
author | Sanyam Khurana <8039608+CuriousLearner@users.noreply.github.com> | 2017-12-11 13:42:09 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2017-12-11 13:42:09 (GMT) |
commit | 1b74f9b77a6fa1d7828986cb79d5b10942ff9141 (patch) | |
tree | 8c1f1d7b3e5b2a0a128868fa7e78c4b688c02ea9 | |
parent | 48d4dd974f0c8d47c54990eedd322b96b19c60ec (diff) | |
download | cpython-1b74f9b77a6fa1d7828986cb79d5b10942ff9141.zip cpython-1b74f9b77a6fa1d7828986cb79d5b10942ff9141.tar.gz cpython-1b74f9b77a6fa1d7828986cb79d5b10942ff9141.tar.bz2 |
bpo-22671: Clarify and test default read method implementations (#4568)
Original patch written by Martin Panter, enhanced by Sanyam Khurana.
-rw-r--r-- | Doc/library/io.rst | 13 | ||||
-rw-r--r-- | Lib/test/test_io.py | 53 |
2 files changed, 59 insertions, 7 deletions
diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 8a695ad..6778058 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -205,8 +205,8 @@ ABC Inherits Stub Methods Mixin M ``writable``, and ``writelines`` :class:`RawIOBase` :class:`IOBase` ``readinto`` and Inherited :class:`IOBase` methods, ``read``, ``write`` and ``readall`` -:class:`BufferedIOBase` :class:`IOBase` ``detach``, ``read``, Inherited :class:`IOBase` methods, ``readinto`` - ``read1``, and ``write`` +:class:`BufferedIOBase` :class:`IOBase` ``detach``, ``read``, Inherited :class:`IOBase` methods, ``readinto``, + ``read1``, and ``write`` and ``readinto1`` :class:`TextIOBase` :class:`IOBase` ``detach``, ``read``, Inherited :class:`IOBase` methods, ``encoding``, ``readline``, and ``errors``, and ``newlines`` ``write`` @@ -385,14 +385,17 @@ I/O Base Classes .. method:: read(size=-1) Read up to *size* bytes from the object and return them. As a convenience, - if *size* is unspecified or -1, :meth:`readall` is called. Otherwise, - only one system call is ever made. Fewer than *size* bytes may be - returned if the operating system call returns fewer than *size* bytes. + if *size* is unspecified or -1, all bytes until EOF are returned. + Otherwise, only one system call is ever made. Fewer than *size* bytes may + be returned if the operating system call returns fewer than *size* bytes. If 0 bytes are returned, and *size* was not 0, this indicates end of file. If the object is in non-blocking mode and no bytes are available, ``None`` is returned. + The default implementation defers to :meth:`readall` and + :meth:`readinto`. + .. method:: readall() Read and return all the bytes from the stream until EOF, using multiple diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 2ac2e17..6bb4127 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -807,8 +807,8 @@ class IOTest(unittest.TestCase): self.assertRaises(ValueError, f.flush) def test_RawIOBase_read(self): - # Exercise the default RawIOBase.read() implementation (which calls - # readinto() internally). + # Exercise the default limited RawIOBase.read(n) 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") @@ -916,6 +916,55 @@ class IOTest(unittest.TestCase): with self.assertRaisesRegex(ValueError, 'read/write/append mode'): self.open(PathLike(support.TESTFN), 'rwxa') + def test_RawIOBase_readall(self): + # Exercise the default unlimited RawIOBase.read() and readall() + # implementations. + rawio = self.MockRawIOWithoutRead((b"abc", b"d", b"efg")) + self.assertEqual(rawio.read(), b"abcdefg") + rawio = self.MockRawIOWithoutRead((b"abc", b"d", b"efg")) + self.assertEqual(rawio.readall(), b"abcdefg") + + def test_BufferedIOBase_readinto(self): + # Exercise the default BufferedIOBase.readinto() and readinto1() + # implementations (which call read() or read1() internally). + class Reader(self.BufferedIOBase): + def __init__(self, avail): + self.avail = avail + def read(self, size): + result = self.avail[:size] + self.avail = self.avail[size:] + return result + def read1(self, size): + """Returns no more than 5 bytes at once""" + return self.read(min(size, 5)) + tests = ( + # (test method, total data available, read buffer size, expected + # read size) + ("readinto", 10, 5, 5), + ("readinto", 10, 6, 6), # More than read1() can return + ("readinto", 5, 6, 5), # Buffer larger than total available + ("readinto", 6, 7, 6), + ("readinto", 10, 0, 0), # Empty buffer + ("readinto1", 10, 5, 5), # Result limited to single read1() call + ("readinto1", 10, 6, 5), # Buffer larger than read1() can return + ("readinto1", 5, 6, 5), # Buffer larger than total available + ("readinto1", 6, 7, 5), + ("readinto1", 10, 0, 0), # Empty buffer + ) + UNUSED_BYTE = 0x81 + for test in tests: + with self.subTest(test): + method, avail, request, result = test + reader = Reader(bytes(range(avail))) + buffer = bytearray((UNUSED_BYTE,) * request) + method = getattr(reader, method) + self.assertEqual(method(buffer), result) + self.assertEqual(len(buffer), request) + self.assertSequenceEqual(buffer[:result], range(result)) + unused = (UNUSED_BYTE,) * (request - result) + self.assertSequenceEqual(buffer[result:], unused) + self.assertEqual(len(reader.avail), avail - result) + class CIOTest(IOTest): |