Skip to content

Commit af4a3d4

Browse files
authored
Merge pull request #268 from reef-technologies/DownloadRefactor3
Download refactor
2 parents 1d28010 + 9f6ba8f commit af4a3d4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1514
-419
lines changed

.github/workflows/ci.yml

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,30 @@ jobs:
4444
run: python -m pip install --upgrade nox pip setuptools
4545
- name: Build the distribution
4646
run: nox -vs build
47-
test:
47+
cleanup_buckets:
4848
needs: lint
49+
env:
50+
B2_TEST_APPLICATION_KEY: ${{ secrets.B2_TEST_APPLICATION_KEY }}
51+
B2_TEST_APPLICATION_KEY_ID: ${{ secrets.B2_TEST_APPLICATION_KEY_ID }}
52+
runs-on: ubuntu-latest
53+
steps:
54+
- uses: actions/checkout@v2
55+
if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }} # TODO: skip this whole job instead
56+
with:
57+
fetch-depth: 0
58+
- name: Set up Python ${{ env.PYTHON_DEFAULT_VERSION }}
59+
if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }} # TODO: skip this whole job instead
60+
uses: actions/setup-python@v2
61+
with:
62+
python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
63+
- name: Install dependencies
64+
if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }} # TODO: skip this whole job instead
65+
run: python -m pip install --upgrade nox pip setuptools
66+
- name: Find and remove old buckets
67+
if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }} # TODO: skip this whole job instead
68+
run: nox -vs cleanup_old_buckets
69+
test:
70+
needs: cleanup_buckets
4971
env:
5072
B2_TEST_APPLICATION_KEY: ${{ secrets.B2_TEST_APPLICATION_KEY }}
5173
B2_TEST_APPLICATION_KEY_ID: ${{ secrets.B2_TEST_APPLICATION_KEY_ID }}
@@ -72,7 +94,7 @@ jobs:
7294
run: nox -vs unit
7395
- name: Run integration tests
7496
if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }}
75-
run: nox -vs integration
97+
run: nox -vs integration -- --dont-cleanup-old-buckets
7698
doc:
7799
needs: build
78100
runs-on: ubuntu-latest

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
### Changed
10+
* `download_file_*` methods refactored to allow for inspecting DownloadVersion before downloading the whole file
11+
12+
### Fixed
13+
* Fix EncryptionSetting.from_response_headers
14+
* Fix FileVersion.size and FileVersion.mod_time_millis type ambiguity
15+
* Old buckets (from past tests) are cleaned up before running integration tests in a single thread
16+
917
## [1.9.0] - 2021-06-07
1018

1119
### Added

b2sdk/_v2/__init__.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@
2525
from b2sdk.encryption.setting import EncryptionSetting
2626
from b2sdk.encryption.setting import EncryptionSettingFactory
2727
from b2sdk.encryption.setting import EncryptionKey
28-
from b2sdk.encryption.setting import SSE_NONE, SSE_B2_AES, SSE_C_KEY_ID_FILE_INFO_KEY_NAME
28+
from b2sdk.encryption.setting import SSE_NONE, SSE_B2_AES
2929
from b2sdk.encryption.types import EncryptionAlgorithm
3030
from b2sdk.encryption.types import EncryptionMode
31+
from b2sdk.http_constants import SSE_C_KEY_ID_FILE_INFO_KEY_NAME
3132

3233
# account info
3334

@@ -50,23 +51,28 @@
5051
b2_url_encode,
5152
b2_url_decode,
5253
choose_part_ranges,
54+
current_time_millis,
5355
fix_windows_path_limit,
5456
format_and_scale_fraction,
5557
format_and_scale_number,
5658
hex_sha1_of_stream,
5759
hex_sha1_of_bytes,
60+
hex_sha1_of_file,
5861
TempDir,
5962
)
6063

6164
from b2sdk.utils import trace_call
6265

6366
# data classes
6467

68+
from b2sdk.file_version import DownloadVersion
69+
from b2sdk.file_version import DownloadVersionFactory
6570
from b2sdk.file_version import FileIdAndName
6671
from b2sdk.file_version import FileVersion
6772
from b2sdk.file_version import FileVersionFactory
6873
from b2sdk.large_file.part import Part
6974
from b2sdk.large_file.unfinished_large_file import UnfinishedLargeFile
75+
from b2sdk.utils.range_ import Range
7076

7177
# file lock
7278

@@ -116,11 +122,7 @@
116122

117123
# source / destination
118124

119-
from b2sdk.download_dest import AbstractDownloadDestination
120-
from b2sdk.download_dest import DownloadDestBytes
121-
from b2sdk.download_dest import DownloadDestLocalFile
122-
from b2sdk.download_dest import DownloadDestProgressWrapper
123-
from b2sdk.download_dest import PreSeekedDownloadDest
125+
from b2sdk.transfer.inbound.downloaded_file import DownloadedFile
124126

125127
from b2sdk.transfer.outbound.outbound_source import OutboundTransferSource
126128
from b2sdk.transfer.outbound.copy_source import CopySource
@@ -136,7 +138,6 @@
136138
# trasfer
137139

138140
from b2sdk.transfer.inbound.downloader.abstract import AbstractDownloader
139-
from b2sdk.transfer.inbound.file_metadata import FileMetadata
140141
from b2sdk.transfer.outbound.large_file_upload_state import LargeFileUploadState
141142
from b2sdk.transfer.inbound.downloader.parallel import AbstractDownloaderThread
142143
from b2sdk.transfer.inbound.downloader.parallel import FirstPartDownloaderThread
@@ -145,7 +146,6 @@
145146
from b2sdk.transfer.inbound.downloader.parallel import PartToDownload
146147
from b2sdk.transfer.inbound.downloader.parallel import WriterThread
147148
from b2sdk.transfer.outbound.progress_reporter import PartProgressReporter
148-
from b2sdk.transfer.inbound.downloader.range import Range
149149
from b2sdk.transfer.inbound.downloader.simple import SimpleDownloader
150150

151151
# sync
@@ -208,4 +208,5 @@
208208
from b2sdk.cache import AuthInfoCache
209209
from b2sdk.cache import DummyCache
210210
from b2sdk.cache import InMemoryCache
211+
from b2sdk.http_constants import SRC_LAST_MODIFIED_MILLIS
211212
from b2sdk.session import B2Session

b2sdk/api.py

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,24 @@
88
#
99
######################################################################
1010

11-
from typing import Any, Dict, Optional
11+
from typing import Any, Dict, Optional, Tuple
1212

1313
from .bucket import Bucket, BucketFactory
1414
from .encryption.setting import EncryptionSetting
1515
from .exception import NonExistentBucket, RestrictedBucket
1616
from .file_lock import FileRetentionSetting, LegalHold
17-
from .file_version import FileIdAndName, FileVersionFactory
17+
from .file_version import DownloadVersionFactory, FileIdAndName, FileVersionFactory
1818
from .large_file.services import LargeFileServices
1919
from .raw_api import API_VERSION
20+
from .progress import AbstractProgressListener
2021
from .session import B2Session
2122
from .transfer import (
2223
CopyManager,
2324
DownloadManager,
2425
Emerger,
2526
UploadManager,
2627
)
28+
from .transfer.inbound.downloaded_file import DownloadedFile
2729
from .utils import B2TraceMeta, b2_url_encode, limit_trace_arguments
2830

2931

@@ -86,6 +88,7 @@ class handles several things that simplify the task of uploading
8688
BUCKET_CLASS = staticmethod(Bucket)
8789
SESSION_CLASS = staticmethod(B2Session)
8890
FILE_VERSION_FACTORY_CLASS = staticmethod(FileVersionFactory)
91+
DOWNLOAD_VERSION_FACTORY_CLASS = staticmethod(DownloadVersionFactory)
8992

9093
def __init__(
9194
self,
@@ -123,6 +126,7 @@ def __init__(
123126
"""
124127
self.session = self.SESSION_CLASS(account_info=account_info, cache=cache, raw_api=raw_api)
125128
self.file_version_factory = self.FILE_VERSION_FACTORY_CLASS(self)
129+
self.download_version_factory = self.DOWNLOAD_VERSION_FACTORY_CLASS(self)
126130
self.services = Services(
127131
self,
128132
max_upload_workers=max_upload_workers,
@@ -220,39 +224,23 @@ def create_bucket(
220224

221225
def download_file_by_id(
222226
self,
223-
file_id,
224-
download_dest,
225-
progress_listener=None,
226-
range_=None,
227+
file_id: str,
228+
progress_listener: Optional[AbstractProgressListener] = None,
229+
range_: Optional[Tuple[int, int]] = None,
227230
encryption: Optional[EncryptionSetting] = None,
228-
):
231+
) -> DownloadedFile:
229232
"""
230233
Download a file with the given ID.
231234
232235
:param str file_id: a file ID
233-
:param download_dest: an instance of the one of the following classes: \
234-
:class:`~b2sdk.v1.DownloadDestLocalFile`,\
235-
:class:`~b2sdk.v1.DownloadDestBytes`,\
236-
:class:`~b2sdk.v1.DownloadDestProgressWrapper`,\
237-
:class:`~b2sdk.v1.PreSeekedDownloadDest`,\
238-
or any sub class of :class:`~b2sdk.v1.AbstractDownloadDestination`
239-
:param progress_listener: an instance of the one of the following classes: \
240-
:class:`~b2sdk.v1.PartProgressReporter`,\
241-
:class:`~b2sdk.v1.TqdmProgressListener`,\
242-
:class:`~b2sdk.v1.SimpleProgressListener`,\
243-
:class:`~b2sdk.v1.DoNothingProgressListener`,\
244-
:class:`~b2sdk.v1.ProgressListenerForTest`,\
245-
:class:`~b2sdk.v1.SyncFileReporter`,\
246-
or any sub class of :class:`~b2sdk.v1.AbstractProgressListener`
247-
:param list range_: a list of two integers, the first one is a start\
236+
:param progress_listener: a progress listener object to use, or ``None`` to not track progress
237+
:param range_: a list of two integers, the first one is a start\
248238
position, and the second one is the end position in the file
249-
:param b2sdk.v1.EncryptionSetting encryption: encryption settings (``None`` if unknown)
250-
:return: context manager that returns an object that supports iter_content()
239+
:param encryption: encryption settings (``None`` if unknown)
251240
"""
252241
url = self.session.get_download_url_by_id(file_id)
253242
return self.services.download_manager.download_file_from_url(
254243
url,
255-
download_dest,
256244
progress_listener,
257245
range_,
258246
encryption,

b2sdk/bucket.py

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
######################################################################
1010

1111
import logging
12-
from typing import Optional
12+
from typing import Optional, Tuple
1313

1414
from .encryption.setting import EncryptionSetting, EncryptionSettingFactory
1515
from .encryption.types import EncryptionMode
@@ -21,10 +21,11 @@
2121
UNKNOWN_BUCKET_RETENTION,
2222
LegalHold,
2323
)
24-
from .file_version import FileVersion
25-
from .progress import DoNothingProgressListener
24+
from .file_version import DownloadVersion, FileVersion
25+
from .progress import AbstractProgressListener, DoNothingProgressListener
2626
from .transfer.emerge.executor import AUTO_CONTENT_TYPE
2727
from .transfer.emerge.write_intent import WriteIntent
28+
from .transfer.inbound.downloaded_file import DownloadedFile
2829
from .transfer.outbound.copy_source import CopySource
2930
from .transfer.outbound.upload_source import UploadSourceBytes, UploadSourceLocalFile
3031
from .utils import B2TraceMeta, disable_trace, limit_trace_arguments
@@ -157,67 +158,51 @@ def cancel_large_file(self, file_id):
157158

158159
def download_file_by_id(
159160
self,
160-
file_id,
161-
download_dest,
162-
progress_listener=None,
163-
range_=None,
161+
file_id: str,
162+
progress_listener: Optional[AbstractProgressListener] = None,
163+
range_: Optional[Tuple[int, int]] = None,
164164
encryption: Optional[EncryptionSetting] = None,
165-
):
165+
) -> DownloadedFile:
166166
"""
167167
Download a file by ID.
168168
169169
.. note::
170170
download_file_by_id actually belongs in :py:class:`b2sdk.v1.B2Api`, not in :py:class:`b2sdk.v1.Bucket`; we just provide a convenient redirect here
171171
172-
:param str file_id: a file ID
173-
:param download_dest: an instance of the one of the following classes: \
174-
:class:`~b2sdk.v1.DownloadDestLocalFile`,\
175-
:class:`~b2sdk.v1.DownloadDestBytes`,\
176-
:class:`~b2sdk.v1.DownloadDestProgressWrapper`,\
177-
:class:`~b2sdk.v1.PreSeekedDownloadDest`,\
178-
or any sub class of :class:`~b2sdk.v1.AbstractDownloadDestination`
179-
:param b2sdk.v1.AbstractProgressListener, None progress_listener: a progress listener object to use, or ``None`` to not report progress
180-
:param tuple[int, int] range_: two integer values, start and end offsets
181-
:param b2sdk.v1.EncryptionSetting encryption: encryption settings (``None`` if unknown)
172+
:param file_id: a file ID
173+
:param progress_listener: a progress listener object to use, or ``None`` to not track progress
174+
:param range_: two integer values, start and end offsets
175+
:param encryption: encryption settings (``None`` if unknown)
182176
"""
183177
return self.api.download_file_by_id(
184178
file_id,
185-
download_dest,
186179
progress_listener,
187180
range_=range_,
188181
encryption=encryption,
189182
)
190183

191184
def download_file_by_name(
192185
self,
193-
file_name,
194-
download_dest,
195-
progress_listener=None,
196-
range_=None,
186+
file_name: str,
187+
progress_listener: Optional[AbstractProgressListener] = None,
188+
range_: Optional[Tuple[int, int]] = None,
197189
encryption: Optional[EncryptionSetting] = None,
198-
):
190+
) -> DownloadedFile:
199191
"""
200192
Download a file by name.
201193
202194
.. seealso::
203195
204196
:ref:`Synchronizer <sync>`, a *high-performance* utility that synchronizes a local folder with a Bucket.
205197
206-
:param str file_name: a file name
207-
:param download_dest: an instance of the one of the following classes: \
208-
:class:`~b2sdk.v1.DownloadDestLocalFile`,\
209-
:class:`~b2sdk.v1.DownloadDestBytes`,\
210-
:class:`~b2sdk.v1.DownloadDestProgressWrapper`,\
211-
:class:`~b2sdk.v1.PreSeekedDownloadDest`,\
212-
or any sub class of :class:`~b2sdk.v1.AbstractDownloadDestination`
213-
:param b2sdk.v1.AbstractProgressListener, None progress_listener: a progress listener object to use, or ``None`` to not track progress
214-
:param tuple[int, int] range_: two integer values, start and end offsets
215-
:param b2sdk.v1.EncryptionSetting encryption: encryption settings (``None`` if unknown)
198+
:param file_name: a file name
199+
:param progress_listener: a progress listener object to use, or ``None`` to not track progress
200+
:param range_: two integer values, start and end offsets
201+
:param encryption: encryption settings (``None`` if unknown)
216202
"""
217203
url = self.api.session.get_download_url_by_name(self.name, file_name)
218204
return self.api.services.download_manager.download_file_from_url(
219205
url,
220-
download_dest,
221206
progress_listener,
222207
range_,
223208
encryption=encryption,
@@ -232,15 +217,14 @@ def get_file_info_by_id(self, file_id: str) -> FileVersion:
232217
"""
233218
return self.api.file_version_factory.from_api_response(self.api.get_file_info(file_id))
234219

235-
def get_file_info_by_name(self, file_name: str) -> FileVersion:
220+
def get_file_info_by_name(self, file_name: str) -> DownloadVersion:
236221
"""
237-
Gets a file version's by its name.
222+
Gets a file's DownloadVersion by name.
238223
239224
:param str file_name: the name of the file who's info will be retrieved.
240-
:rtype: generator[b2sdk.v1.FileVersionInfo]
241225
"""
242226
try:
243-
return self.api.file_version_factory.from_response_headers(
227+
return self.api.download_version_factory.from_response_headers(
244228
self.api.session.get_file_info_by_name(self.name, file_name)
245229
)
246230
except FileOrBucketNotFound:

0 commit comments

Comments
 (0)