diff --git a/doc/changes/unreleased.md b/doc/changes/unreleased.md index 88d02ee7..a14290ed 100644 --- a/doc/changes/unreleased.md +++ b/doc/changes/unreleased.md @@ -1,5 +1,9 @@ # Unreleased +## Bug Fixing + +* #262: Fixed a wrong type interpretation in `path.write`. The chunks are now passed as a Sequence, not Iterable. + ## Internal * #260: Re-locked transitive dependencies urllib3, filelock, and Werkzeug and update to exasol-toolbox 4.0.0 \ No newline at end of file diff --git a/exasol/bucketfs/_path.py b/exasol/bucketfs/_path.py index 5af0e8e5..3fca590f 100644 --- a/exasol/bucketfs/_path.py +++ b/exasol/bucketfs/_path.py @@ -6,12 +6,12 @@ ByteString, Generator, Iterable, + Sequence, ) from enum import ( Enum, auto, ) -from io import IOBase from pathlib import ( PurePath, PureWindowsPath, @@ -115,7 +115,7 @@ def read(self, chunk_size: int = 8192) -> Iterable[ByteString]: IsADirectoryError: if the pathlike object points to a directory. """ - def write(self, data: ByteString | BinaryIO | Iterable[ByteString]) -> None: + def write(self, data: ByteString | BinaryIO | Sequence[ByteString]) -> None: """ Writes data to this path. @@ -353,12 +353,10 @@ def is_file(self) -> bool: def read(self, chunk_size: int = 8192) -> Iterable[ByteString]: return self._bucket_api.download(str(self._path), chunk_size) - def write(self, data: ByteString | BinaryIO | Iterable[ByteString]) -> None: - if ( - not isinstance(data, IOBase) - and isinstance(data, Iterable) - and all(isinstance(chunk, ByteString) for chunk in data) - ): + def write(self, data: ByteString | BinaryIO | Sequence[ByteString]) -> None: + if (not isinstance(data, ByteString)) and isinstance(data, Sequence): + if not all(isinstance(chunk, ByteString) for chunk in data): + raise ValueError("The file chunks must be byte strings") data = b"".join(data) self._bucket_api.upload(str(self._path), data) diff --git a/test/integration/test_path.py b/test/integration/test_path.py index 3aa9e621..23a1d5d6 100644 --- a/test/integration/test_path.py +++ b/test/integration/test_path.py @@ -54,6 +54,29 @@ def test_write_read_back(backend_aware_bucketfs_params, children_poem): assert data_back == children_poem +def test_write_chunks_read_back( + backend_aware_bucketfs_params, children_poem, classic_poem +): + + base_path = bfs.path.build_path(**backend_aware_bucketfs_params) + file_name = "test_bucket_path/test_write_read_back/two_poems.txt" + poem_path = base_path / file_name + + poem_path.write([children_poem, classic_poem]) + data_back = b"".join(poem_path.read(20)) + assert data_back == children_poem + classic_poem + + +def test_write_chunks_error(backend_aware_bucketfs_params): + + base_path = bfs.path.build_path(**backend_aware_bucketfs_params) + file_name = "test_bucket_path/test_write_read_back/data_error.txt" + file_path = base_path / file_name + + with pytest.raises(ValueError, match="byte strings"): + file_path.write([b"part1", "part2"]) + + def test_write_read_back_tar_gz(backend_aware_bucketfs_params, children_poem, tmp_path): # Write the content into a tar.gz file