Skip to content

Commit f814fdd

Browse files
prevent errors due to the use of seekable download strategies for seekable but not readable files
1 parent 29696c4 commit f814fdd

File tree

3 files changed

+38
-0
lines changed

3 files changed

+38
-0
lines changed

b2sdk/_internal/transfer/inbound/downloaded_file.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,15 @@ def save(self, file: BinaryIO, allow_seeking: bool | None = None) -> None:
204204
logger.warning('File is not seekable, disabling strategies that require seeking')
205205
allow_seeking = False
206206

207+
if allow_seeking: # check if file allows reading from arbitrary position
208+
try:
209+
file.read(0)
210+
except io.UnsupportedOperation:
211+
logger.warning(
212+
'File is seekable, but does not allow reads, disabling strategies that require seeking'
213+
)
214+
allow_seeking = False
215+
207216
if self.progress_listener:
208217
file = WritingStreamWithProgress(file, self.progress_listener)
209218
if self.range_ is not None:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Prevent errors due to the use of "seekable" download strategies for seekable, but not readable files.

test/unit/bucket/test_bucket.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2556,6 +2556,34 @@ def test_download_to_non_seekable_file(self):
25562556
)
25572557
assert output_file.getvalue() == self.DATA.encode()
25582558

2559+
@pytest.mark.apiver(from_ver=2)
2560+
def test_download_to_seekable_but_no_read_file(self):
2561+
file_version = self.bucket.upload_bytes(self.DATA.encode(), 'file1')
2562+
2563+
non_seekable_strategies = [
2564+
strat for strat in self.bucket.api.services.download_manager.strategies
2565+
if not isinstance(strat, ParallelDownloader)
2566+
]
2567+
context = contextlib.nullcontext() if non_seekable_strategies else pytest.raises(
2568+
ValueError,
2569+
match='no strategy suitable for download was found!',
2570+
)
2571+
output_file = io.BytesIO()
2572+
seekable_but_not_readable = io.BufferedWriter(output_file)
2573+
2574+
# test sanity check
2575+
assert seekable_but_not_readable.seekable()
2576+
with pytest.raises(io.UnsupportedOperation):
2577+
seekable_but_not_readable.read(0)
2578+
2579+
with context:
2580+
self.download_file_by_id(
2581+
file_version.id_,
2582+
v2_file=seekable_but_not_readable,
2583+
)
2584+
seekable_but_not_readable.flush()
2585+
assert output_file.getvalue() == self.DATA.encode()
2586+
25592587

25602588
# download empty file
25612589

0 commit comments

Comments
 (0)