Skip to content

add options for several IO operation #362

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

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -7,11 +7,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [2.4.12] - (Unreleased)

### Added

- Added `options` kwargs to several I/O operations, including: `readbytes`,
`writebytes`, `readtext`, `writetext` and `writefile`.

### Changed

- Start testing on PyPy. Due to [#342](https://github.com/PyFilesystem/pyfilesystem2/issues/342)
we have to treat PyPy builds specially and allow them to fail, but at least we'll
be able to see if we break something aside from known issues with FTP tests.
- Set `py36 = false` for black to prevent adding trailing comma in function
definition and cause `SyntaxError` in older python version.

## [2.4.11] - 2019-09-07

50 changes: 39 additions & 11 deletions fs/base.py
Original file line number Diff line number Diff line change
@@ -586,12 +586,14 @@ def exclude_file(patterns, info):
iter_info = itertools.islice(iter_info, start, end)
return iter_info

def readbytes(self, path):
# type: (Text) -> bytes
def readbytes(self, path, **options):
# type: (Text, Any) -> bytes
"""Get the contents of a file as bytes.

Arguments:
path (str): A path to a readable file on the filesystem.
**options: keyword arguments for any additional information
required by the filesystem (if any).

Returns:
bytes: the file contents.
@@ -600,7 +602,7 @@ def readbytes(self, path):
fs.errors.ResourceNotFound: if ``path`` does not exist.

"""
with closing(self.open(path, mode="rb")) as read_file:
with closing(self.open(path, mode="rb", **options)) as read_file:
contents = read_file.read()
return contents

@@ -644,6 +646,7 @@ def readtext(
encoding=None, # type: Optional[Text]
errors=None, # type: Optional[Text]
newline="", # type: Text
**options # type: Any
):
# type: (...) -> Text
"""Get the contents of a file as a string.
@@ -654,6 +657,8 @@ def readtext(
in text mode (defaults to `None`, reading in binary mode).
errors (str, optional): Unicode errors parameter.
newline (str): Newlines parameter.
**options: keyword arguments for any additional information
required by the filesystem (if any).

Returns:
str: file contents.
@@ -664,7 +669,12 @@ def readtext(
"""
with closing(
self.open(
path, mode="rt", encoding=encoding, errors=errors, newline=newline
path,
mode="rt",
encoding=encoding,
errors=errors,
newline=newline,
**options
)
) as read_file:
contents = read_file.read()
@@ -1163,7 +1173,7 @@ def open(
"""
validate_open_mode(mode)
bin_mode = mode.replace("t", "")
bin_file = self.openbin(path, mode=bin_mode, buffering=buffering)
bin_file = self.openbin(path, mode=bin_mode, buffering=buffering, **options)
io_stream = iotools.make_stream(
path,
bin_file,
@@ -1172,7 +1182,7 @@ def open(
encoding=encoding or "utf-8",
errors=errors,
newline=newline,
**options
line_buffering=options.get("line_buffering", False),
)
return io_stream

@@ -1271,22 +1281,24 @@ def scandir(
iter_info = itertools.islice(iter_info, start, end)
return iter_info

def writebytes(self, path, contents):
# type: (Text, bytes) -> None
def writebytes(self, path, contents, **options):
# type: (Text, bytes, Any) -> None
# FIXME(@althonos): accept bytearray and memoryview as well ?
"""Copy binary data to a file.

Arguments:
path (str): Destination path on the filesystem.
contents (bytes): Data to be written.
**options: keyword arguments for any additional information
required by the filesystem (if any).

Raises:
TypeError: if contents is not bytes.

"""
if not isinstance(contents, bytes):
raise TypeError("contents must be bytes")
with closing(self.open(path, mode="wb")) as write_file:
with closing(self.open(path, mode="wb", **options)) as write_file:
write_file.write(contents)

setbytes = _new_name(writebytes, "setbytes")
@@ -1331,6 +1343,7 @@ def writefile(
encoding=None, # type: Optional[Text]
errors=None, # type: Optional[Text]
newline="", # type: Text
**options # type: Any
):
# type: (...) -> None
"""Set a file to the contents of a file object.
@@ -1343,6 +1356,8 @@ def writefile(
errors (str, optional): How encoding errors should be treated
(same as `io.open`).
newline (str): Newline parameter (same as `io.open`).
**options: Implementation specific options required to open
the source file.

This method is similar to `~FS.upload`, in that it copies data from a
file-like object to a resource on the filesystem, but unlike ``upload``,
@@ -1362,7 +1377,12 @@ def writefile(

with self._lock:
with self.open(
path, mode=mode, encoding=encoding, errors=errors, newline=newline
path,
mode=mode,
encoding=encoding,
errors=errors,
newline=newline,
**options
) as dst_file:
tools.copy_file_data(file, dst_file)

@@ -1401,6 +1421,7 @@ def writetext(
encoding="utf-8", # type: Text
errors=None, # type: Optional[Text]
newline="", # type: Text
**options # type: Any
):
# type: (...) -> None
"""Create or replace a file with text.
@@ -1413,6 +1434,8 @@ def writetext(
errors (str, optional): How encoding errors should be treated
(same as `io.open`).
newline (str): Newline parameter (same as `io.open`).
**options: keyword arguments for any additional information
required by the filesystem (if any).

Raises:
TypeError: if ``contents`` is not a unicode string.
@@ -1422,7 +1445,12 @@ def writetext(
raise TypeError("contents must be unicode")
with closing(
self.open(
path, mode="wt", encoding=encoding, errors=errors, newline=newline
path,
mode="wt",
encoding=encoding,
errors=errors,
newline=newline,
**options
)
) as write_file:
write_file.write(contents)
10 changes: 5 additions & 5 deletions fs/ftpfs.py
Original file line number Diff line number Diff line change
@@ -761,19 +761,19 @@ def upload(self, path, file, chunk_size=None, **options):
str("STOR ") + _encode(_path, self.ftp.encoding), file
)

def writebytes(self, path, contents):
# type: (Text, ByteString) -> None
def writebytes(self, path, contents, **options):
# type: (Text, ByteString, Any) -> None
if not isinstance(contents, bytes):
raise TypeError("contents must be bytes")
self.upload(path, io.BytesIO(contents))
self.upload(path, io.BytesIO(contents), **options)

def setinfo(self, path, info):
# type: (Text, RawInfo) -> None
if not self.exists(path):
raise errors.ResourceNotFound(path)

def readbytes(self, path):
# type: (Text) -> bytes
def readbytes(self, path, **options):
# type: (Text, Any) -> bytes
_path = self.validatepath(path)
data = io.BytesIO()
with ftp_errors(self, path):
25 changes: 17 additions & 8 deletions fs/mountfs.py
Original file line number Diff line number Diff line change
@@ -186,11 +186,11 @@ def removedir(self, path):
fs, _path = self._delegate(path)
return fs.removedir(_path)

def readbytes(self, path):
# type: (Text) -> bytes
def readbytes(self, path, **options):
# type: (Text, Any) -> bytes
self.check()
fs, _path = self._delegate(path)
return fs.readbytes(_path)
return fs.readbytes(_path, **options)

def download(self, path, file, chunk_size=None, **options):
# type: (Text, BinaryIO, Optional[int], **Any) -> None
@@ -203,11 +203,14 @@ def readtext(
encoding=None, # type: Optional[Text]
errors=None, # type: Optional[Text]
newline="", # type: Text
**options # type: Any
):
# type: (...) -> Text
self.check()
fs, _path = self._delegate(path)
return fs.readtext(_path, encoding=encoding, errors=errors, newline=newline)
return fs.readtext(
_path, encoding=encoding, errors=errors, newline=newline, **options
)

def getsize(self, path):
# type: (Text) -> int
@@ -306,11 +309,11 @@ def upload(self, path, file, chunk_size=None, **options):
fs, _path = self._delegate(path)
return fs.upload(_path, file, chunk_size=chunk_size, **options)

def writebytes(self, path, contents):
# type: (Text, bytes) -> None
def writebytes(self, path, contents, **options):
# type: (Text, bytes, Any) -> None
self.check()
fs, _path = self._delegate(path)
return fs.writebytes(_path, contents)
return fs.writebytes(_path, contents, **options)

def writetext(
self,
@@ -319,9 +322,15 @@ def writetext(
encoding="utf-8", # type: Text
errors=None, # type: Optional[Text]
newline="", # type: Text
**options # type: Any
):
# type: (...) -> None
fs, _path = self._delegate(path)
return fs.writetext(
_path, contents, encoding=encoding, errors=errors, newline=newline
_path,
contents,
encoding=encoding,
errors=errors,
newline=newline,
**options
)
23 changes: 13 additions & 10 deletions fs/multifs.py
Original file line number Diff line number Diff line change
@@ -280,24 +280,26 @@ def scandir(
if not exists:
raise errors.ResourceNotFound(path)

def readbytes(self, path):
# type: (Text) -> bytes
def readbytes(self, path, **options):
# type: (Text, Any) -> bytes
self.check()
fs = self._delegate(path)
if fs is None:
raise errors.ResourceNotFound(path)
return fs.readbytes(path)
return fs.readbytes(path, **options)

def download(self, path, file, chunk_size=None, **options):
# type: (Text, BinaryIO, Optional[int], **Any) -> None
fs = self._delegate_required(path)
return fs.download(path, file, chunk_size=chunk_size, **options)

def readtext(self, path, encoding=None, errors=None, newline=""):
# type: (Text, Optional[Text], Optional[Text], Text) -> Text
def readtext(self, path, encoding=None, errors=None, newline="", **options):
# type: (Text, Optional[Text], Optional[Text], Text, Any) -> Text
self.check()
fs = self._delegate_required(path)
return fs.readtext(path, encoding=encoding, errors=errors, newline=newline)
return fs.readtext(
path, encoding=encoding, errors=errors, newline=newline, **options
)

def getsize(self, path):
# type: (Text) -> int
@@ -406,9 +408,9 @@ def upload(self, path, file, chunk_size=None, **options):
path, file, chunk_size=chunk_size, **options
)

def writebytes(self, path, contents):
# type: (Text, bytes) -> None
self._writable_required(path).writebytes(path, contents)
def writebytes(self, path, contents, **options):
# type: (Text, bytes, Any) -> None
self._writable_required(path).writebytes(path, contents, **options)

def writetext(
self,
@@ -417,9 +419,10 @@ def writetext(
encoding="utf-8", # type: Text
errors=None, # type: Optional[Text]
newline="", # type: Text
**options # type: Any
):
# type: (...) -> None
write_fs = self._writable_required(path)
return write_fs.writetext(
path, contents, encoding=encoding, errors=errors, newline=newline
path, contents, encoding=encoding, errors=errors, newline=newline, **options
)
6 changes: 4 additions & 2 deletions fs/wrap.py
Original file line number Diff line number Diff line change
@@ -215,6 +215,7 @@ def writetext(
encoding="utf-8", # type: Text
errors=None, # type: Optional[Text]
newline="", # type: Text
**options # type: Any
):
# type: (...) -> None
self.check()
@@ -271,8 +272,8 @@ def open(
**options
)

def writebytes(self, path, contents):
# type: (Text, bytes) -> None
def writebytes(self, path, contents, **options):
# type: (Text, bytes, Any) -> None
self.check()
raise ResourceReadOnly(path)

@@ -288,6 +289,7 @@ def writefile(
encoding=None, # type: Optional[Text]
errors=None, # type: Optional[Text]
newline="", # type: Text
**options # type: Any
):
# type: (...) -> None
self.check()
Loading