From 14c16992b563a77330478bcc6fecdb54df4300b5 Mon Sep 17 00:00:00 2001 From: Prashanth Pai Date: Tue, 23 Jun 2015 20:04:30 +0530 Subject: Add readinto() API readinto() This method is useful when you have to read a large file over multiple read calls. While read() allocates a buffer every time it's invoked, readinto() copies data to an already allocated buffer passed to it. Change-Id: Ic8a3aa0e544e09e05101c983b329c91864832e4a Signed-off-by: Prashanth Pai --- gluster/gfapi.py | 23 +++++++++++++++++++++++ test/functional/libgfapi-python-tests.py | 18 ++++++++++++++++++ test/unit/gluster/test_gfapi.py | 11 +++++++++++ 3 files changed, 52 insertions(+) diff --git a/gluster/gfapi.py b/gluster/gfapi.py index fa0e1b3..d6f847d 100755 --- a/gluster/gfapi.py +++ b/gluster/gfapi.py @@ -328,6 +328,29 @@ class File(object): err = ctypes.get_errno() raise OSError(err, os.strerror(err)) + def readinto(self, buf): + """ + Read up to len(buf) bytes into buf which must be a bytearray. + (buf cannot be a string as strings are immutable in python) + + This method is useful when you have to read a large file over + multiple read calls. While read() allocates a buffer every time + it's invoked, readinto() copies data to an already allocated + buffer passed to it. + + Returns the number of bytes read (0 for EOF). + """ + if type(buf) is bytearray: + buf_ptr = (ctypes.c_ubyte * len(buf)).from_buffer(buf) + else: + # TODO: Allow reading other types such as array.array + raise TypeError("buffer must of type bytearray") + nread = api.glfs_read(self.fd, buf_ptr, len(buf_ptr), 0) + if nread < 0: + err = ctypes.get_errno() + raise OSError(err, os.strerror(err)) + return nread + def write(self, data, flags=0): """ Write data to the file. diff --git a/test/functional/libgfapi-python-tests.py b/test/functional/libgfapi-python-tests.py index 6840d18..3a0b89e 100644 --- a/test/functional/libgfapi-python-tests.py +++ b/test/functional/libgfapi-python-tests.py @@ -502,6 +502,24 @@ class FileOpsTest(unittest.TestCase): self.vol.symlink(file_name, link_name) self.assertEqual(self.vol.readlink(link_name), file_name) + def test_readinto(self): + file_name = uuid4().hex + with File(self.vol.open(file_name, os.O_WRONLY | os.O_CREAT)) as f: + s = ''.join([str(i) for i in xrange(10)]) + f.write(s) + f.fsync() + + buf = bytearray(1) + with File(self.vol.open(file_name, os.O_RDONLY)) as f: + for i in xrange(10): + # Read one character at a time into buf + f.readinto(buf) + self.assertEqual(len(buf), 1) + self.assertEqual(buf, bytearray(str(i))) + + with File(self.vol.open(file_name, os.O_RDONLY)) as f: + self.assertRaises(TypeError, f.readinto, str("buf")) + class DirOpsTest(unittest.TestCase): diff --git a/test/unit/gluster/test_gfapi.py b/test/unit/gluster/test_gfapi.py index 5551235..4be2346 100644 --- a/test/unit/gluster/test_gfapi.py +++ b/test/unit/gluster/test_gfapi.py @@ -192,6 +192,17 @@ class TestFile(unittest.TestCase): with patch("gluster.gfapi.File.fgetsize", _mock_fgetsize): self.fd.read(buflen) + def test_readinto(self): + mock_glfs_read = Mock() + mock_glfs_read.return_value = 5 + + with patch("gluster.gfapi.api.glfs_read", mock_glfs_read): + buf = bytearray(10) + ret = self.fd.readinto(buf) + self.assertEqual(ret, 5) + + self.assertRaises(TypeError, self.fd.readinto, str("hello")) + def test_write_success(self): mock_glfs_write = Mock() mock_glfs_write.return_value = 5 -- cgit