diff --git a/CHANGELOG.md b/CHANGELOG.md index fa072e2e..3b965c9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Missing `mode` attribute to `_MemoryFile` objects returned by `MemoryFS.openbin`. +- Missing `readinto` method for `MemoryFS` and `FTPFS` file objects. Closes + [#380](https://github.com/PyFilesystem/pyfilesystem2/issues/380). ### Changed diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 3b3e8c2d..ba15fd73 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -9,6 +9,7 @@ Many thanks to the following developers for contributing to this project: - [Justin Charlong](https://github.com/jcharlong) - [Louis Sautier](https://github.com/sbraz) - [Martin Larralde](https://github.com/althonos) +- [Nick Henderson](https://github.com/nwh) - [Will McGugan](https://github.com/willmcgugan) - [Zmej Serow](https://github.com/zmej-serow) - [Morten Engelhardt Olsen](https://github.com/xoriath) diff --git a/fs/ftpfs.py b/fs/ftpfs.py index 6442a1ba..e3a39411 100644 --- a/fs/ftpfs.py +++ b/fs/ftpfs.py @@ -235,6 +235,13 @@ def read(self, size=-1): remaining -= len(chunk) return b"".join(chunks) + def readinto(self, buffer): + # type: (bytearray) -> int + data = self.read(len(buffer)) + bytes_read = len(data) + buffer[:bytes_read] = data + return bytes_read + def readline(self, size=-1): # type: (int) -> bytes return next(line_iterator(self, size)) # type: ignore diff --git a/fs/iotools.py b/fs/iotools.py index 26402ff3..44849680 100644 --- a/fs/iotools.py +++ b/fs/iotools.py @@ -117,7 +117,7 @@ def readinto(self, b): except AttributeError: data = self._f.read(len(b)) bytes_read = len(data) - b[: len(data)] = data + b[:bytes_read] = data return bytes_read @typing.no_type_check @@ -128,7 +128,7 @@ def readinto1(self, b): except AttributeError: data = self._f.read1(len(b)) bytes_read = len(data) - b[: len(data)] = data + b[:bytes_read] = data return bytes_read def readline(self, limit=-1): diff --git a/fs/memoryfs.py b/fs/memoryfs.py index 2a011b21..d1c23724 100644 --- a/fs/memoryfs.py +++ b/fs/memoryfs.py @@ -113,12 +113,15 @@ def __iter__(self): def next(self): # type: () -> bytes with self._seek_lock(): + self.on_access() return next(self._bytes_io) __next__ = next def readline(self, size=-1): # type: (int) -> bytes + if not self._mode.reading: + raise IOError("File not open for reading") with self._seek_lock(): self.on_access() return self._bytes_io.readline(size) @@ -142,9 +145,20 @@ def readable(self): # type: () -> bool return self._mode.reading + def readinto(self, buffer): + # type (bytearray) -> Optional[int] + if not self._mode.reading: + raise IOError("File not open for reading") + with self._seek_lock(): + self.on_access() + return self._bytes_io.readinto(buffer) + def readlines(self, hint=-1): # type: (int) -> List[bytes] + if not self._mode.reading: + raise IOError("File not open for reading") with self._seek_lock(): + self.on_access() return self._bytes_io.readlines(hint) def seekable(self): diff --git a/fs/test.py b/fs/test.py index 37751731..9d8e7ed7 100644 --- a/fs/test.py +++ b/fs/test.py @@ -870,6 +870,11 @@ def test_open_files(self): self.assertTrue(f.readable()) self.assertFalse(f.closed) self.assertEqual(f.readlines(8), [b"Hello\n", b"World\n"]) + self.assertEqual(f.tell(), 12) + buffer = bytearray(4) + self.assertEqual(f.readinto(buffer), 4) + self.assertEqual(f.tell(), 16) + self.assertEqual(buffer, b"foo\n") with self.assertRaises(IOError): f.write(b"no") self.assertTrue(f.closed)