Skip to content

Commit

Permalink
added extract method (#127)
Browse files Browse the repository at this point in the history
* added extract method

* added chunk_size and docs

* typo

* renamed extract to getfile

* used openbin

* locked getfile, added to docs

* changelog

* version bump
  • Loading branch information
willmcgugan committed Jan 3, 2018
1 parent 84c0f7d commit bf0dbd8
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 10 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unrealeased]

### Added

- fs.getfile function

## [2.0.17] - 2017-11-20

### Fixed
Expand Down
9 changes: 5 additions & 4 deletions docs/source/implementers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,11 @@ In the general case, it is a good idea to look at how these methods are implemen
* :meth:`~fs.base.FS.exists`
* :meth:`~fs.base.FS.filterdir`
* :meth:`~fs.base.FS.getbytes`
* :meth:`~fs.base.FS.gettext`
* :meth:`~fs.base.FS.getfile`
* :meth:`~fs.base.FS.getmeta`
* :meth:`~fs.base.FS.getsize`
* :meth:`~fs.base.FS.getsyspath`
* :meth:`~fs.base.FS.gettext`
* :meth:`~fs.base.FS.gettype`
* :meth:`~fs.base.FS.geturl`
* :meth:`~fs.base.FS.hassyspath`
Expand All @@ -105,18 +106,18 @@ In the general case, it is a good idea to look at how these methods are implemen
* :meth:`~fs.base.FS.isempty`
* :meth:`~fs.base.FS.isfile`
* :meth:`~fs.base.FS.lock`
* :meth:`~fs.base.FS.movedir`
* :meth:`~fs.base.FS.makedirs`
* :meth:`~fs.base.FS.move`
* :meth:`~fs.base.FS.movedir`
* :meth:`~fs.base.FS.open`
* :meth:`~fs.base.FS.opendir`
* :meth:`~fs.base.FS.removetree`
* :meth:`~fs.base.FS.scandir`
* :meth:`~fs.base.FS.setbytes`
* :meth:`~fs.base.FS.setbin`
* :meth:`~fs.base.FS.setbytes`
* :meth:`~fs.base.FS.setfile`
* :meth:`~fs.base.FS.settimes`
* :meth:`~fs.base.FS.settext`
* :meth:`~fs.base.FS.settimes`
* :meth:`~fs.base.FS.touch`
* :meth:`~fs.base.FS.validatepath`

Expand Down
2 changes: 1 addition & 1 deletion fs/_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Version, used in module and setup.py.
"""
__version__ = "2.0.17"
__version__ = "2.0.18a0"
35 changes: 34 additions & 1 deletion fs/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class FS(object):

# This is the "standard" meta namespace.
_meta = {}

# most FS will use default walking algorithms
walker_class=Walker

Expand Down Expand Up @@ -501,6 +501,39 @@ def getbytes(self, path):
contents = read_file.read()
return contents

def getfile(self, path, file, chunk_size=None, **options):
"""Copies a file from the filesystem to a file-like object.
This may be more efficient that opening and copying files
manually if the filesystem supplies an optimized method.
Arguments:
path (str): Path to a resource.
file (file-like): A file-like object open for writing in
binary mode.
chunk_size (int, optional): Number of bytes to read at a
time, if a simple copy is used, or `None` to use
sensible default.
**options: Implementation specific options required to open
the source file.
Note that the file object ``file`` will *not* be closed by this
method. Take care to close it after this method completes
(ideally with a context manager).
Example:
>>> with open('starwars.mov', 'wb') as write_file:
... my_fs.getfile('/movies/starwars.mov', write_file)
"""
with self._lock:
with self.openbin(path, **options) as read_file:
tools.copy_file_data(
read_file,
file,
chunk_size=chunk_size
)

def gettext(self, path, encoding=None, errors=None, newline=''):
"""Get the contents of a file as a string.
Expand Down
5 changes: 2 additions & 3 deletions fs/copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def copy_file(src_fs, src_path, dst_fs, dst_path):
else:
# Standard copy
with src_fs.lock(), dst_fs.lock():
with src_fs.open(src_path, 'rb') as read_file:
with src_fs.openbin(src_path) as read_file:
# There may be an optimized copy available on
# dst_fs
dst_fs.setbinfile(dst_path, read_file)
Expand Down Expand Up @@ -143,7 +143,7 @@ def copy_file_if_newer(src_fs, src_path, dst_fs, dst_path):
with src_fs.lock(), dst_fs.lock():
if _source_is_newer(src_fs, src_path,
dst_fs, dst_path):
with src_fs.open(src_path, 'rb') as read_file:
with src_fs.openbin(src_path) as read_file:
# There may be an optimized copy available
# on dst_fs
dst_fs.setbinfile(dst_path, read_file)
Expand All @@ -152,7 +152,6 @@ def copy_file_if_newer(src_fs, src_path, dst_fs, dst_path):
return False



def copy_structure(src_fs, dst_fs, walker=None):
"""Copy directories (but not files) from ``src_fs`` to ``dst_fs``.
Expand Down
10 changes: 10 additions & 0 deletions fs/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1331,6 +1331,16 @@ def test_getbytes(self):
with self.assertRaises(errors.FileExpected):
self.fs.getbytes('baz')

def test_getfile(self):
test_bytes = b'Hello, World'
self.fs.setbytes('hello.bin', test_bytes)
write_file = io.BytesIO()
self.fs.getfile('hello.bin', write_file)
self.assertEqual(write_file.getvalue(), test_bytes)

with self.assertRaises(errors.ResourceNotFound):
self.fs.getfile('foo.bin', write_file)

def test_isempty(self):
self.assertTrue(self.fs.isempty('/'))
self.fs.makedir('foo')
Expand Down
1 change: 0 additions & 1 deletion tests/test_ftpfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ def test_manager(self):
with ftp_errors(mem_fs):
raise error_perm('999 foo')


class TestFTPFS(FSTestCases, unittest.TestCase):

user = 'user'
Expand Down

0 comments on commit bf0dbd8

Please sign in to comment.