Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added extract method #127

Merged
merged 8 commits into from
Jan 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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