Skip to content

Commit c3cd602

Browse files
Merge pull request #270 from reef-technologies/minor-tweaks-2
Several improvements that are necessary for v2
2 parents af4a3d4 + 0c57864 commit c3cd602

Some content is hidden

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

54 files changed

+1632
-1054
lines changed

CHANGELOG.md

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

99
### Changed
1010
* `download_file_*` methods refactored to allow for inspecting DownloadVersion before downloading the whole file
11+
* `B2Api.get_file_info` returns a `FileVersion` object in v2
12+
* `B2RawApi` renamed to `B2RawHTTPApi`
13+
* `B2HTTP` tests are now common
14+
* `B2HttpApiConfig` class introduced to provide parameters like `user_agent_append` to `B2Api` without using internal classes in v2
15+
* `Bucket.update` returns a `Bucket` object in v2
16+
* `Bucket.ls` argument `show_versions` renamed to `latest_only` in v2
17+
* `B2Api` application key methods refactored to operate with dataclasses instead of dicts in v2
18+
* `B2Api.list_keys` is a generator lazily fetching all keys in v2
19+
* `account_id` and `bucket_id` added to FileVersion
1120

1221
### Fixed
1322
* Fix EncryptionSetting.from_response_headers
1423
* Fix FileVersion.size and FileVersion.mod_time_millis type ambiguity
1524
* Old buckets (from past tests) are cleaned up before running integration tests in a single thread
1625

26+
### Removed
27+
* Remove deprecated `SyncReport` methods
28+
1729
## [1.9.0] - 2021-06-07
1830

1931
### Added

b2sdk/_v2/__init__.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
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
28+
from b2sdk.encryption.setting import SSE_NONE, SSE_B2_AES, UNKNOWN_KEY_ID
2929
from b2sdk.encryption.types import EncryptionAlgorithm
3030
from b2sdk.encryption.types import EncryptionMode
3131
from b2sdk.http_constants import SSE_C_KEY_ID_FILE_INFO_KEY_NAME
@@ -65,6 +65,9 @@
6565

6666
# data classes
6767

68+
from b2sdk.application_key import ApplicationKey
69+
from b2sdk.application_key import BaseApplicationKey
70+
from b2sdk.application_key import FullApplicationKey
6871
from b2sdk.file_version import DownloadVersion
6972
from b2sdk.file_version import DownloadVersionFactory
7073
from b2sdk.file_version import FileIdAndName
@@ -109,7 +112,7 @@
109112
# raw_api
110113

111114
from b2sdk.raw_api import AbstractRawApi
112-
from b2sdk.raw_api import B2RawApi
115+
from b2sdk.raw_api import B2RawHTTPApi
113116
from b2sdk.raw_api import MetadataDirectiveMode
114117

115118
# stream
@@ -198,6 +201,8 @@
198201
# other
199202

200203
from b2sdk.b2http import B2Http
204+
from b2sdk.api_config import B2HttpApiConfig
205+
from b2sdk.api_config import DEFAULT_HTTP_API_CONFIG
201206
from b2sdk.b2http import ClockSkewHook
202207
from b2sdk.b2http import HttpCallback
203208
from b2sdk.b2http import ResponseContextManager

b2sdk/_v2/exception.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from b2sdk.exception import ClockSkew
3434
from b2sdk.exception import Conflict
3535
from b2sdk.exception import ConnectionReset
36+
from b2sdk.exception import CopyArgumentsMismatch
3637
from b2sdk.exception import DestFileNewer
3738
from b2sdk.exception import DuplicateBucketName
3839
from b2sdk.exception import FileAlreadyHidden
@@ -101,6 +102,7 @@
101102
'ClockSkew',
102103
'Conflict',
103104
'ConnectionReset',
105+
'CopyArgumentsMismatch',
104106
'CorruptAccountInfo',
105107
'DestFileNewer',
106108
'DuplicateBucketName',

b2sdk/account_info/abstract.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,6 @@ def allowed_is_valid(cls, allowed):
320320
('capabilities' in allowed) and ('namePrefix' in allowed)
321321
)
322322

323-
# TODO: make a decorator for set_auth_data()
324323
@abstractmethod
325324
def _set_auth_data(
326325
self, account_id, auth_token, api_url, download_url, recommended_part_size,

b2sdk/api.py

Lines changed: 68 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@
88
#
99
######################################################################
1010

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

13+
from .account_info.abstract import AbstractAccountInfo
14+
from .api_config import B2HttpApiConfig, DEFAULT_HTTP_API_CONFIG
15+
from .application_key import ApplicationKey, BaseApplicationKey, FullApplicationKey
16+
from .cache import AbstractCache
1317
from .bucket import Bucket, BucketFactory
1418
from .encryption.setting import EncryptionSetting
1519
from .exception import NonExistentBucket, RestrictedBucket
1620
from .file_lock import FileRetentionSetting, LegalHold
17-
from .file_version import DownloadVersionFactory, FileIdAndName, FileVersionFactory
21+
from .file_version import DownloadVersionFactory, FileIdAndName, FileVersion, FileVersionFactory
1822
from .large_file.services import LargeFileServices
1923
from .raw_api import API_VERSION
2024
from .progress import AbstractProgressListener
@@ -68,7 +72,7 @@ class B2Api(metaclass=B2TraceMeta):
6872
"""
6973
Provide file-level access to B2 services.
7074
71-
While :class:`b2sdk.v1.B2RawApi` provides direct access to the B2 web APIs, this
75+
While :class:`b2sdk.v1.B2RawHTTPApi` provides direct access to the B2 web APIs, this
7276
class handles several things that simplify the task of uploading
7377
and downloading files:
7478
@@ -89,42 +93,32 @@ class handles several things that simplify the task of uploading
8993
SESSION_CLASS = staticmethod(B2Session)
9094
FILE_VERSION_FACTORY_CLASS = staticmethod(FileVersionFactory)
9195
DOWNLOAD_VERSION_FACTORY_CLASS = staticmethod(DownloadVersionFactory)
96+
DEFAULT_LIST_KEY_COUNT = 1000
9297

9398
def __init__(
9499
self,
95-
account_info=None,
96-
cache=None,
97-
raw_api=None,
98-
max_upload_workers=10,
99-
max_copy_workers=10
100+
account_info: Optional[AbstractAccountInfo] = None,
101+
cache: Optional[AbstractCache] = None,
102+
max_upload_workers: int = 10,
103+
max_copy_workers: int = 10,
104+
api_config: B2HttpApiConfig = DEFAULT_HTTP_API_CONFIG,
100105
):
101106
"""
102107
Initialize the API using the given account info.
103108
104-
:param account_info: an instance of :class:`~b2sdk.v1.UrlPoolAccountInfo`,
105-
or any custom class derived from
106-
:class:`~b2sdk.v1.AbstractAccountInfo`
107-
To learn more about Account Info objects, see here
109+
:param account_info: To learn more about Account Info objects, see here
108110
:class:`~b2sdk.v1.SqliteAccountInfo`
109111
110-
:param cache: an instance of the one of the following classes:
111-
:class:`~b2sdk.cache.DummyCache`, :class:`~b2sdk.cache.InMemoryCache`,
112-
:class:`~b2sdk.cache.AuthInfoCache`,
113-
or any custom class derived from :class:`~b2sdk.cache.AbstractCache`
114-
It is used by B2Api to cache the mapping between bucket name and bucket ids.
112+
:param cache: It is used by B2Api to cache the mapping between bucket name and bucket ids.
115113
default is :class:`~b2sdk.cache.DummyCache`
116114
117-
:param raw_api: an instance of one of the following classes:
118-
:class:`~b2sdk.raw_api.B2RawApi`, :class:`~b2sdk.raw_simulator.RawSimulator`,
119-
or any custom class derived from :class:`~b2sdk.raw_api.AbstractRawApi`
120-
It makes network-less unit testing simple by using :class:`~b2sdk.raw_simulator.RawSimulator`,
121-
in tests and :class:`~b2sdk.raw_api.B2RawApi` in production.
122-
default is :class:`~b2sdk.raw_api.B2RawApi`
123-
124-
:param int max_upload_workers: a number of upload threads, default is 10
125-
:param int max_copy_workers: a number of copy threads, default is 10
115+
:param max_upload_workers: a number of upload threads
116+
:param max_copy_workers: a number of copy threads
117+
:param api_config:
126118
"""
127-
self.session = self.SESSION_CLASS(account_info=account_info, cache=cache, raw_api=raw_api)
119+
self.session = self.SESSION_CLASS(
120+
account_info=account_info, cache=cache, api_config=api_config
121+
)
128122
self.file_version_factory = self.FILE_VERSION_FACTORY_CLASS(self)
129123
self.download_version_factory = self.DOWNLOAD_VERSION_FACTORY_CLASS(self)
130124
self.services = Services(
@@ -145,8 +139,8 @@ def cache(self):
145139
def raw_api(self):
146140
"""
147141
.. warning::
148-
:class:`~b2sdk.raw_api.B2RawApi` attribute is deprecated.
149-
:class:`~b2sdk.session.B2Session` expose all :class:`~b2sdk.raw_api.B2RawApi` methods now."""
142+
:class:`~b2sdk.raw_api.B2RawHTTPApi` attribute is deprecated.
143+
:class:`~b2sdk.session.B2Session` expose all :class:`~b2sdk.raw_api.B2RawHTTPApi` methods now."""
150144
return self.session.raw_api
151145

152146
def authorize_automatically(self):
@@ -424,20 +418,20 @@ def get_download_url_for_file_name(self, bucket_name, file_name):
424418
# keys
425419
def create_key(
426420
self,
427-
capabilities,
428-
key_name,
429-
valid_duration_seconds=None,
430-
bucket_id=None,
431-
name_prefix=None,
421+
capabilities: List[str],
422+
key_name: str,
423+
valid_duration_seconds: Optional[int] = None,
424+
bucket_id: Optional[str] = None,
425+
name_prefix: Optional[str] = None,
432426
):
433427
"""
434428
Create a new :term:`application key`.
435429
436-
:param list capabilities: a list of capabilities
437-
:param str key_name: a name of a key
438-
:param int,None valid_duration_seconds: key auto-expire time after it is created, in seconds, or ``None`` to not expire
439-
:param str,None bucket_id: a bucket ID to restrict the key to, or ``None`` to not restrict
440-
:param str,None name_prefix: a remote filename prefix to restrict the key to or ``None`` to not restrict
430+
:param capabilities: a list of capabilities
431+
:param key_name: a name of a key
432+
:param valid_duration_seconds: key auto-expire time after it is created, in seconds, or ``None`` to not expire
433+
:param bucket_id: a bucket ID to restrict the key to, or ``None`` to not restrict
434+
:param name_prefix: a remote filename prefix to restrict the key to or ``None`` to not restrict
441435
"""
442436
account_id = self.account_info.get_account_id()
443437

@@ -453,43 +447,60 @@ def create_key(
453447
assert set(response['capabilities']) == set(capabilities)
454448
assert response['keyName'] == key_name
455449

456-
return response
450+
return FullApplicationKey.from_create_response(response)
451+
452+
def delete_key(self, application_key: BaseApplicationKey):
453+
"""
454+
Delete :term:`application key`.
455+
456+
:param application_key: an :term:`application key`
457+
"""
458+
459+
return self.delete_key_by_id(application_key.id_)
457460

458-
def delete_key(self, application_key_id):
461+
def delete_key_by_id(self, application_key_id: str):
459462
"""
460463
Delete :term:`application key`.
461464
462-
:param str application_key_id: an :term:`application key ID`
465+
:param application_key_id: an :term:`application key ID`
463466
"""
464467

465468
response = self.session.delete_key(application_key_id=application_key_id)
466-
return response
469+
return ApplicationKey.from_api_response(response)
467470

468-
def list_keys(self, start_application_key_id=None):
471+
def list_keys(self, start_application_key_id: Optional[str] = None
472+
) -> Generator[ApplicationKey, None, None]:
469473
"""
470-
List application keys.
474+
List application keys. Lazily perform requests to B2 cloud and return all keys.
471475
472-
:param str,None start_application_key_id: an :term:`application key ID` to start from or ``None`` to start from the beginning
476+
:param start_application_key_id: an :term:`application key ID` to start from or ``None`` to start from the beginning
473477
"""
474478
account_id = self.account_info.get_account_id()
475479

476-
return self.session.list_keys(
477-
account_id, max_key_count=1000, start_application_key_id=start_application_key_id
478-
)
480+
while True:
481+
response = self.session.list_keys(
482+
account_id,
483+
max_key_count=self.DEFAULT_LIST_KEY_COUNT,
484+
start_application_key_id=start_application_key_id,
485+
)
486+
for entry in response['keys']:
487+
yield ApplicationKey.from_api_response(entry)
488+
489+
next_application_key_id = response['nextApplicationKeyId']
490+
if next_application_key_id is None:
491+
return
492+
start_application_key_id = next_application_key_id
479493

480494
# other
481-
def get_file_info(self, file_id: str) -> Dict[str, Any]:
495+
def get_file_info(self, file_id: str) -> FileVersion:
482496
"""
483-
Legacy interface which just returns whatever remote API returns.
484-
485-
.. todo::
486-
get_file_info() should return a File with .delete(), copy(), rename(), read() and so on
497+
Gets info about file version.
487498
488499
:param str file_id: the id of the file who's info will be retrieved.
489-
:return: The parsed response
490-
:rtype: dict
491500
"""
492-
return self.session.get_file_info_by_id(file_id)
501+
return self.file_version_factory.from_api_response(
502+
self.session.get_file_info_by_id(file_id)
503+
)
493504

494505
def check_bucket_name_restrictions(self, bucket_name: str):
495506
"""

b2sdk/api_config.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
######################################################################
2+
#
3+
# File: b2sdk/api_config.py
4+
#
5+
# Copyright 2021 Backblaze Inc. All Rights Reserved.
6+
#
7+
# License https://www.backblaze.com/using_b2_code.html
8+
#
9+
######################################################################
10+
11+
from typing import Optional, Callable, Type
12+
import requests
13+
14+
from .raw_api import AbstractRawApi, B2RawHTTPApi
15+
16+
17+
class B2HttpApiConfig:
18+
19+
DEFAULT_RAW_API_CLASS = B2RawHTTPApi
20+
21+
def __init__(
22+
self,
23+
http_session_factory: Callable[[], requests.Session] = requests.Session,
24+
install_clock_skew_hook: bool = True,
25+
user_agent_append: Optional[str] = None,
26+
_raw_api_class: Optional[Type[AbstractRawApi]] = None,
27+
):
28+
"""
29+
A structure with params to be passed to low level API.
30+
31+
:param http_session_factory: a callable that returns a requests.Session object (or a compatible one)
32+
:param install_clock_skew_hook: if True, install a clock skew hook
33+
:param user_agent_append: if provided, the string will be appended to the User-Agent
34+
:param _raw_api_class: AbstractRawApi-compliant class
35+
"""
36+
self.http_session_factory = http_session_factory
37+
self.install_clock_skew_hook = install_clock_skew_hook
38+
self.user_agent_append = user_agent_append
39+
self.raw_api_class = _raw_api_class or self.DEFAULT_RAW_API_CLASS
40+
41+
42+
DEFAULT_HTTP_API_CONFIG = B2HttpApiConfig()

0 commit comments

Comments
 (0)