azure-storage-blob 12.21.0__py3-none-any.whl → 12.22.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- azure/storage/blob/__init__.py +19 -18
- azure/storage/blob/_blob_client.py +417 -1507
- azure/storage/blob/_blob_client_helpers.py +1242 -0
- azure/storage/blob/_blob_service_client.py +82 -101
- azure/storage/blob/_blob_service_client_helpers.py +27 -0
- azure/storage/blob/_container_client.py +147 -356
- azure/storage/blob/_container_client_helpers.py +261 -0
- azure/storage/blob/_deserialize.py +68 -44
- azure/storage/blob/_download.py +114 -90
- azure/storage/blob/_encryption.py +14 -7
- azure/storage/blob/_lease.py +47 -58
- azure/storage/blob/_list_blobs_helper.py +129 -135
- azure/storage/blob/_models.py +479 -276
- azure/storage/blob/_quick_query_helper.py +30 -31
- azure/storage/blob/_serialize.py +38 -56
- azure/storage/blob/_shared/avro/datafile.py +1 -1
- azure/storage/blob/_shared/avro/datafile_async.py +1 -1
- azure/storage/blob/_shared/base_client.py +1 -1
- azure/storage/blob/_shared/base_client_async.py +1 -1
- azure/storage/blob/_shared/policies.py +8 -6
- azure/storage/blob/_shared/policies_async.py +3 -1
- azure/storage/blob/_shared/response_handlers.py +6 -2
- azure/storage/blob/_shared/shared_access_signature.py +2 -2
- azure/storage/blob/_shared/uploads.py +1 -1
- azure/storage/blob/_shared/uploads_async.py +1 -1
- azure/storage/blob/_shared_access_signature.py +70 -53
- azure/storage/blob/_upload_helpers.py +75 -68
- azure/storage/blob/_version.py +1 -1
- azure/storage/blob/aio/__init__.py +19 -11
- azure/storage/blob/aio/_blob_client_async.py +505 -255
- azure/storage/blob/aio/_blob_service_client_async.py +138 -87
- azure/storage/blob/aio/_container_client_async.py +260 -120
- azure/storage/blob/aio/_download_async.py +104 -87
- azure/storage/blob/aio/_lease_async.py +56 -55
- azure/storage/blob/aio/_list_blobs_helper.py +94 -96
- azure/storage/blob/aio/_models.py +60 -38
- azure/storage/blob/aio/_upload_helpers.py +75 -66
- {azure_storage_blob-12.21.0.dist-info → azure_storage_blob-12.22.0.dist-info}/METADATA +1 -1
- {azure_storage_blob-12.21.0.dist-info → azure_storage_blob-12.22.0.dist-info}/RECORD +42 -39
- {azure_storage_blob-12.21.0.dist-info → azure_storage_blob-12.22.0.dist-info}/LICENSE +0 -0
- {azure_storage_blob-12.21.0.dist-info → azure_storage_blob-12.22.0.dist-info}/WHEEL +0 -0
- {azure_storage_blob-12.21.0.dist-info → azure_storage_blob-12.22.0.dist-info}/top_level.txt +0 -0
@@ -5,86 +5,89 @@
|
|
5
5
|
# --------------------------------------------------------------------------
|
6
6
|
# pylint: disable=too-many-lines, docstring-keyword-should-match-keyword-only
|
7
7
|
|
8
|
+
import warnings
|
9
|
+
from datetime import datetime
|
8
10
|
from functools import partial
|
9
|
-
from io import BytesIO
|
10
11
|
from typing import (
|
11
|
-
Any, AnyStr,
|
12
|
+
Any, AnyStr, cast, Dict, IO, Iterable, List, Optional, overload, Tuple, Union,
|
12
13
|
TYPE_CHECKING
|
13
14
|
)
|
14
|
-
from urllib.parse import urlparse, quote, unquote
|
15
|
-
import warnings
|
16
|
-
|
17
15
|
from typing_extensions import Self
|
18
16
|
|
19
|
-
from azure.core.exceptions import
|
17
|
+
from azure.core.exceptions import HttpResponseError, ResourceExistsError, ResourceNotFoundError
|
20
18
|
from azure.core.paging import ItemPaged
|
21
19
|
from azure.core.pipeline import Pipeline
|
22
20
|
from azure.core.tracing.decorator import distributed_trace
|
23
|
-
from .
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
21
|
+
from ._blob_client_helpers import (
|
22
|
+
_abort_copy_options,
|
23
|
+
_append_block_from_url_options,
|
24
|
+
_append_block_options,
|
25
|
+
_clear_page_options,
|
26
|
+
_commit_block_list_options,
|
27
|
+
_create_append_blob_options,
|
28
|
+
_create_page_blob_options,
|
29
|
+
_create_snapshot_options,
|
30
|
+
_delete_blob_options,
|
31
|
+
_download_blob_options,
|
32
|
+
_format_url,
|
33
|
+
_from_blob_url,
|
34
|
+
_get_blob_tags_options,
|
35
|
+
_get_block_list_result,
|
36
|
+
_get_page_ranges_options,
|
37
|
+
_parse_url,
|
38
|
+
_quick_query_options,
|
39
|
+
_resize_blob_options,
|
40
|
+
_seal_append_blob_options,
|
41
|
+
_set_blob_metadata_options,
|
42
|
+
_set_blob_tags_options,
|
43
|
+
_set_http_headers_options,
|
44
|
+
_set_sequence_number_options,
|
45
|
+
_stage_block_from_url_options,
|
46
|
+
_stage_block_options,
|
47
|
+
_start_copy_from_url_options,
|
48
|
+
_upload_blob_from_url_options,
|
49
|
+
_upload_blob_options,
|
50
|
+
_upload_page_options,
|
51
|
+
_upload_pages_from_url_options
|
51
52
|
)
|
52
53
|
from ._deserialize import (
|
53
|
-
get_page_ranges_result,
|
54
54
|
deserialize_blob_properties,
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
deserialize_pipeline_response_into_cls,
|
56
|
+
get_page_ranges_result,
|
57
|
+
parse_tags
|
58
58
|
)
|
59
59
|
from ._download import StorageStreamDownloader
|
60
|
-
from ._encryption import
|
60
|
+
from ._encryption import StorageEncryptionMixin, _ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION
|
61
|
+
from ._generated import AzureBlobStorage
|
62
|
+
from ._generated.models import CpkInfo
|
61
63
|
from ._lease import BlobLeaseClient
|
62
|
-
from ._models import
|
63
|
-
DelimitedJsonDialect, DelimitedTextDialect, PageRangePaged, PageRange
|
64
|
+
from ._models import BlobBlock, BlobProperties, BlobQueryError, BlobType, PageRange, PageRangePaged
|
64
65
|
from ._quick_query_helper import BlobQueryReader
|
66
|
+
from ._shared.base_client import parse_connection_str, StorageAccountHostsMixin, TransportWrapper
|
67
|
+
from ._shared.response_handlers import process_storage_error, return_response_headers
|
68
|
+
from ._serialize import (
|
69
|
+
get_access_conditions,
|
70
|
+
get_api_version,
|
71
|
+
get_modify_conditions,
|
72
|
+
get_version_id
|
73
|
+
)
|
65
74
|
from ._upload_helpers import (
|
66
|
-
upload_block_blob,
|
67
75
|
upload_append_blob,
|
68
|
-
|
69
|
-
|
76
|
+
upload_block_blob,
|
77
|
+
upload_page_blob
|
70
78
|
)
|
71
79
|
|
72
80
|
if TYPE_CHECKING:
|
73
81
|
from azure.core.credentials import AzureNamedKeyCredential, AzureSasCredential, TokenCredential
|
74
|
-
from
|
75
|
-
from ._generated.models import BlockList
|
82
|
+
from azure.storage.blob import ContainerClient
|
76
83
|
from ._models import (
|
77
84
|
ContentSettings,
|
78
85
|
ImmutabilityPolicy,
|
79
86
|
PremiumPageBlobTier,
|
80
|
-
|
81
|
-
|
87
|
+
SequenceNumberAction,
|
88
|
+
StandardBlobTier
|
82
89
|
)
|
83
90
|
|
84
|
-
_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION = (
|
85
|
-
'The require_encryption flag is set, but encryption is not supported'
|
86
|
-
' for this method.')
|
87
|
-
|
88
91
|
|
89
92
|
class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: disable=too-many-public-methods
|
90
93
|
"""A client to interact with a specific blob, although that blob may not yet exist.
|
@@ -157,36 +160,26 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
157
160
|
:caption: Creating the BlobClient from a SAS URL to a blob.
|
158
161
|
"""
|
159
162
|
def __init__(
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
raise ValueError("Account URL must be a string.") from exc
|
172
|
-
parsed_url = urlparse(account_url.rstrip('/'))
|
173
|
-
|
174
|
-
if not (container_name and blob_name):
|
175
|
-
raise ValueError("Please specify a container name and blob name.")
|
176
|
-
if not parsed_url.netloc:
|
177
|
-
raise ValueError(f"Invalid URL: {account_url}")
|
178
|
-
|
179
|
-
path_snapshot, sas_token = parse_query(parsed_url.query)
|
180
|
-
|
163
|
+
self, account_url: str,
|
164
|
+
container_name: str,
|
165
|
+
blob_name: str,
|
166
|
+
snapshot: Optional[Union[str, Dict[str, Any]]] = None,
|
167
|
+
credential: Optional[Union[str, Dict[str, str], "AzureNamedKeyCredential", "AzureSasCredential", "TokenCredential"]] = None, # pylint: disable=line-too-long
|
168
|
+
**kwargs: Any
|
169
|
+
) -> None:
|
170
|
+
parsed_url, sas_token, path_snapshot = _parse_url(
|
171
|
+
account_url=account_url,
|
172
|
+
container_name=container_name,
|
173
|
+
blob_name=blob_name)
|
181
174
|
self.container_name = container_name
|
182
175
|
self.blob_name = blob_name
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
176
|
+
|
177
|
+
if snapshot is not None and hasattr(snapshot, 'snapshot'):
|
178
|
+
self.snapshot = snapshot.snapshot
|
179
|
+
elif isinstance(snapshot, dict):
|
180
|
+
self.snapshot = snapshot['snapshot']
|
181
|
+
else:
|
182
|
+
self.snapshot = snapshot or path_snapshot
|
190
183
|
self.version_id = kwargs.pop('version_id', None)
|
191
184
|
|
192
185
|
# This parameter is used for the hierarchy traversal. Give precedence to credential.
|
@@ -194,33 +187,25 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
194
187
|
self._query_str, credential = self._format_query_string(sas_token, credential, snapshot=self.snapshot)
|
195
188
|
super(BlobClient, self).__init__(parsed_url, service='blob', credential=credential, **kwargs)
|
196
189
|
self._client = AzureBlobStorage(self.url, base_url=self.url, pipeline=self._pipeline)
|
197
|
-
self._client._config.version = get_api_version(kwargs) # pylint: disable=protected-access
|
190
|
+
self._client._config.version = get_api_version(kwargs) # type: ignore [assignment] # pylint: disable=protected-access
|
198
191
|
self._configure_encryption(kwargs)
|
199
192
|
|
200
|
-
def _format_url(self, hostname):
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
source_scheme = parsed_source_url.scheme
|
209
|
-
source_hostname = parsed_source_url.netloc.rstrip('/')
|
210
|
-
source_path = unquote(parsed_source_url.path)
|
211
|
-
source_query = parsed_source_url.query
|
212
|
-
result = [f"{source_scheme}://{source_hostname}{quote(source_path, safe='~/')}"]
|
213
|
-
if source_query:
|
214
|
-
result.append(source_query)
|
215
|
-
return '?'.join(result)
|
193
|
+
def _format_url(self, hostname: str) -> str:
|
194
|
+
return _format_url(
|
195
|
+
container_name=self.container_name,
|
196
|
+
scheme=self.scheme,
|
197
|
+
blob_name=self.blob_name,
|
198
|
+
query_str=self._query_str,
|
199
|
+
hostname=hostname
|
200
|
+
)
|
216
201
|
|
217
202
|
@classmethod
|
218
203
|
def from_blob_url(
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
204
|
+
cls, blob_url: str,
|
205
|
+
credential: Optional[Union[str, Dict[str, str], "AzureNamedKeyCredential", "AzureSasCredential", "TokenCredential"]] = None, # pylint: disable=line-too-long
|
206
|
+
snapshot: Optional[Union[str, Dict[str, Any]]] = None,
|
207
|
+
**kwargs: Any
|
208
|
+
) -> Self:
|
224
209
|
"""Create BlobClient from a blob url. This doesn't support customized blob url with '/' in blob name.
|
225
210
|
|
226
211
|
:param str blob_url:
|
@@ -254,48 +239,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
254
239
|
:returns: A Blob client.
|
255
240
|
:rtype: ~azure.storage.blob.BlobClient
|
256
241
|
"""
|
257
|
-
|
258
|
-
if not blob_url.lower().startswith('http'):
|
259
|
-
blob_url = "https://" + blob_url
|
260
|
-
except AttributeError as exc:
|
261
|
-
raise ValueError("Blob URL must be a string.") from exc
|
262
|
-
parsed_url = urlparse(blob_url.rstrip('/'))
|
263
|
-
|
264
|
-
if not parsed_url.netloc:
|
265
|
-
raise ValueError(f"Invalid URL: {blob_url}")
|
266
|
-
|
267
|
-
account_path = ""
|
268
|
-
if ".core." in parsed_url.netloc:
|
269
|
-
# .core. is indicating non-customized url. Blob name with directory info can also be parsed.
|
270
|
-
path_blob = parsed_url.path.lstrip('/').split('/', maxsplit=1)
|
271
|
-
elif "localhost" in parsed_url.netloc or "127.0.0.1" in parsed_url.netloc:
|
272
|
-
path_blob = parsed_url.path.lstrip('/').split('/', maxsplit=2)
|
273
|
-
account_path += '/' + path_blob[0]
|
274
|
-
else:
|
275
|
-
# for customized url. blob name that has directory info cannot be parsed.
|
276
|
-
path_blob = parsed_url.path.lstrip('/').split('/')
|
277
|
-
if len(path_blob) > 2:
|
278
|
-
account_path = "/" + "/".join(path_blob[:-2])
|
279
|
-
|
280
|
-
account_url = f"{parsed_url.scheme}://{parsed_url.netloc.rstrip('/')}{account_path}?{parsed_url.query}"
|
281
|
-
|
282
|
-
msg_invalid_url = "Invalid URL. Provide a blob_url with a valid blob and container name."
|
283
|
-
if len(path_blob) <= 1:
|
284
|
-
raise ValueError(msg_invalid_url)
|
285
|
-
container_name, blob_name = unquote(path_blob[-2]), unquote(path_blob[-1])
|
286
|
-
if not container_name or not blob_name:
|
287
|
-
raise ValueError(msg_invalid_url)
|
288
|
-
|
289
|
-
path_snapshot, _ = parse_query(parsed_url.query)
|
290
|
-
if snapshot:
|
291
|
-
try:
|
292
|
-
path_snapshot = snapshot.snapshot # type: ignore
|
293
|
-
except AttributeError:
|
294
|
-
try:
|
295
|
-
path_snapshot = snapshot['snapshot'] # type: ignore
|
296
|
-
except TypeError:
|
297
|
-
path_snapshot = snapshot
|
298
|
-
|
242
|
+
account_url, container_name, blob_name, path_snapshot = _from_blob_url(blob_url=blob_url, snapshot=snapshot)
|
299
243
|
return cls(
|
300
244
|
account_url, container_name=container_name, blob_name=blob_name,
|
301
245
|
snapshot=path_snapshot, credential=credential, **kwargs
|
@@ -303,13 +247,13 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
303
247
|
|
304
248
|
@classmethod
|
305
249
|
def from_connection_string(
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
250
|
+
cls, conn_str: str,
|
251
|
+
container_name: str,
|
252
|
+
blob_name: str,
|
253
|
+
snapshot: Optional[Union[str, Dict[str, Any]]] = None,
|
254
|
+
credential: Optional[Union[str, Dict[str, str], "AzureNamedKeyCredential", "AzureSasCredential", "TokenCredential"]] = None, # pylint: disable=line-too-long
|
255
|
+
**kwargs: Any
|
256
|
+
) -> Self:
|
313
257
|
"""Create BlobClient from a Connection String.
|
314
258
|
|
315
259
|
:param str conn_str:
|
@@ -361,8 +305,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
361
305
|
)
|
362
306
|
|
363
307
|
@distributed_trace
|
364
|
-
def get_account_information(self, **kwargs):
|
365
|
-
# type: (**Any) -> Dict[str, str]
|
308
|
+
def get_account_information(self, **kwargs: Any) -> Dict[str, str]:
|
366
309
|
"""Gets information related to the storage account in which the blob resides.
|
367
310
|
|
368
311
|
The information can also be retrieved if the user has a SAS to a container or blob.
|
@@ -372,149 +315,12 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
372
315
|
:rtype: dict(str, str)
|
373
316
|
"""
|
374
317
|
try:
|
375
|
-
return self._client.blob.get_account_info(cls=return_response_headers, **kwargs)
|
318
|
+
return cast(Dict[str, str], self._client.blob.get_account_info(cls=return_response_headers, **kwargs))
|
376
319
|
except HttpResponseError as error:
|
377
320
|
process_storage_error(error)
|
378
321
|
|
379
|
-
def _upload_blob_options( # pylint:disable=too-many-statements
|
380
|
-
self, data: Union[bytes, str, Iterable[AnyStr], AsyncIterable[AnyStr], IO[AnyStr]],
|
381
|
-
blob_type: Union[str, BlobType] = BlobType.BlockBlob,
|
382
|
-
length: Optional[int] = None,
|
383
|
-
metadata: Optional[Dict[str, str]] = None,
|
384
|
-
**kwargs
|
385
|
-
) -> Dict[str, Any]:
|
386
|
-
if self.require_encryption and not self.key_encryption_key:
|
387
|
-
raise ValueError("Encryption required but no key was provided.")
|
388
|
-
encryption_options = {
|
389
|
-
'required': self.require_encryption,
|
390
|
-
'version': self.encryption_version,
|
391
|
-
'key': self.key_encryption_key,
|
392
|
-
'resolver': self.key_resolver_function,
|
393
|
-
}
|
394
|
-
|
395
|
-
encoding = kwargs.pop('encoding', 'UTF-8')
|
396
|
-
if isinstance(data, str):
|
397
|
-
data = data.encode(encoding)
|
398
|
-
if length is None:
|
399
|
-
length = get_length(data)
|
400
|
-
if isinstance(data, bytes):
|
401
|
-
data = data[:length]
|
402
|
-
|
403
|
-
if isinstance(data, bytes):
|
404
|
-
stream = BytesIO(data)
|
405
|
-
elif hasattr(data, 'read'):
|
406
|
-
stream = data
|
407
|
-
elif hasattr(data, '__iter__') and not isinstance(data, (list, tuple, set, dict)):
|
408
|
-
stream = IterStreamer(data, encoding=encoding)
|
409
|
-
elif hasattr(data, '__aiter__'):
|
410
|
-
stream = AsyncIterStreamer(data, encoding=encoding)
|
411
|
-
else:
|
412
|
-
raise TypeError(f"Unsupported data type: {type(data)}")
|
413
|
-
|
414
|
-
validate_content = kwargs.pop('validate_content', False)
|
415
|
-
content_settings = kwargs.pop('content_settings', None)
|
416
|
-
overwrite = kwargs.pop('overwrite', False)
|
417
|
-
max_concurrency = kwargs.pop('max_concurrency', 1)
|
418
|
-
cpk = kwargs.pop('cpk', None)
|
419
|
-
cpk_info = None
|
420
|
-
if cpk:
|
421
|
-
if self.scheme.lower() != 'https':
|
422
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
423
|
-
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
|
424
|
-
encryption_algorithm=cpk.algorithm)
|
425
|
-
kwargs['cpk_info'] = cpk_info
|
426
|
-
|
427
|
-
headers = kwargs.pop('headers', {})
|
428
|
-
headers.update(add_metadata_headers(metadata))
|
429
|
-
kwargs['lease_access_conditions'] = get_access_conditions(kwargs.pop('lease', None))
|
430
|
-
kwargs['modified_access_conditions'] = get_modify_conditions(kwargs)
|
431
|
-
kwargs['cpk_scope_info'] = get_cpk_scope_info(kwargs)
|
432
|
-
if content_settings:
|
433
|
-
kwargs['blob_headers'] = BlobHTTPHeaders(
|
434
|
-
blob_cache_control=content_settings.cache_control,
|
435
|
-
blob_content_type=content_settings.content_type,
|
436
|
-
blob_content_md5=content_settings.content_md5,
|
437
|
-
blob_content_encoding=content_settings.content_encoding,
|
438
|
-
blob_content_language=content_settings.content_language,
|
439
|
-
blob_content_disposition=content_settings.content_disposition
|
440
|
-
)
|
441
|
-
kwargs['blob_tags_string'] = serialize_blob_tags_header(kwargs.pop('tags', None))
|
442
|
-
kwargs['stream'] = stream
|
443
|
-
kwargs['length'] = length
|
444
|
-
kwargs['overwrite'] = overwrite
|
445
|
-
kwargs['headers'] = headers
|
446
|
-
kwargs['validate_content'] = validate_content
|
447
|
-
kwargs['blob_settings'] = self._config
|
448
|
-
kwargs['max_concurrency'] = max_concurrency
|
449
|
-
kwargs['encryption_options'] = encryption_options
|
450
|
-
# Add feature flag to user agent for encryption
|
451
|
-
if self.key_encryption_key:
|
452
|
-
modify_user_agent_for_encryption(
|
453
|
-
self._config.user_agent_policy.user_agent,
|
454
|
-
self._sdk_moniker,
|
455
|
-
self.encryption_version,
|
456
|
-
kwargs)
|
457
|
-
|
458
|
-
if blob_type == BlobType.BlockBlob:
|
459
|
-
kwargs['client'] = self._client.block_blob
|
460
|
-
elif blob_type == BlobType.PageBlob:
|
461
|
-
if self.encryption_version == '2.0' and (self.require_encryption or self.key_encryption_key is not None):
|
462
|
-
raise ValueError("Encryption version 2.0 does not currently support page blobs.")
|
463
|
-
kwargs['client'] = self._client.page_blob
|
464
|
-
elif blob_type == BlobType.AppendBlob:
|
465
|
-
if self.require_encryption or (self.key_encryption_key is not None):
|
466
|
-
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
467
|
-
kwargs['client'] = self._client.append_blob
|
468
|
-
else:
|
469
|
-
raise ValueError(f"Unsupported BlobType: {blob_type}")
|
470
|
-
return kwargs
|
471
|
-
|
472
|
-
def _upload_blob_from_url_options(self, source_url, **kwargs):
|
473
|
-
# type: (...) -> Dict[str, Any]
|
474
|
-
tier = kwargs.pop('standard_blob_tier', None)
|
475
|
-
overwrite = kwargs.pop('overwrite', False)
|
476
|
-
content_settings = kwargs.pop('content_settings', None)
|
477
|
-
source_authorization = kwargs.pop('source_authorization', None)
|
478
|
-
if content_settings:
|
479
|
-
kwargs['blob_http_headers'] = BlobHTTPHeaders(
|
480
|
-
blob_cache_control=content_settings.cache_control,
|
481
|
-
blob_content_type=content_settings.content_type,
|
482
|
-
blob_content_md5=None,
|
483
|
-
blob_content_encoding=content_settings.content_encoding,
|
484
|
-
blob_content_language=content_settings.content_language,
|
485
|
-
blob_content_disposition=content_settings.content_disposition
|
486
|
-
)
|
487
|
-
cpk = kwargs.pop('cpk', None)
|
488
|
-
cpk_info = None
|
489
|
-
if cpk:
|
490
|
-
if self.scheme.lower() != 'https':
|
491
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
492
|
-
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
|
493
|
-
encryption_algorithm=cpk.algorithm)
|
494
|
-
|
495
|
-
options = {
|
496
|
-
'copy_source_authorization': source_authorization,
|
497
|
-
'content_length': 0,
|
498
|
-
'copy_source_blob_properties': kwargs.pop('include_source_blob_properties', True),
|
499
|
-
'source_content_md5': kwargs.pop('source_content_md5', None),
|
500
|
-
'copy_source': source_url,
|
501
|
-
'modified_access_conditions': get_modify_conditions(kwargs),
|
502
|
-
'blob_tags_string': serialize_blob_tags_header(kwargs.pop('tags', None)),
|
503
|
-
'cls': return_response_headers,
|
504
|
-
'lease_access_conditions': get_access_conditions(kwargs.pop('destination_lease', None)),
|
505
|
-
'tier': tier.value if tier else None,
|
506
|
-
'source_modified_access_conditions': get_source_conditions(kwargs),
|
507
|
-
'cpk_info': cpk_info,
|
508
|
-
'cpk_scope_info': get_cpk_scope_info(kwargs)
|
509
|
-
}
|
510
|
-
options.update(kwargs)
|
511
|
-
if not overwrite and not _any_conditions(**options): # pylint: disable=protected-access
|
512
|
-
options['modified_access_conditions'].if_none_match = '*'
|
513
|
-
return options
|
514
|
-
|
515
322
|
@distributed_trace
|
516
|
-
def upload_blob_from_url(self, source_url, **kwargs):
|
517
|
-
# type: (str, Any) -> Dict[str, Any]
|
323
|
+
def upload_blob_from_url(self, source_url: str, **kwargs: Any) -> Dict[str, Any]:
|
518
324
|
"""
|
519
325
|
Creates a new Block Blob where the content of the blob is read from a given URL.
|
520
326
|
The content of an existing blob is overwritten with the new blob.
|
@@ -609,25 +415,27 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
609
415
|
:keyword str source_authorization:
|
610
416
|
Authenticate as a service principal using a client secret to access a source blob. Ensure "bearer " is
|
611
417
|
the prefix of the source_authorization string.
|
612
|
-
:returns:
|
418
|
+
:returns: Blob-updated property Dict (Etag and last modified)
|
613
419
|
:rtype: Dict[str, Any]
|
614
420
|
"""
|
615
|
-
|
616
|
-
|
421
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
422
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
423
|
+
options = _upload_blob_from_url_options(
|
424
|
+
source_url=source_url,
|
617
425
|
**kwargs)
|
618
426
|
try:
|
619
|
-
return self._client.block_blob.put_blob_from_url(**options)
|
427
|
+
return cast(Dict[str, Any], self._client.block_blob.put_blob_from_url(**options))
|
620
428
|
except HttpResponseError as error:
|
621
429
|
process_storage_error(error)
|
622
430
|
|
623
431
|
@distributed_trace
|
624
432
|
def upload_blob(
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
433
|
+
self, data: Union[bytes, str, Iterable[AnyStr], IO[bytes]],
|
434
|
+
blob_type: Union[str, BlobType] = BlobType.BLOCKBLOB,
|
435
|
+
length: Optional[int] = None,
|
436
|
+
metadata: Optional[Dict[str, str]] = None,
|
437
|
+
**kwargs: Any
|
438
|
+
) -> Dict[str, Any]:
|
631
439
|
"""Creates a new blob from a data source with automatic chunking.
|
632
440
|
|
633
441
|
:param data: The blob data to upload.
|
@@ -753,8 +561,8 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
753
561
|
see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
|
754
562
|
#other-client--per-operation-configuration>`__. This method may make multiple calls to the service and
|
755
563
|
the timeout will apply to each call individually.
|
756
|
-
:returns: Blob-updated property
|
757
|
-
:rtype:
|
564
|
+
:returns: Blob-updated property Dict (Etag and last modified)
|
565
|
+
:rtype: Dict[str, Any]
|
758
566
|
|
759
567
|
.. admonition:: Example:
|
760
568
|
|
@@ -765,11 +573,24 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
765
573
|
:dedent: 12
|
766
574
|
:caption: Upload a blob to the container.
|
767
575
|
"""
|
768
|
-
|
769
|
-
|
576
|
+
if self.require_encryption and not self.key_encryption_key:
|
577
|
+
raise ValueError("Encryption required but no key was provided.")
|
578
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
579
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
580
|
+
options = _upload_blob_options(
|
581
|
+
data=data,
|
770
582
|
blob_type=blob_type,
|
771
583
|
length=length,
|
772
584
|
metadata=metadata,
|
585
|
+
encryption_options={
|
586
|
+
'required': self.require_encryption,
|
587
|
+
'version': self.encryption_version,
|
588
|
+
'key': self.key_encryption_key,
|
589
|
+
'resolver': self.key_resolver_function
|
590
|
+
},
|
591
|
+
config=self._config,
|
592
|
+
sdk_moniker=self._sdk_moniker,
|
593
|
+
client=self._client,
|
773
594
|
**kwargs)
|
774
595
|
if blob_type == BlobType.BlockBlob:
|
775
596
|
return upload_block_blob(**options)
|
@@ -777,84 +598,34 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
777
598
|
return upload_page_blob(**options)
|
778
599
|
return upload_append_blob(**options)
|
779
600
|
|
780
|
-
def _download_blob_options(self, offset=None, length=None, encoding=None, **kwargs):
|
781
|
-
# type: (Optional[int], Optional[int], Optional[str], **Any) -> Dict[str, Any]
|
782
|
-
if self.require_encryption and not (self.key_encryption_key or self.key_resolver_function):
|
783
|
-
raise ValueError("Encryption required but no key was provided.")
|
784
|
-
if length is not None and offset is None:
|
785
|
-
raise ValueError("Offset value must not be None if length is set.")
|
786
|
-
if length is not None:
|
787
|
-
length = offset + length - 1 # Service actually uses an end-range inclusive index
|
788
|
-
|
789
|
-
validate_content = kwargs.pop('validate_content', False)
|
790
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
791
|
-
mod_conditions = get_modify_conditions(kwargs)
|
792
|
-
version_id = get_version_id(self.version_id, kwargs)
|
793
|
-
|
794
|
-
cpk = kwargs.pop('cpk', None)
|
795
|
-
cpk_info = None
|
796
|
-
if cpk:
|
797
|
-
if self.scheme.lower() != 'https':
|
798
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
799
|
-
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
|
800
|
-
encryption_algorithm=cpk.algorithm)
|
801
|
-
|
802
|
-
# Add feature flag to user agent for encryption
|
803
|
-
if self.key_encryption_key or self.key_resolver_function:
|
804
|
-
modify_user_agent_for_encryption(
|
805
|
-
self._config.user_agent_policy.user_agent,
|
806
|
-
self._sdk_moniker,
|
807
|
-
self.encryption_version,
|
808
|
-
kwargs)
|
809
|
-
|
810
|
-
options = {
|
811
|
-
'clients': self._client,
|
812
|
-
'config': self._config,
|
813
|
-
'start_range': offset,
|
814
|
-
'end_range': length,
|
815
|
-
'version_id': version_id,
|
816
|
-
'validate_content': validate_content,
|
817
|
-
'encryption_options': {
|
818
|
-
'required': self.require_encryption,
|
819
|
-
'key': self.key_encryption_key,
|
820
|
-
'resolver': self.key_resolver_function},
|
821
|
-
'lease_access_conditions': access_conditions,
|
822
|
-
'modified_access_conditions': mod_conditions,
|
823
|
-
'cpk_info': cpk_info,
|
824
|
-
'download_cls': kwargs.pop('cls', None) or deserialize_blob_stream,
|
825
|
-
'max_concurrency':kwargs.pop('max_concurrency', 1),
|
826
|
-
'encoding': encoding,
|
827
|
-
'timeout': kwargs.pop('timeout', None),
|
828
|
-
'name': self.blob_name,
|
829
|
-
'container': self.container_name}
|
830
|
-
options.update(kwargs)
|
831
|
-
return options
|
832
|
-
|
833
601
|
@overload
|
834
602
|
def download_blob(
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
603
|
+
self, offset: Optional[int] = None,
|
604
|
+
length: Optional[int] = None,
|
605
|
+
*,
|
606
|
+
encoding: str,
|
607
|
+
**kwargs: Any
|
608
|
+
) -> StorageStreamDownloader[str]:
|
840
609
|
...
|
841
610
|
|
842
611
|
@overload
|
843
612
|
def download_blob(
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
613
|
+
self, offset: Optional[int] = None,
|
614
|
+
length: Optional[int] = None,
|
615
|
+
*,
|
616
|
+
encoding: None = None,
|
617
|
+
**kwargs: Any
|
618
|
+
) -> StorageStreamDownloader[bytes]:
|
849
619
|
...
|
850
620
|
|
851
621
|
@distributed_trace
|
852
622
|
def download_blob(
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
623
|
+
self, offset: Optional[int] = None,
|
624
|
+
length: Optional[int] = None,
|
625
|
+
*,
|
626
|
+
encoding: Union[str, None] = None,
|
627
|
+
**kwargs: Any
|
628
|
+
) -> Union[StorageStreamDownloader[str], StorageStreamDownloader[bytes]]:
|
858
629
|
"""Downloads a blob to the StorageStreamDownloader. The readall() method must
|
859
630
|
be used to read all the content or readinto() must be used to download the blob into
|
860
631
|
a stream. Using chunks() returns an iterator which allows the user to iterate over the content in chunks.
|
@@ -917,7 +688,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
917
688
|
a secure connection must be established to transfer the key.
|
918
689
|
:keyword int max_concurrency:
|
919
690
|
The number of parallel connections with which to download.
|
920
|
-
:keyword str encoding:
|
691
|
+
:keyword Optional[str] encoding:
|
921
692
|
Encoding to decode the downloaded bytes. Default is None, i.e. no decoding.
|
922
693
|
:keyword progress_hook:
|
923
694
|
A callback to track the progress of a long running download. The signature is
|
@@ -945,82 +716,33 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
945
716
|
:dedent: 12
|
946
717
|
:caption: Download a blob.
|
947
718
|
"""
|
948
|
-
|
719
|
+
if self.require_encryption and not (self.key_encryption_key or self.key_resolver_function):
|
720
|
+
raise ValueError("Encryption required but no key was provided.")
|
721
|
+
if length is not None and offset is None:
|
722
|
+
raise ValueError("Offset value must not be None if length is set.")
|
723
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
724
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
725
|
+
options = _download_blob_options(
|
726
|
+
blob_name=self.blob_name,
|
727
|
+
container_name=self.container_name,
|
728
|
+
version_id=get_version_id(self.version_id, kwargs),
|
949
729
|
offset=offset,
|
950
730
|
length=length,
|
951
731
|
encoding=encoding,
|
732
|
+
encryption_options={
|
733
|
+
'required': self.require_encryption,
|
734
|
+
'version': self.encryption_version,
|
735
|
+
'key': self.key_encryption_key,
|
736
|
+
'resolver': self.key_resolver_function
|
737
|
+
},
|
738
|
+
config=self._config,
|
739
|
+
sdk_moniker=self._sdk_moniker,
|
740
|
+
client=self._client,
|
952
741
|
**kwargs)
|
953
742
|
return StorageStreamDownloader(**options)
|
954
743
|
|
955
|
-
def _quick_query_options(self, query_expression,
|
956
|
-
**kwargs):
|
957
|
-
# type: (str, **Any) -> Dict[str, Any]
|
958
|
-
delimiter = '\n'
|
959
|
-
input_format = kwargs.pop('blob_format', None)
|
960
|
-
if input_format == QuickQueryDialect.DelimitedJson:
|
961
|
-
input_format = DelimitedJsonDialect()
|
962
|
-
if input_format == QuickQueryDialect.DelimitedText:
|
963
|
-
input_format = DelimitedTextDialect()
|
964
|
-
input_parquet_format = input_format == "ParquetDialect"
|
965
|
-
if input_format and not input_parquet_format:
|
966
|
-
try:
|
967
|
-
delimiter = input_format.lineterminator
|
968
|
-
except AttributeError:
|
969
|
-
try:
|
970
|
-
delimiter = input_format.delimiter
|
971
|
-
except AttributeError as exc:
|
972
|
-
raise ValueError("The Type of blob_format can only be DelimitedTextDialect or "
|
973
|
-
"DelimitedJsonDialect or ParquetDialect") from exc
|
974
|
-
output_format = kwargs.pop('output_format', None)
|
975
|
-
if output_format == QuickQueryDialect.DelimitedJson:
|
976
|
-
output_format = DelimitedJsonDialect()
|
977
|
-
if output_format == QuickQueryDialect.DelimitedText:
|
978
|
-
output_format = DelimitedTextDialect()
|
979
|
-
if output_format:
|
980
|
-
if output_format == "ParquetDialect":
|
981
|
-
raise ValueError("ParquetDialect is invalid as an output format.")
|
982
|
-
try:
|
983
|
-
delimiter = output_format.lineterminator
|
984
|
-
except AttributeError:
|
985
|
-
try:
|
986
|
-
delimiter = output_format.delimiter
|
987
|
-
except AttributeError:
|
988
|
-
pass
|
989
|
-
else:
|
990
|
-
output_format = input_format if not input_parquet_format else None
|
991
|
-
query_request = QueryRequest(
|
992
|
-
expression=query_expression,
|
993
|
-
input_serialization=serialize_query_format(input_format),
|
994
|
-
output_serialization=serialize_query_format(output_format)
|
995
|
-
)
|
996
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
997
|
-
mod_conditions = get_modify_conditions(kwargs)
|
998
|
-
|
999
|
-
cpk = kwargs.pop('cpk', None)
|
1000
|
-
cpk_info = None
|
1001
|
-
if cpk:
|
1002
|
-
if self.scheme.lower() != 'https':
|
1003
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
1004
|
-
cpk_info = CpkInfo(
|
1005
|
-
encryption_key=cpk.key_value,
|
1006
|
-
encryption_key_sha256=cpk.key_hash,
|
1007
|
-
encryption_algorithm=cpk.algorithm
|
1008
|
-
)
|
1009
|
-
options = {
|
1010
|
-
'query_request': query_request,
|
1011
|
-
'lease_access_conditions': access_conditions,
|
1012
|
-
'modified_access_conditions': mod_conditions,
|
1013
|
-
'cpk_info': cpk_info,
|
1014
|
-
'snapshot': self.snapshot,
|
1015
|
-
'timeout': kwargs.pop('timeout', None),
|
1016
|
-
'cls': return_headers_and_deserialized,
|
1017
|
-
}
|
1018
|
-
options.update(kwargs)
|
1019
|
-
return options, delimiter
|
1020
|
-
|
1021
744
|
@distributed_trace
|
1022
|
-
def query_blob(self, query_expression, **kwargs):
|
1023
|
-
# type: (str, **Any) -> BlobQueryReader
|
745
|
+
def query_blob(self, query_expression: str, **kwargs: Any) -> BlobQueryReader:
|
1024
746
|
"""Enables users to select/project on blob/or blob snapshot data by providing simple query expressions.
|
1025
747
|
This operations returns a BlobQueryReader, users need to use readall() or readinto() to get query data.
|
1026
748
|
|
@@ -1047,7 +769,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
1047
769
|
This value can be a DelimitedTextDialect or a DelimitedJsonDialect or ArrowDialect.
|
1048
770
|
These dialects can be passed through their respective classes, the QuickQueryDialect enum or as a string
|
1049
771
|
:paramtype output_format: ~azure.storage.blob.DelimitedTextDialect or ~azure.storage.blob.DelimitedJsonDialect
|
1050
|
-
or
|
772
|
+
or List[~azure.storage.blob.ArrowDialect] or ~azure.storage.blob.QuickQueryDialect or str
|
1051
773
|
:keyword lease:
|
1052
774
|
Required if the blob has an active lease. Value can be a BlobLeaseClient object
|
1053
775
|
or the lease ID as a string.
|
@@ -1101,7 +823,9 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
1101
823
|
errors = kwargs.pop("on_error", None)
|
1102
824
|
error_cls = kwargs.pop("error_cls", BlobQueryError)
|
1103
825
|
encoding = kwargs.pop("encoding", None)
|
1104
|
-
|
826
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
827
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
828
|
+
options, delimiter = _quick_query_options(self.snapshot, query_expression, **kwargs)
|
1105
829
|
try:
|
1106
830
|
headers, raw_response_body = self._client.blob.query(**options)
|
1107
831
|
except HttpResponseError as error:
|
@@ -1116,36 +840,8 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
1116
840
|
response=raw_response_body,
|
1117
841
|
error_cls=error_cls)
|
1118
842
|
|
1119
|
-
@staticmethod
|
1120
|
-
def _generic_delete_blob_options(delete_snapshots=None, **kwargs):
|
1121
|
-
# type: (str, **Any) -> Dict[str, Any]
|
1122
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
1123
|
-
mod_conditions = get_modify_conditions(kwargs)
|
1124
|
-
if delete_snapshots:
|
1125
|
-
delete_snapshots = DeleteSnapshotsOptionType(delete_snapshots)
|
1126
|
-
options = {
|
1127
|
-
'timeout': kwargs.pop('timeout', None),
|
1128
|
-
'snapshot': kwargs.pop('snapshot', None), # this is added for delete_blobs
|
1129
|
-
'delete_snapshots': delete_snapshots or None,
|
1130
|
-
'lease_access_conditions': access_conditions,
|
1131
|
-
'modified_access_conditions': mod_conditions}
|
1132
|
-
options.update(kwargs)
|
1133
|
-
return options
|
1134
|
-
|
1135
|
-
def _delete_blob_options(self, delete_snapshots=None, **kwargs):
|
1136
|
-
# type: (str, **Any) -> Dict[str, Any]
|
1137
|
-
if self.snapshot and delete_snapshots:
|
1138
|
-
raise ValueError("The delete_snapshots option cannot be used with a specific snapshot.")
|
1139
|
-
version_id = get_version_id(self.version_id, kwargs)
|
1140
|
-
options = self._generic_delete_blob_options(delete_snapshots, **kwargs)
|
1141
|
-
options['snapshot'] = self.snapshot
|
1142
|
-
options['version_id'] = version_id
|
1143
|
-
options['blob_delete_type'] = kwargs.pop('blob_delete_type', None)
|
1144
|
-
return options
|
1145
|
-
|
1146
843
|
@distributed_trace
|
1147
|
-
def delete_blob(self, delete_snapshots=None, **kwargs):
|
1148
|
-
# type: (str, **Any) -> None
|
844
|
+
def delete_blob(self, delete_snapshots: Optional[str] = None, **kwargs: Any) -> None:
|
1149
845
|
"""Marks the specified blob for deletion.
|
1150
846
|
|
1151
847
|
The blob is later deleted during garbage collection.
|
@@ -1159,11 +855,11 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
1159
855
|
Soft deleted blob is accessible through :func:`~ContainerClient.list_blobs()` specifying `include=['deleted']`
|
1160
856
|
option. Soft-deleted blob can be restored using :func:`undelete` operation.
|
1161
857
|
|
1162
|
-
:param str delete_snapshots:
|
858
|
+
:param Optional[str] delete_snapshots:
|
1163
859
|
Required if the blob has associated snapshots. Values include:
|
1164
860
|
- "only": Deletes only the blobs snapshots.
|
1165
861
|
- "include": Deletes the blob along with all snapshots.
|
1166
|
-
:keyword str version_id:
|
862
|
+
:keyword Optional[str] version_id:
|
1167
863
|
The version id parameter is an opaque DateTime
|
1168
864
|
value that, when present, specifies the version of the blob to delete.
|
1169
865
|
|
@@ -1216,15 +912,18 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
1216
912
|
:dedent: 12
|
1217
913
|
:caption: Delete a blob.
|
1218
914
|
"""
|
1219
|
-
options =
|
915
|
+
options = _delete_blob_options(
|
916
|
+
snapshot=self.snapshot,
|
917
|
+
version_id=get_version_id(self.version_id, kwargs),
|
918
|
+
delete_snapshots=delete_snapshots,
|
919
|
+
**kwargs)
|
1220
920
|
try:
|
1221
921
|
self._client.blob.delete(**options)
|
1222
922
|
except HttpResponseError as error:
|
1223
923
|
process_storage_error(error)
|
1224
924
|
|
1225
925
|
@distributed_trace
|
1226
|
-
def undelete_blob(self, **kwargs):
|
1227
|
-
# type: (**Any) -> None
|
926
|
+
def undelete_blob(self, **kwargs: Any) -> None:
|
1228
927
|
"""Restores soft-deleted blobs or snapshots.
|
1229
928
|
|
1230
929
|
Operation will only be successful if used within the specified number of days
|
@@ -1257,8 +956,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
1257
956
|
process_storage_error(error)
|
1258
957
|
|
1259
958
|
@distributed_trace
|
1260
|
-
def exists(self, **kwargs):
|
1261
|
-
# type: (**Any) -> bool
|
959
|
+
def exists(self, **kwargs: Any) -> bool:
|
1262
960
|
"""
|
1263
961
|
Returns True if a blob exists with the defined parameters, and returns
|
1264
962
|
False otherwise.
|
@@ -1292,8 +990,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
1292
990
|
return False
|
1293
991
|
|
1294
992
|
@distributed_trace
|
1295
|
-
def get_blob_properties(self, **kwargs):
|
1296
|
-
# type: (**Any) -> BlobProperties
|
993
|
+
def get_blob_properties(self, **kwargs: Any) -> BlobProperties:
|
1297
994
|
"""Returns all user-defined metadata, standard HTTP properties, and
|
1298
995
|
system properties for the blob. It does not return the content of the blob.
|
1299
996
|
|
@@ -1370,7 +1067,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
1370
1067
|
cls_method = kwargs.pop('cls', None)
|
1371
1068
|
if cls_method:
|
1372
1069
|
kwargs['cls'] = partial(deserialize_pipeline_response_into_cls, cls_method)
|
1373
|
-
blob_props = self._client.blob.get_properties(
|
1070
|
+
blob_props = cast(BlobProperties, self._client.blob.get_properties(
|
1374
1071
|
timeout=kwargs.pop('timeout', None),
|
1375
1072
|
version_id=version_id,
|
1376
1073
|
snapshot=self.snapshot,
|
@@ -1378,41 +1075,17 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
1378
1075
|
modified_access_conditions=mod_conditions,
|
1379
1076
|
cls=kwargs.pop('cls', None) or deserialize_blob_properties,
|
1380
1077
|
cpk_info=cpk_info,
|
1381
|
-
**kwargs)
|
1078
|
+
**kwargs))
|
1382
1079
|
except HttpResponseError as error:
|
1383
1080
|
process_storage_error(error)
|
1384
1081
|
blob_props.name = self.blob_name
|
1385
1082
|
if isinstance(blob_props, BlobProperties):
|
1386
1083
|
blob_props.container = self.container_name
|
1387
1084
|
blob_props.snapshot = self.snapshot
|
1388
|
-
return blob_props
|
1389
|
-
|
1390
|
-
def _set_http_headers_options(self, content_settings=None, **kwargs):
|
1391
|
-
# type: (Optional[ContentSettings], **Any) -> Dict[str, Any]
|
1392
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
1393
|
-
mod_conditions = get_modify_conditions(kwargs)
|
1394
|
-
blob_headers = None
|
1395
|
-
if content_settings:
|
1396
|
-
blob_headers = BlobHTTPHeaders(
|
1397
|
-
blob_cache_control=content_settings.cache_control,
|
1398
|
-
blob_content_type=content_settings.content_type,
|
1399
|
-
blob_content_md5=content_settings.content_md5,
|
1400
|
-
blob_content_encoding=content_settings.content_encoding,
|
1401
|
-
blob_content_language=content_settings.content_language,
|
1402
|
-
blob_content_disposition=content_settings.content_disposition
|
1403
|
-
)
|
1404
|
-
options = {
|
1405
|
-
'timeout': kwargs.pop('timeout', None),
|
1406
|
-
'blob_http_headers': blob_headers,
|
1407
|
-
'lease_access_conditions': access_conditions,
|
1408
|
-
'modified_access_conditions': mod_conditions,
|
1409
|
-
'cls': return_response_headers}
|
1410
|
-
options.update(kwargs)
|
1411
|
-
return options
|
1085
|
+
return blob_props
|
1412
1086
|
|
1413
1087
|
@distributed_trace
|
1414
|
-
def set_http_headers(self, content_settings=None, **kwargs):
|
1415
|
-
# type: (Optional[ContentSettings], **Any) -> None
|
1088
|
+
def set_http_headers(self, content_settings: Optional["ContentSettings"] = None, **kwargs: Any) -> Dict[str, Any]:
|
1416
1089
|
"""Sets system properties on the blob.
|
1417
1090
|
|
1418
1091
|
If one property is set for the content_settings, all properties will be overridden.
|
@@ -1456,41 +1129,17 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
1456
1129
|
:returns: Blob-updated property dict (Etag and last modified)
|
1457
1130
|
:rtype: Dict[str, Any]
|
1458
1131
|
"""
|
1459
|
-
options =
|
1132
|
+
options = _set_http_headers_options(content_settings=content_settings, **kwargs)
|
1460
1133
|
try:
|
1461
|
-
return self._client.blob.set_http_headers(**options)
|
1134
|
+
return cast(Dict[str, Any], self._client.blob.set_http_headers(**options))
|
1462
1135
|
except HttpResponseError as error:
|
1463
1136
|
process_storage_error(error)
|
1464
1137
|
|
1465
|
-
def _set_blob_metadata_options(self, metadata=None, **kwargs):
|
1466
|
-
# type: (Optional[Dict[str, str]], **Any) -> Dict[str, Any]
|
1467
|
-
headers = kwargs.pop('headers', {})
|
1468
|
-
headers.update(add_metadata_headers(metadata))
|
1469
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
1470
|
-
mod_conditions = get_modify_conditions(kwargs)
|
1471
|
-
cpk_scope_info = get_cpk_scope_info(kwargs)
|
1472
|
-
|
1473
|
-
cpk = kwargs.pop('cpk', None)
|
1474
|
-
cpk_info = None
|
1475
|
-
if cpk:
|
1476
|
-
if self.scheme.lower() != 'https':
|
1477
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
1478
|
-
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
|
1479
|
-
encryption_algorithm=cpk.algorithm)
|
1480
|
-
options = {
|
1481
|
-
'timeout': kwargs.pop('timeout', None),
|
1482
|
-
'lease_access_conditions': access_conditions,
|
1483
|
-
'modified_access_conditions': mod_conditions,
|
1484
|
-
'cpk_scope_info': cpk_scope_info,
|
1485
|
-
'cpk_info': cpk_info,
|
1486
|
-
'cls': return_response_headers,
|
1487
|
-
'headers': headers}
|
1488
|
-
options.update(kwargs)
|
1489
|
-
return options
|
1490
|
-
|
1491
1138
|
@distributed_trace
|
1492
|
-
def set_blob_metadata(
|
1493
|
-
|
1139
|
+
def set_blob_metadata(
|
1140
|
+
self, metadata: Optional[Dict[str, str]] = None,
|
1141
|
+
**kwargs: Any
|
1142
|
+
) -> Dict[str, Union[str, datetime]]:
|
1494
1143
|
"""Sets user-defined metadata for the blob as one or more name-value pairs.
|
1495
1144
|
|
1496
1145
|
:param metadata:
|
@@ -1547,15 +1196,19 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
1547
1196
|
:returns: Blob-updated property dict (Etag and last modified)
|
1548
1197
|
:rtype: Dict[str, Union[str, datetime]]
|
1549
1198
|
"""
|
1550
|
-
|
1199
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
1200
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
1201
|
+
options = _set_blob_metadata_options(metadata=metadata, **kwargs)
|
1551
1202
|
try:
|
1552
|
-
return self._client.blob.set_metadata(**options)
|
1203
|
+
return cast(Dict[str, Union[str, datetime]], self._client.blob.set_metadata(**options))
|
1553
1204
|
except HttpResponseError as error:
|
1554
1205
|
process_storage_error(error)
|
1555
1206
|
|
1556
1207
|
@distributed_trace
|
1557
|
-
def set_immutability_policy(
|
1558
|
-
|
1208
|
+
def set_immutability_policy(
|
1209
|
+
self, immutability_policy: "ImmutabilityPolicy",
|
1210
|
+
**kwargs: Any
|
1211
|
+
) -> Dict[str, str]:
|
1559
1212
|
"""The Set Immutability Policy operation sets the immutability policy on the blob.
|
1560
1213
|
|
1561
1214
|
.. versionadded:: 12.10.0
|
@@ -1579,11 +1232,10 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
1579
1232
|
|
1580
1233
|
kwargs['immutability_policy_expiry'] = immutability_policy.expiry_time
|
1581
1234
|
kwargs['immutability_policy_mode'] = immutability_policy.policy_mode
|
1582
|
-
return self._client.blob.set_immutability_policy(cls=return_response_headers, **kwargs)
|
1235
|
+
return cast(Dict[str, str], self._client.blob.set_immutability_policy(cls=return_response_headers, **kwargs))
|
1583
1236
|
|
1584
1237
|
@distributed_trace
|
1585
|
-
def delete_immutability_policy(self, **kwargs):
|
1586
|
-
# type: (**Any) -> None
|
1238
|
+
def delete_immutability_policy(self, **kwargs: Any) -> None:
|
1587
1239
|
"""The Delete Immutability Policy operation deletes the immutability policy on the blob.
|
1588
1240
|
|
1589
1241
|
.. versionadded:: 12.10.0
|
@@ -1602,8 +1254,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
1602
1254
|
self._client.blob.delete_immutability_policy(**kwargs)
|
1603
1255
|
|
1604
1256
|
@distributed_trace
|
1605
|
-
def set_legal_hold(self, legal_hold, **kwargs):
|
1606
|
-
# type: (bool, **Any) -> Dict[str, Union[str, datetime, bool]]
|
1257
|
+
def set_legal_hold(self, legal_hold: bool, **kwargs: Any) -> Dict[str, Union[str, datetime, bool]]:
|
1607
1258
|
"""The Set Legal Hold operation sets a legal hold on the blob.
|
1608
1259
|
|
1609
1260
|
.. versionadded:: 12.10.0
|
@@ -1621,83 +1272,17 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
1621
1272
|
:rtype: Dict[str, Union[str, datetime, bool]]
|
1622
1273
|
"""
|
1623
1274
|
|
1624
|
-
return
|
1625
|
-
|
1626
|
-
def _create_page_blob_options( # type: ignore
|
1627
|
-
self, size, # type: int
|
1628
|
-
content_settings=None, # type: Optional[ContentSettings]
|
1629
|
-
metadata=None, # type: Optional[Dict[str, str]]
|
1630
|
-
premium_page_blob_tier=None, # type: Optional[Union[str, PremiumPageBlobTier]]
|
1631
|
-
**kwargs
|
1632
|
-
):
|
1633
|
-
# type: (...) -> Dict[str, Any]
|
1634
|
-
if self.require_encryption or (self.key_encryption_key is not None):
|
1635
|
-
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
1636
|
-
headers = kwargs.pop('headers', {})
|
1637
|
-
headers.update(add_metadata_headers(metadata))
|
1638
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
1639
|
-
mod_conditions = get_modify_conditions(kwargs)
|
1640
|
-
cpk_scope_info = get_cpk_scope_info(kwargs)
|
1641
|
-
blob_headers = None
|
1642
|
-
if content_settings:
|
1643
|
-
blob_headers = BlobHTTPHeaders(
|
1644
|
-
blob_cache_control=content_settings.cache_control,
|
1645
|
-
blob_content_type=content_settings.content_type,
|
1646
|
-
blob_content_md5=content_settings.content_md5,
|
1647
|
-
blob_content_encoding=content_settings.content_encoding,
|
1648
|
-
blob_content_language=content_settings.content_language,
|
1649
|
-
blob_content_disposition=content_settings.content_disposition
|
1650
|
-
)
|
1651
|
-
|
1652
|
-
sequence_number = kwargs.pop('sequence_number', None)
|
1653
|
-
cpk = kwargs.pop('cpk', None)
|
1654
|
-
cpk_info = None
|
1655
|
-
if cpk:
|
1656
|
-
if self.scheme.lower() != 'https':
|
1657
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
1658
|
-
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
|
1659
|
-
encryption_algorithm=cpk.algorithm)
|
1660
|
-
|
1661
|
-
immutability_policy = kwargs.pop('immutability_policy', None)
|
1662
|
-
if immutability_policy:
|
1663
|
-
kwargs['immutability_policy_expiry'] = immutability_policy.expiry_time
|
1664
|
-
kwargs['immutability_policy_mode'] = immutability_policy.policy_mode
|
1665
|
-
|
1666
|
-
tier = None
|
1667
|
-
if premium_page_blob_tier:
|
1668
|
-
try:
|
1669
|
-
tier = premium_page_blob_tier.value # type: ignore
|
1670
|
-
except AttributeError:
|
1671
|
-
tier = premium_page_blob_tier # type: ignore
|
1672
|
-
|
1673
|
-
blob_tags_string = serialize_blob_tags_header(kwargs.pop('tags', None))
|
1674
|
-
|
1675
|
-
options = {
|
1676
|
-
'content_length': 0,
|
1677
|
-
'blob_content_length': size,
|
1678
|
-
'blob_sequence_number': sequence_number,
|
1679
|
-
'blob_http_headers': blob_headers,
|
1680
|
-
'timeout': kwargs.pop('timeout', None),
|
1681
|
-
'lease_access_conditions': access_conditions,
|
1682
|
-
'modified_access_conditions': mod_conditions,
|
1683
|
-
'cpk_scope_info': cpk_scope_info,
|
1684
|
-
'cpk_info': cpk_info,
|
1685
|
-
'blob_tags_string': blob_tags_string,
|
1686
|
-
'cls': return_response_headers,
|
1687
|
-
"tier": tier,
|
1688
|
-
'headers': headers}
|
1689
|
-
options.update(kwargs)
|
1690
|
-
return options
|
1275
|
+
return cast(Dict[str, Union[str, datetime, bool]],
|
1276
|
+
self._client.blob.set_legal_hold(legal_hold, cls=return_response_headers, **kwargs))
|
1691
1277
|
|
1692
1278
|
@distributed_trace
|
1693
|
-
def create_page_blob(
|
1694
|
-
|
1695
|
-
|
1696
|
-
|
1697
|
-
|
1698
|
-
|
1699
|
-
|
1700
|
-
# type: (...) -> Dict[str, Union[str, datetime]]
|
1279
|
+
def create_page_blob(
|
1280
|
+
self, size: int,
|
1281
|
+
content_settings: Optional["ContentSettings"] = None,
|
1282
|
+
metadata: Optional[Dict[str, str]] = None,
|
1283
|
+
premium_page_blob_tier: Optional[Union[str, "PremiumPageBlobTier"]] = None,
|
1284
|
+
**kwargs: Any
|
1285
|
+
) -> Dict[str, Union[str, datetime]]:
|
1701
1286
|
"""Creates a new Page Blob of the specified size.
|
1702
1287
|
|
1703
1288
|
:param int size:
|
@@ -1782,69 +1367,27 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
1782
1367
|
:returns: Blob-updated property dict (Etag and last modified).
|
1783
1368
|
:rtype: dict[str, Any]
|
1784
1369
|
"""
|
1785
|
-
|
1786
|
-
|
1370
|
+
if self.require_encryption or (self.key_encryption_key is not None):
|
1371
|
+
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
1372
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
1373
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
1374
|
+
options = _create_page_blob_options(
|
1375
|
+
size=size,
|
1787
1376
|
content_settings=content_settings,
|
1788
1377
|
metadata=metadata,
|
1789
1378
|
premium_page_blob_tier=premium_page_blob_tier,
|
1790
1379
|
**kwargs)
|
1791
1380
|
try:
|
1792
|
-
return self._client.page_blob.create(**options)
|
1381
|
+
return cast(Dict[str, Any], self._client.page_blob.create(**options))
|
1793
1382
|
except HttpResponseError as error:
|
1794
1383
|
process_storage_error(error)
|
1795
1384
|
|
1796
|
-
def _create_append_blob_options(self, content_settings=None, metadata=None, **kwargs):
|
1797
|
-
# type: (Optional[ContentSettings], Optional[Dict[str, str]], **Any) -> Dict[str, Any]
|
1798
|
-
if self.require_encryption or (self.key_encryption_key is not None):
|
1799
|
-
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
1800
|
-
headers = kwargs.pop('headers', {})
|
1801
|
-
headers.update(add_metadata_headers(metadata))
|
1802
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
1803
|
-
mod_conditions = get_modify_conditions(kwargs)
|
1804
|
-
cpk_scope_info = get_cpk_scope_info(kwargs)
|
1805
|
-
blob_headers = None
|
1806
|
-
if content_settings:
|
1807
|
-
blob_headers = BlobHTTPHeaders(
|
1808
|
-
blob_cache_control=content_settings.cache_control,
|
1809
|
-
blob_content_type=content_settings.content_type,
|
1810
|
-
blob_content_md5=content_settings.content_md5,
|
1811
|
-
blob_content_encoding=content_settings.content_encoding,
|
1812
|
-
blob_content_language=content_settings.content_language,
|
1813
|
-
blob_content_disposition=content_settings.content_disposition
|
1814
|
-
)
|
1815
|
-
|
1816
|
-
cpk = kwargs.pop('cpk', None)
|
1817
|
-
cpk_info = None
|
1818
|
-
if cpk:
|
1819
|
-
if self.scheme.lower() != 'https':
|
1820
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
1821
|
-
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
|
1822
|
-
encryption_algorithm=cpk.algorithm)
|
1823
|
-
|
1824
|
-
immutability_policy = kwargs.pop('immutability_policy', None)
|
1825
|
-
if immutability_policy:
|
1826
|
-
kwargs['immutability_policy_expiry'] = immutability_policy.expiry_time
|
1827
|
-
kwargs['immutability_policy_mode'] = immutability_policy.policy_mode
|
1828
|
-
|
1829
|
-
blob_tags_string = serialize_blob_tags_header(kwargs.pop('tags', None))
|
1830
|
-
|
1831
|
-
options = {
|
1832
|
-
'content_length': 0,
|
1833
|
-
'blob_http_headers': blob_headers,
|
1834
|
-
'timeout': kwargs.pop('timeout', None),
|
1835
|
-
'lease_access_conditions': access_conditions,
|
1836
|
-
'modified_access_conditions': mod_conditions,
|
1837
|
-
'cpk_scope_info': cpk_scope_info,
|
1838
|
-
'cpk_info': cpk_info,
|
1839
|
-
'blob_tags_string': blob_tags_string,
|
1840
|
-
'cls': return_response_headers,
|
1841
|
-
'headers': headers}
|
1842
|
-
options.update(kwargs)
|
1843
|
-
return options
|
1844
|
-
|
1845
1385
|
@distributed_trace
|
1846
|
-
def create_append_blob(
|
1847
|
-
|
1386
|
+
def create_append_blob(
|
1387
|
+
self, content_settings: Optional["ContentSettings"] = None,
|
1388
|
+
metadata: Optional[Dict[str, str]] = None,
|
1389
|
+
**kwargs: Any
|
1390
|
+
) -> Dict[str, Union[str, datetime]]:
|
1848
1391
|
"""Creates a new Append Blob. This operation creates a new 0-length append blob. The content
|
1849
1392
|
of any existing blob is overwritten with the newly initialized append blob. To add content to
|
1850
1393
|
the append blob, call the :func:`append_block` or :func:`append_block_from_url` method.
|
@@ -1920,44 +1463,24 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
1920
1463
|
:returns: Blob-updated property dict (Etag and last modified).
|
1921
1464
|
:rtype: dict[str, Any]
|
1922
1465
|
"""
|
1923
|
-
|
1466
|
+
if self.require_encryption or (self.key_encryption_key is not None):
|
1467
|
+
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
1468
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
1469
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
1470
|
+
options = _create_append_blob_options(
|
1924
1471
|
content_settings=content_settings,
|
1925
1472
|
metadata=metadata,
|
1926
1473
|
**kwargs)
|
1927
1474
|
try:
|
1928
|
-
return self._client.append_blob.create(**options)
|
1475
|
+
return cast(Dict[str, Union[str, datetime]], self._client.append_blob.create(**options))
|
1929
1476
|
except HttpResponseError as error:
|
1930
1477
|
process_storage_error(error)
|
1931
1478
|
|
1932
|
-
def _create_snapshot_options(self, metadata=None, **kwargs):
|
1933
|
-
# type: (Optional[Dict[str, str]], **Any) -> Dict[str, Any]
|
1934
|
-
headers = kwargs.pop('headers', {})
|
1935
|
-
headers.update(add_metadata_headers(metadata))
|
1936
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
1937
|
-
mod_conditions = get_modify_conditions(kwargs)
|
1938
|
-
cpk_scope_info = get_cpk_scope_info(kwargs)
|
1939
|
-
cpk = kwargs.pop('cpk', None)
|
1940
|
-
cpk_info = None
|
1941
|
-
if cpk:
|
1942
|
-
if self.scheme.lower() != 'https':
|
1943
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
1944
|
-
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
|
1945
|
-
encryption_algorithm=cpk.algorithm)
|
1946
|
-
|
1947
|
-
options = {
|
1948
|
-
'timeout': kwargs.pop('timeout', None),
|
1949
|
-
'lease_access_conditions': access_conditions,
|
1950
|
-
'modified_access_conditions': mod_conditions,
|
1951
|
-
'cpk_scope_info': cpk_scope_info,
|
1952
|
-
'cpk_info': cpk_info,
|
1953
|
-
'cls': return_response_headers,
|
1954
|
-
'headers': headers}
|
1955
|
-
options.update(kwargs)
|
1956
|
-
return options
|
1957
|
-
|
1958
1479
|
@distributed_trace
|
1959
|
-
def create_snapshot(
|
1960
|
-
|
1480
|
+
def create_snapshot(
|
1481
|
+
self, metadata: Optional[Dict[str, str]] = None,
|
1482
|
+
**kwargs: Any
|
1483
|
+
) -> Dict[str, Union[str, datetime]]:
|
1961
1484
|
"""Creates a snapshot of the blob.
|
1962
1485
|
|
1963
1486
|
A snapshot is a read-only version of a blob that's taken at a point in time.
|
@@ -2028,92 +1551,21 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
2028
1551
|
:dedent: 8
|
2029
1552
|
:caption: Create a snapshot of the blob.
|
2030
1553
|
"""
|
2031
|
-
|
1554
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
1555
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
1556
|
+
options = _create_snapshot_options(metadata=metadata, **kwargs)
|
2032
1557
|
try:
|
2033
|
-
return self._client.blob.create_snapshot(**options)
|
1558
|
+
return cast(Dict[str, Any], self._client.blob.create_snapshot(**options))
|
2034
1559
|
except HttpResponseError as error:
|
2035
1560
|
process_storage_error(error)
|
2036
1561
|
|
2037
|
-
def _start_copy_from_url_options(self, source_url, metadata=None, incremental_copy=False, **kwargs):
|
2038
|
-
# type: (str, Optional[Dict[str, str]], bool, **Any) -> Dict[str, Any]
|
2039
|
-
headers = kwargs.pop('headers', {})
|
2040
|
-
headers.update(add_metadata_headers(metadata))
|
2041
|
-
if 'source_lease' in kwargs:
|
2042
|
-
source_lease = kwargs.pop('source_lease')
|
2043
|
-
try:
|
2044
|
-
headers['x-ms-source-lease-id'] = source_lease.id
|
2045
|
-
except AttributeError:
|
2046
|
-
headers['x-ms-source-lease-id'] = source_lease
|
2047
|
-
|
2048
|
-
tier = kwargs.pop('premium_page_blob_tier', None) or kwargs.pop('standard_blob_tier', None)
|
2049
|
-
tags = kwargs.pop('tags', None)
|
2050
|
-
|
2051
|
-
# Options only available for sync copy
|
2052
|
-
requires_sync = kwargs.pop('requires_sync', None)
|
2053
|
-
encryption_scope_str = kwargs.pop('encryption_scope', None)
|
2054
|
-
source_authorization = kwargs.pop('source_authorization', None)
|
2055
|
-
# If tags is a str, interpret that as copy_source_tags
|
2056
|
-
copy_source_tags = isinstance(tags, str)
|
2057
|
-
|
2058
|
-
if incremental_copy:
|
2059
|
-
if source_authorization:
|
2060
|
-
raise ValueError("Source authorization tokens are not applicable for incremental copying.")
|
2061
|
-
if copy_source_tags:
|
2062
|
-
raise ValueError("Copying source tags is not applicable for incremental copying.")
|
2063
|
-
|
2064
|
-
# TODO: refactor start_copy_from_url api in _blob_client.py. Call _generated/_blob_operations.py copy_from_url
|
2065
|
-
# when requires_sync=True is set.
|
2066
|
-
# Currently both sync copy and async copy are calling _generated/_blob_operations.py start_copy_from_url.
|
2067
|
-
# As sync copy diverges more from async copy, more problem will surface.
|
2068
|
-
if requires_sync is True:
|
2069
|
-
headers['x-ms-requires-sync'] = str(requires_sync)
|
2070
|
-
if encryption_scope_str:
|
2071
|
-
headers['x-ms-encryption-scope'] = encryption_scope_str
|
2072
|
-
if source_authorization:
|
2073
|
-
headers['x-ms-copy-source-authorization'] = source_authorization
|
2074
|
-
if copy_source_tags:
|
2075
|
-
headers['x-ms-copy-source-tag-option'] = tags
|
2076
|
-
else:
|
2077
|
-
if encryption_scope_str:
|
2078
|
-
raise ValueError(
|
2079
|
-
"Encryption_scope is only supported for sync copy, please specify requires_sync=True")
|
2080
|
-
if source_authorization:
|
2081
|
-
raise ValueError(
|
2082
|
-
"Source authorization tokens are only supported for sync copy, please specify requires_sync=True")
|
2083
|
-
if copy_source_tags:
|
2084
|
-
raise ValueError(
|
2085
|
-
"Copying source tags is only supported for sync copy, please specify requires_sync=True")
|
2086
|
-
|
2087
|
-
timeout = kwargs.pop('timeout', None)
|
2088
|
-
dest_mod_conditions = get_modify_conditions(kwargs)
|
2089
|
-
blob_tags_string = serialize_blob_tags_header(tags) if not copy_source_tags else None
|
2090
|
-
|
2091
|
-
immutability_policy = kwargs.pop('immutability_policy', None)
|
2092
|
-
if immutability_policy:
|
2093
|
-
kwargs['immutability_policy_expiry'] = immutability_policy.expiry_time
|
2094
|
-
kwargs['immutability_policy_mode'] = immutability_policy.policy_mode
|
2095
|
-
|
2096
|
-
options = {
|
2097
|
-
'copy_source': source_url,
|
2098
|
-
'seal_blob': kwargs.pop('seal_destination_blob', None),
|
2099
|
-
'timeout': timeout,
|
2100
|
-
'modified_access_conditions': dest_mod_conditions,
|
2101
|
-
'blob_tags_string': blob_tags_string,
|
2102
|
-
'headers': headers,
|
2103
|
-
'cls': return_response_headers,
|
2104
|
-
}
|
2105
|
-
if not incremental_copy:
|
2106
|
-
source_mod_conditions = get_source_conditions(kwargs)
|
2107
|
-
dest_access_conditions = get_access_conditions(kwargs.pop('destination_lease', None))
|
2108
|
-
options['source_modified_access_conditions'] = source_mod_conditions
|
2109
|
-
options['lease_access_conditions'] = dest_access_conditions
|
2110
|
-
options['tier'] = tier.value if tier else None
|
2111
|
-
options.update(kwargs)
|
2112
|
-
return options
|
2113
|
-
|
2114
1562
|
@distributed_trace
|
2115
|
-
def start_copy_from_url(
|
2116
|
-
|
1563
|
+
def start_copy_from_url(
|
1564
|
+
self, source_url: str,
|
1565
|
+
metadata: Optional[Dict[str, str]] = None,
|
1566
|
+
incremental_copy: bool = False,
|
1567
|
+
**kwargs: Any
|
1568
|
+
) -> Dict[str, Union[str, datetime]]:
|
2117
1569
|
"""Copies a blob from the given URL.
|
2118
1570
|
|
2119
1571
|
This operation returns a dictionary containing `copy_status` and `copy_id`,
|
@@ -2290,38 +1742,23 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
2290
1742
|
:dedent: 12
|
2291
1743
|
:caption: Copy a blob from a URL.
|
2292
1744
|
"""
|
2293
|
-
options =
|
2294
|
-
source_url=
|
1745
|
+
options = _start_copy_from_url_options(
|
1746
|
+
source_url=source_url,
|
2295
1747
|
metadata=metadata,
|
2296
1748
|
incremental_copy=incremental_copy,
|
2297
1749
|
**kwargs)
|
2298
1750
|
try:
|
2299
1751
|
if incremental_copy:
|
2300
|
-
return self._client.page_blob.copy_incremental(**options)
|
2301
|
-
return self._client.blob.start_copy_from_url(**options)
|
1752
|
+
return cast(Dict[str, Union[str, datetime]], self._client.page_blob.copy_incremental(**options))
|
1753
|
+
return cast(Dict[str, Union[str, datetime]], self._client.blob.start_copy_from_url(**options))
|
2302
1754
|
except HttpResponseError as error:
|
2303
1755
|
process_storage_error(error)
|
2304
1756
|
|
2305
|
-
def _abort_copy_options(self, copy_id, **kwargs):
|
2306
|
-
# type: (Union[str, Dict[str, Any], BlobProperties], **Any) -> Dict[str, Any]
|
2307
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
2308
|
-
try:
|
2309
|
-
copy_id = copy_id.copy.id
|
2310
|
-
except AttributeError:
|
2311
|
-
try:
|
2312
|
-
copy_id = copy_id['copy_id']
|
2313
|
-
except TypeError:
|
2314
|
-
pass
|
2315
|
-
options = {
|
2316
|
-
'copy_id': copy_id,
|
2317
|
-
'lease_access_conditions': access_conditions,
|
2318
|
-
'timeout': kwargs.pop('timeout', None)}
|
2319
|
-
options.update(kwargs)
|
2320
|
-
return options
|
2321
|
-
|
2322
1757
|
@distributed_trace
|
2323
|
-
def abort_copy(
|
2324
|
-
|
1758
|
+
def abort_copy(
|
1759
|
+
self, copy_id: Union[str, Dict[str, Any], BlobProperties],
|
1760
|
+
**kwargs: Any
|
1761
|
+
) -> None:
|
2325
1762
|
"""Abort an ongoing copy operation.
|
2326
1763
|
|
2327
1764
|
This will leave a destination blob with zero length and full metadata.
|
@@ -2342,15 +1779,14 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
2342
1779
|
:dedent: 12
|
2343
1780
|
:caption: Abort copying a blob from URL.
|
2344
1781
|
"""
|
2345
|
-
options =
|
1782
|
+
options = _abort_copy_options(copy_id, **kwargs)
|
2346
1783
|
try:
|
2347
1784
|
self._client.blob.abort_copy_from_url(**options)
|
2348
1785
|
except HttpResponseError as error:
|
2349
1786
|
process_storage_error(error)
|
2350
1787
|
|
2351
1788
|
@distributed_trace
|
2352
|
-
def acquire_lease(self, lease_duration=-1, lease_id=None, **kwargs):
|
2353
|
-
# type: (int, Optional[str], **Any) -> BlobLeaseClient
|
1789
|
+
def acquire_lease(self, lease_duration: int =-1, lease_id: Optional[str] = None, **kwargs: Any) -> BlobLeaseClient:
|
2354
1790
|
"""Requests a new lease.
|
2355
1791
|
|
2356
1792
|
If the blob does not have an active lease, the Blob
|
@@ -2406,13 +1842,12 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
2406
1842
|
:dedent: 8
|
2407
1843
|
:caption: Acquiring a lease on a blob.
|
2408
1844
|
"""
|
2409
|
-
lease = BlobLeaseClient(self, lease_id=lease_id)
|
1845
|
+
lease = BlobLeaseClient(self, lease_id=lease_id)
|
2410
1846
|
lease.acquire(lease_duration=lease_duration, **kwargs)
|
2411
1847
|
return lease
|
2412
1848
|
|
2413
1849
|
@distributed_trace
|
2414
|
-
def set_standard_blob_tier(self, standard_blob_tier, **kwargs):
|
2415
|
-
# type: (Union[str, StandardBlobTier], Any) -> None
|
1850
|
+
def set_standard_blob_tier(self, standard_blob_tier: Union[str, "StandardBlobTier"], **kwargs: Any) -> None:
|
2416
1851
|
"""This operation sets the tier on a block blob.
|
2417
1852
|
|
2418
1853
|
A block blob's tier determines Hot/Cool/Archive storage type.
|
@@ -2472,66 +1907,20 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
2472
1907
|
except HttpResponseError as error:
|
2473
1908
|
process_storage_error(error)
|
2474
1909
|
|
2475
|
-
def _stage_block_options(
|
2476
|
-
self, block_id, # type: str
|
2477
|
-
data, # type: Union[Iterable[AnyStr], IO[AnyStr]]
|
2478
|
-
length=None, # type: Optional[int]
|
2479
|
-
**kwargs
|
2480
|
-
):
|
2481
|
-
# type: (...) -> Dict[str, Any]
|
2482
|
-
if self.require_encryption or (self.key_encryption_key is not None):
|
2483
|
-
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
2484
|
-
block_id = encode_base64(str(block_id))
|
2485
|
-
if isinstance(data, str):
|
2486
|
-
data = data.encode(kwargs.pop('encoding', 'UTF-8')) # type: ignore
|
2487
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
2488
|
-
if length is None:
|
2489
|
-
length = get_length(data)
|
2490
|
-
if length is None:
|
2491
|
-
length, data = read_length(data)
|
2492
|
-
if isinstance(data, bytes):
|
2493
|
-
data = data[:length]
|
2494
|
-
|
2495
|
-
validate_content = kwargs.pop('validate_content', False)
|
2496
|
-
cpk_scope_info = get_cpk_scope_info(kwargs)
|
2497
|
-
cpk = kwargs.pop('cpk', None)
|
2498
|
-
cpk_info = None
|
2499
|
-
if cpk:
|
2500
|
-
if self.scheme.lower() != 'https':
|
2501
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
2502
|
-
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
|
2503
|
-
encryption_algorithm=cpk.algorithm)
|
2504
|
-
|
2505
|
-
options = {
|
2506
|
-
'block_id': block_id,
|
2507
|
-
'content_length': length,
|
2508
|
-
'body': data,
|
2509
|
-
'transactional_content_md5': None,
|
2510
|
-
'timeout': kwargs.pop('timeout', None),
|
2511
|
-
'lease_access_conditions': access_conditions,
|
2512
|
-
'validate_content': validate_content,
|
2513
|
-
'cpk_scope_info': cpk_scope_info,
|
2514
|
-
'cpk_info': cpk_info,
|
2515
|
-
'cls': return_response_headers,
|
2516
|
-
}
|
2517
|
-
options.update(kwargs)
|
2518
|
-
return options
|
2519
|
-
|
2520
1910
|
@distributed_trace
|
2521
1911
|
def stage_block(
|
2522
|
-
|
2523
|
-
|
2524
|
-
|
2525
|
-
|
2526
|
-
|
2527
|
-
# type: (...) -> Dict[str, Any]
|
1912
|
+
self, block_id: str,
|
1913
|
+
data: Union[bytes, str, Iterable[AnyStr], IO[AnyStr]],
|
1914
|
+
length: Optional[int] = None,
|
1915
|
+
**kwargs: Any
|
1916
|
+
) -> Dict[str, Any]:
|
2528
1917
|
"""Creates a new block to be committed as part of a blob.
|
2529
1918
|
|
2530
1919
|
:param str block_id: A string value that identifies the block.
|
2531
1920
|
The string should be less than or equal to 64 bytes in size.
|
2532
1921
|
For a given blob, the block_id must be the same size for each block.
|
2533
1922
|
:param data: The blob data.
|
2534
|
-
:type data: Union[Iterable[AnyStr], IO[AnyStr]]
|
1923
|
+
:type data: Union[bytes, str, Iterable[AnyStr], IO[AnyStr]]
|
2535
1924
|
:param int length: Size of the block.
|
2536
1925
|
:keyword bool validate_content:
|
2537
1926
|
If true, calculates an MD5 hash for each chunk of the blob. The storage
|
@@ -2570,70 +1959,29 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
2570
1959
|
:returns: Blob property dict.
|
2571
1960
|
:rtype: dict[str, Any]
|
2572
1961
|
"""
|
2573
|
-
|
2574
|
-
|
2575
|
-
|
1962
|
+
if self.require_encryption or (self.key_encryption_key is not None):
|
1963
|
+
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
1964
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
1965
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
1966
|
+
options = _stage_block_options(
|
1967
|
+
block_id=block_id,
|
1968
|
+
data=data,
|
2576
1969
|
length=length,
|
2577
1970
|
**kwargs)
|
2578
1971
|
try:
|
2579
|
-
return self._client.block_blob.stage_block(**options)
|
1972
|
+
return cast(Dict[str, Any], self._client.block_blob.stage_block(**options))
|
2580
1973
|
except HttpResponseError as error:
|
2581
1974
|
process_storage_error(error)
|
2582
1975
|
|
2583
|
-
def _stage_block_from_url_options(
|
2584
|
-
self, block_id, # type: str
|
2585
|
-
source_url, # type: str
|
2586
|
-
source_offset=None, # type: Optional[int]
|
2587
|
-
source_length=None, # type: Optional[int]
|
2588
|
-
source_content_md5=None, # type: Optional[Union[bytes, bytearray]]
|
2589
|
-
**kwargs
|
2590
|
-
):
|
2591
|
-
# type: (...) -> Dict[str, Any]
|
2592
|
-
source_authorization = kwargs.pop('source_authorization', None)
|
2593
|
-
if source_length is not None and source_offset is None:
|
2594
|
-
raise ValueError("Source offset value must not be None if length is set.")
|
2595
|
-
if source_length is not None:
|
2596
|
-
source_length = source_offset + source_length - 1
|
2597
|
-
block_id = encode_base64(str(block_id))
|
2598
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
2599
|
-
range_header = None
|
2600
|
-
if source_offset is not None:
|
2601
|
-
range_header, _ = validate_and_format_range_headers(source_offset, source_length)
|
2602
|
-
|
2603
|
-
cpk_scope_info = get_cpk_scope_info(kwargs)
|
2604
|
-
cpk = kwargs.pop('cpk', None)
|
2605
|
-
cpk_info = None
|
2606
|
-
if cpk:
|
2607
|
-
if self.scheme.lower() != 'https':
|
2608
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
2609
|
-
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
|
2610
|
-
encryption_algorithm=cpk.algorithm)
|
2611
|
-
options = {
|
2612
|
-
'copy_source_authorization': source_authorization,
|
2613
|
-
'block_id': block_id,
|
2614
|
-
'content_length': 0,
|
2615
|
-
'source_url': source_url,
|
2616
|
-
'source_range': range_header,
|
2617
|
-
'source_content_md5': bytearray(source_content_md5) if source_content_md5 else None,
|
2618
|
-
'timeout': kwargs.pop('timeout', None),
|
2619
|
-
'lease_access_conditions': access_conditions,
|
2620
|
-
'cpk_scope_info': cpk_scope_info,
|
2621
|
-
'cpk_info': cpk_info,
|
2622
|
-
'cls': return_response_headers,
|
2623
|
-
}
|
2624
|
-
options.update(kwargs)
|
2625
|
-
return options
|
2626
|
-
|
2627
1976
|
@distributed_trace
|
2628
1977
|
def stage_block_from_url(
|
2629
|
-
|
2630
|
-
|
2631
|
-
|
2632
|
-
|
2633
|
-
|
2634
|
-
|
2635
|
-
|
2636
|
-
# type: (...) -> Dict[str, Any]
|
1978
|
+
self, block_id: str,
|
1979
|
+
source_url: str,
|
1980
|
+
source_offset: Optional[int] = None,
|
1981
|
+
source_length: Optional[int] = None,
|
1982
|
+
source_content_md5: Optional[Union[bytes, bytearray]] = None,
|
1983
|
+
**kwargs: Any
|
1984
|
+
) -> Dict[str, Any]:
|
2637
1985
|
"""Creates a new block to be committed as part of a blob where
|
2638
1986
|
the contents are read from a URL.
|
2639
1987
|
|
@@ -2677,31 +2025,25 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
2677
2025
|
:returns: Blob property dict.
|
2678
2026
|
:rtype: dict[str, Any]
|
2679
2027
|
"""
|
2680
|
-
|
2681
|
-
|
2682
|
-
|
2028
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
2029
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
2030
|
+
options = _stage_block_from_url_options(
|
2031
|
+
block_id=block_id,
|
2032
|
+
source_url=source_url,
|
2683
2033
|
source_offset=source_offset,
|
2684
2034
|
source_length=source_length,
|
2685
2035
|
source_content_md5=source_content_md5,
|
2686
2036
|
**kwargs)
|
2687
2037
|
try:
|
2688
|
-
return self._client.block_blob.stage_block_from_url(**options)
|
2038
|
+
return cast(Dict[str, Any], self._client.block_blob.stage_block_from_url(**options))
|
2689
2039
|
except HttpResponseError as error:
|
2690
2040
|
process_storage_error(error)
|
2691
2041
|
|
2692
|
-
def _get_block_list_result(self, blocks):
|
2693
|
-
# type: (BlockList) -> Tuple[List[BlobBlock], List[BlobBlock]]
|
2694
|
-
committed = [] # type: List
|
2695
|
-
uncommitted = [] # type: List
|
2696
|
-
if blocks.committed_blocks:
|
2697
|
-
committed = [BlobBlock._from_generated(b) for b in blocks.committed_blocks] # pylint: disable=protected-access
|
2698
|
-
if blocks.uncommitted_blocks:
|
2699
|
-
uncommitted = [BlobBlock._from_generated(b) for b in blocks.uncommitted_blocks] # pylint: disable=protected-access
|
2700
|
-
return committed, uncommitted
|
2701
|
-
|
2702
2042
|
@distributed_trace
|
2703
|
-
def get_block_list(
|
2704
|
-
|
2043
|
+
def get_block_list(
|
2044
|
+
self, block_list_type: str = "committed",
|
2045
|
+
**kwargs: Any
|
2046
|
+
) -> Tuple[List[BlobBlock], List[BlobBlock]]:
|
2705
2047
|
"""The Get Block List operation retrieves the list of blocks that have
|
2706
2048
|
been uploaded as part of a block blob.
|
2707
2049
|
|
@@ -2725,7 +2067,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
2725
2067
|
see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
|
2726
2068
|
#other-client--per-operation-configuration>`__.
|
2727
2069
|
:returns: A tuple of two lists - committed and uncommitted blocks
|
2728
|
-
:rtype:
|
2070
|
+
:rtype: Tuple[List[BlobBlock], List[BlobBlock]]
|
2729
2071
|
"""
|
2730
2072
|
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
2731
2073
|
mod_conditions = get_modify_conditions(kwargs)
|
@@ -2739,86 +2081,15 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
2739
2081
|
**kwargs)
|
2740
2082
|
except HttpResponseError as error:
|
2741
2083
|
process_storage_error(error)
|
2742
|
-
return
|
2743
|
-
|
2744
|
-
def _commit_block_list_options( # type: ignore
|
2745
|
-
self, block_list, # type: List[BlobBlock]
|
2746
|
-
content_settings=None, # type: Optional[ContentSettings]
|
2747
|
-
metadata=None, # type: Optional[Dict[str, str]]
|
2748
|
-
**kwargs
|
2749
|
-
):
|
2750
|
-
# type: (...) -> Dict[str, Any]
|
2751
|
-
if self.require_encryption or (self.key_encryption_key is not None):
|
2752
|
-
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
2753
|
-
block_lookup = BlockLookupList(committed=[], uncommitted=[], latest=[])
|
2754
|
-
for block in block_list:
|
2755
|
-
try:
|
2756
|
-
if block.state.value == 'committed':
|
2757
|
-
block_lookup.committed.append(encode_base64(str(block.id)))
|
2758
|
-
elif block.state.value == 'uncommitted':
|
2759
|
-
block_lookup.uncommitted.append(encode_base64(str(block.id)))
|
2760
|
-
else:
|
2761
|
-
block_lookup.latest.append(encode_base64(str(block.id)))
|
2762
|
-
except AttributeError:
|
2763
|
-
block_lookup.latest.append(encode_base64(str(block)))
|
2764
|
-
headers = kwargs.pop('headers', {})
|
2765
|
-
headers.update(add_metadata_headers(metadata))
|
2766
|
-
blob_headers = None
|
2767
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
2768
|
-
mod_conditions = get_modify_conditions(kwargs)
|
2769
|
-
if content_settings:
|
2770
|
-
blob_headers = BlobHTTPHeaders(
|
2771
|
-
blob_cache_control=content_settings.cache_control,
|
2772
|
-
blob_content_type=content_settings.content_type,
|
2773
|
-
blob_content_md5=content_settings.content_md5,
|
2774
|
-
blob_content_encoding=content_settings.content_encoding,
|
2775
|
-
blob_content_language=content_settings.content_language,
|
2776
|
-
blob_content_disposition=content_settings.content_disposition
|
2777
|
-
)
|
2778
|
-
|
2779
|
-
validate_content = kwargs.pop('validate_content', False)
|
2780
|
-
cpk_scope_info = get_cpk_scope_info(kwargs)
|
2781
|
-
cpk = kwargs.pop('cpk', None)
|
2782
|
-
cpk_info = None
|
2783
|
-
if cpk:
|
2784
|
-
if self.scheme.lower() != 'https':
|
2785
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
2786
|
-
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
|
2787
|
-
encryption_algorithm=cpk.algorithm)
|
2788
|
-
|
2789
|
-
immutability_policy = kwargs.pop('immutability_policy', None)
|
2790
|
-
if immutability_policy:
|
2791
|
-
kwargs['immutability_policy_expiry'] = immutability_policy.expiry_time
|
2792
|
-
kwargs['immutability_policy_mode'] = immutability_policy.policy_mode
|
2793
|
-
|
2794
|
-
tier = kwargs.pop('standard_blob_tier', None)
|
2795
|
-
blob_tags_string = serialize_blob_tags_header(kwargs.pop('tags', None))
|
2796
|
-
|
2797
|
-
options = {
|
2798
|
-
'blocks': block_lookup,
|
2799
|
-
'blob_http_headers': blob_headers,
|
2800
|
-
'lease_access_conditions': access_conditions,
|
2801
|
-
'timeout': kwargs.pop('timeout', None),
|
2802
|
-
'modified_access_conditions': mod_conditions,
|
2803
|
-
'cls': return_response_headers,
|
2804
|
-
'validate_content': validate_content,
|
2805
|
-
'cpk_scope_info': cpk_scope_info,
|
2806
|
-
'cpk_info': cpk_info,
|
2807
|
-
'tier': tier.value if tier else None,
|
2808
|
-
'blob_tags_string': blob_tags_string,
|
2809
|
-
'headers': headers
|
2810
|
-
}
|
2811
|
-
options.update(kwargs)
|
2812
|
-
return options
|
2084
|
+
return _get_block_list_result(blocks)
|
2813
2085
|
|
2814
2086
|
@distributed_trace
|
2815
|
-
def commit_block_list(
|
2816
|
-
|
2817
|
-
|
2818
|
-
|
2819
|
-
|
2820
|
-
|
2821
|
-
# type: (...) -> Dict[str, Union[str, datetime]]
|
2087
|
+
def commit_block_list(
|
2088
|
+
self, block_list: List[BlobBlock],
|
2089
|
+
content_settings: Optional["ContentSettings"] = None,
|
2090
|
+
metadata: Optional[Dict[str, str]] = None,
|
2091
|
+
**kwargs: Any
|
2092
|
+
) -> Dict[str, Union[str, datetime]]:
|
2822
2093
|
"""The Commit Block List operation writes a blob by specifying the list of
|
2823
2094
|
block IDs that make up the blob.
|
2824
2095
|
|
@@ -2910,19 +2181,22 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
2910
2181
|
:returns: Blob-updated property dict (Etag and last modified).
|
2911
2182
|
:rtype: dict(str, Any)
|
2912
2183
|
"""
|
2913
|
-
|
2914
|
-
|
2184
|
+
if self.require_encryption or (self.key_encryption_key is not None):
|
2185
|
+
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
2186
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
2187
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
2188
|
+
options = _commit_block_list_options(
|
2189
|
+
block_list=block_list,
|
2915
2190
|
content_settings=content_settings,
|
2916
2191
|
metadata=metadata,
|
2917
2192
|
**kwargs)
|
2918
2193
|
try:
|
2919
|
-
return self._client.block_blob.commit_block_list(**options)
|
2194
|
+
return cast(Dict[str, Any], self._client.block_blob.commit_block_list(**options))
|
2920
2195
|
except HttpResponseError as error:
|
2921
2196
|
process_storage_error(error)
|
2922
2197
|
|
2923
2198
|
@distributed_trace
|
2924
|
-
def set_premium_page_blob_tier(self, premium_page_blob_tier, **kwargs):
|
2925
|
-
# type: (Union[str, PremiumPageBlobTier], **Any) -> None
|
2199
|
+
def set_premium_page_blob_tier(self, premium_page_blob_tier: "PremiumPageBlobTier", **kwargs: Any) -> None:
|
2926
2200
|
"""Sets the page blob tiers on the blob. This API is only supported for page blobs on premium accounts.
|
2927
2201
|
|
2928
2202
|
:param premium_page_blob_tier:
|
@@ -2962,25 +2236,8 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
2962
2236
|
except HttpResponseError as error:
|
2963
2237
|
process_storage_error(error)
|
2964
2238
|
|
2965
|
-
def _set_blob_tags_options(self, tags=None, **kwargs):
|
2966
|
-
# type: (Optional[Dict[str, str]], **Any) -> Dict[str, Any]
|
2967
|
-
tags = serialize_blob_tags(tags)
|
2968
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
2969
|
-
mod_conditions = get_modify_conditions(kwargs)
|
2970
|
-
version_id = get_version_id(self.version_id, kwargs)
|
2971
|
-
|
2972
|
-
options = {
|
2973
|
-
'tags': tags,
|
2974
|
-
'lease_access_conditions': access_conditions,
|
2975
|
-
'modified_access_conditions': mod_conditions,
|
2976
|
-
'version_id': version_id,
|
2977
|
-
'cls': return_response_headers}
|
2978
|
-
options.update(kwargs)
|
2979
|
-
return options
|
2980
|
-
|
2981
2239
|
@distributed_trace
|
2982
|
-
def set_blob_tags(self, tags=None, **kwargs):
|
2983
|
-
# type: (Optional[Dict[str, str]], **Any) -> Dict[str, Any]
|
2240
|
+
def set_blob_tags(self, tags: Optional[Dict[str, str]] = None, **kwargs: Any) -> Dict[str, Any]:
|
2984
2241
|
"""The Set Tags operation enables users to set tags on a blob or specific blob version, but not snapshot.
|
2985
2242
|
Each call to this operation replaces all existing tags attached to the blob. To remove all
|
2986
2243
|
tags from the blob, call this operation with no tags set.
|
@@ -3021,36 +2278,21 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
3021
2278
|
:returns: Blob-updated property dict (Etag and last modified)
|
3022
2279
|
:rtype: Dict[str, Any]
|
3023
2280
|
"""
|
3024
|
-
|
2281
|
+
version_id = get_version_id(self.version_id, kwargs)
|
2282
|
+
options = _set_blob_tags_options(version_id=version_id, tags=tags, **kwargs)
|
3025
2283
|
try:
|
3026
|
-
return self._client.blob.set_tags(**options)
|
2284
|
+
return cast(Dict[str, Any], self._client.blob.set_tags(**options))
|
3027
2285
|
except HttpResponseError as error:
|
3028
2286
|
process_storage_error(error)
|
3029
2287
|
|
3030
|
-
def _get_blob_tags_options(self, **kwargs):
|
3031
|
-
# type: (**Any) -> Dict[str, str]
|
3032
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
3033
|
-
mod_conditions = get_modify_conditions(kwargs)
|
3034
|
-
version_id = get_version_id(self.version_id, kwargs)
|
3035
|
-
|
3036
|
-
options = {
|
3037
|
-
'version_id': version_id,
|
3038
|
-
'snapshot': self.snapshot,
|
3039
|
-
'lease_access_conditions': access_conditions,
|
3040
|
-
'modified_access_conditions': mod_conditions,
|
3041
|
-
'timeout': kwargs.pop('timeout', None),
|
3042
|
-
'cls': return_headers_and_deserialized}
|
3043
|
-
return options
|
3044
|
-
|
3045
2288
|
@distributed_trace
|
3046
|
-
def get_blob_tags(self, **kwargs):
|
3047
|
-
# type: (**Any) -> Dict[str, str]
|
2289
|
+
def get_blob_tags(self, **kwargs: Any) -> Dict[str, str]:
|
3048
2290
|
"""The Get Tags operation enables users to get tags on a blob or specific blob version, or snapshot.
|
3049
2291
|
|
3050
2292
|
.. versionadded:: 12.4.0
|
3051
2293
|
This operation was introduced in API version '2019-12-12'.
|
3052
2294
|
|
3053
|
-
:keyword str version_id:
|
2295
|
+
:keyword Optional[str] version_id:
|
3054
2296
|
The version id parameter is an opaque DateTime
|
3055
2297
|
value that, when present, specifies the version of the blob to add tags to.
|
3056
2298
|
:keyword str if_tags_match_condition:
|
@@ -3069,54 +2311,21 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
3069
2311
|
:returns: Key value pairs of blob tags.
|
3070
2312
|
:rtype: Dict[str, str]
|
3071
2313
|
"""
|
3072
|
-
|
2314
|
+
version_id = get_version_id(self.version_id, kwargs)
|
2315
|
+
options = _get_blob_tags_options(version_id=version_id, snapshot=self.snapshot, **kwargs)
|
3073
2316
|
try:
|
3074
2317
|
_, tags = self._client.blob.get_tags(**options)
|
3075
|
-
return parse_tags(tags)
|
2318
|
+
return cast(Dict[str, str], parse_tags(tags))
|
3076
2319
|
except HttpResponseError as error:
|
3077
2320
|
process_storage_error(error)
|
3078
2321
|
|
3079
|
-
def _get_page_ranges_options( # type: ignore
|
3080
|
-
self, offset=None, # type: Optional[int]
|
3081
|
-
length=None, # type: Optional[int]
|
3082
|
-
previous_snapshot_diff=None, # type: Optional[Union[str, Dict[str, Any]]]
|
3083
|
-
**kwargs
|
3084
|
-
):
|
3085
|
-
# type: (...) -> Dict[str, Any]
|
3086
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
3087
|
-
mod_conditions = get_modify_conditions(kwargs)
|
3088
|
-
if length is not None and offset is None:
|
3089
|
-
raise ValueError("Offset value must not be None if length is set.")
|
3090
|
-
if length is not None:
|
3091
|
-
length = offset + length - 1 # Reformat to an inclusive range index
|
3092
|
-
page_range, _ = validate_and_format_range_headers(
|
3093
|
-
offset, length, start_range_required=False, end_range_required=False, align_to_page=True
|
3094
|
-
)
|
3095
|
-
options = {
|
3096
|
-
'snapshot': self.snapshot,
|
3097
|
-
'lease_access_conditions': access_conditions,
|
3098
|
-
'modified_access_conditions': mod_conditions,
|
3099
|
-
'timeout': kwargs.pop('timeout', None),
|
3100
|
-
'range': page_range}
|
3101
|
-
if previous_snapshot_diff:
|
3102
|
-
try:
|
3103
|
-
options['prevsnapshot'] = previous_snapshot_diff.snapshot # type: ignore
|
3104
|
-
except AttributeError:
|
3105
|
-
try:
|
3106
|
-
options['prevsnapshot'] = previous_snapshot_diff['snapshot'] # type: ignore
|
3107
|
-
except TypeError:
|
3108
|
-
options['prevsnapshot'] = previous_snapshot_diff
|
3109
|
-
options.update(kwargs)
|
3110
|
-
return options
|
3111
|
-
|
3112
2322
|
@distributed_trace
|
3113
|
-
def get_page_ranges(
|
3114
|
-
|
3115
|
-
|
3116
|
-
|
3117
|
-
|
3118
|
-
|
3119
|
-
# type: (...) -> Tuple[List[Dict[str, int]], List[Dict[str, int]]]
|
2323
|
+
def get_page_ranges(
|
2324
|
+
self, offset: Optional[int] = None,
|
2325
|
+
length: Optional[int] = None,
|
2326
|
+
previous_snapshot_diff: Optional[Union[str, Dict[str, Any]]] = None,
|
2327
|
+
**kwargs: Any
|
2328
|
+
) -> Tuple[List[Dict[str, int]], List[Dict[str, int]]]:
|
3120
2329
|
"""DEPRECATED: Returns the list of valid page ranges for a Page Blob or snapshot
|
3121
2330
|
of a page blob.
|
3122
2331
|
|
@@ -3181,7 +2390,8 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
3181
2390
|
DeprecationWarning
|
3182
2391
|
)
|
3183
2392
|
|
3184
|
-
options =
|
2393
|
+
options = _get_page_ranges_options(
|
2394
|
+
snapshot=self.snapshot,
|
3185
2395
|
offset=offset,
|
3186
2396
|
length=length,
|
3187
2397
|
previous_snapshot_diff=previous_snapshot_diff,
|
@@ -3197,13 +2407,13 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
3197
2407
|
|
3198
2408
|
@distributed_trace
|
3199
2409
|
def list_page_ranges(
|
3200
|
-
|
3201
|
-
|
3202
|
-
|
3203
|
-
|
3204
|
-
|
3205
|
-
|
3206
|
-
|
2410
|
+
self,
|
2411
|
+
*,
|
2412
|
+
offset: Optional[int] = None,
|
2413
|
+
length: Optional[int] = None,
|
2414
|
+
previous_snapshot: Optional[Union[str, Dict[str, Any]]] = None,
|
2415
|
+
**kwargs: Any
|
2416
|
+
) -> ItemPaged[PageRange]:
|
3207
2417
|
"""Returns the list of valid page ranges for a Page Blob or snapshot
|
3208
2418
|
of a page blob. If `previous_snapshot` is specified, the result will be
|
3209
2419
|
a diff of changes between the target blob and the previous snapshot.
|
@@ -3267,7 +2477,8 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
3267
2477
|
:rtype: ~azure.core.paging.ItemPaged[~azure.storage.blob.PageRange]
|
3268
2478
|
"""
|
3269
2479
|
results_per_page = kwargs.pop('results_per_page', None)
|
3270
|
-
options =
|
2480
|
+
options = _get_page_ranges_options(
|
2481
|
+
snapshot=self.snapshot,
|
3271
2482
|
offset=offset,
|
3272
2483
|
length=length,
|
3273
2484
|
previous_snapshot_diff=previous_snapshot,
|
@@ -3287,12 +2498,11 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
3287
2498
|
|
3288
2499
|
@distributed_trace
|
3289
2500
|
def get_page_range_diff_for_managed_disk(
|
3290
|
-
|
3291
|
-
|
3292
|
-
|
3293
|
-
|
3294
|
-
|
3295
|
-
# type: (...) -> Tuple[List[Dict[str, int]], List[Dict[str, int]]]
|
2501
|
+
self, previous_snapshot_url: str,
|
2502
|
+
offset: Optional[int] = None,
|
2503
|
+
length:Optional[int] = None,
|
2504
|
+
**kwargs: Any
|
2505
|
+
) -> Tuple[List[Dict[str, int]], List[Dict[str, int]]]:
|
3296
2506
|
"""Returns the list of valid page ranges for a managed disk or snapshot.
|
3297
2507
|
|
3298
2508
|
.. note::
|
@@ -3351,7 +2561,8 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
3351
2561
|
The first element are filled page ranges, the 2nd element is cleared page ranges.
|
3352
2562
|
:rtype: tuple(list(dict(str, str), list(dict(str, str))
|
3353
2563
|
"""
|
3354
|
-
options =
|
2564
|
+
options = _get_page_ranges_options(
|
2565
|
+
snapshot=self.snapshot,
|
3355
2566
|
offset=offset,
|
3356
2567
|
length=length,
|
3357
2568
|
prev_snapshot_url=previous_snapshot_url,
|
@@ -3362,25 +2573,12 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
3362
2573
|
process_storage_error(error)
|
3363
2574
|
return get_page_ranges_result(ranges)
|
3364
2575
|
|
3365
|
-
def _set_sequence_number_options(self, sequence_number_action, sequence_number=None, **kwargs):
|
3366
|
-
# type: (Union[str, SequenceNumberAction], Optional[str], **Any) -> Dict[str, Any]
|
3367
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
3368
|
-
mod_conditions = get_modify_conditions(kwargs)
|
3369
|
-
if sequence_number_action is None:
|
3370
|
-
raise ValueError("A sequence number action must be specified")
|
3371
|
-
options = {
|
3372
|
-
'sequence_number_action': sequence_number_action,
|
3373
|
-
'timeout': kwargs.pop('timeout', None),
|
3374
|
-
'blob_sequence_number': sequence_number,
|
3375
|
-
'lease_access_conditions': access_conditions,
|
3376
|
-
'modified_access_conditions': mod_conditions,
|
3377
|
-
'cls': return_response_headers}
|
3378
|
-
options.update(kwargs)
|
3379
|
-
return options
|
3380
|
-
|
3381
2576
|
@distributed_trace
|
3382
|
-
def set_sequence_number(
|
3383
|
-
|
2577
|
+
def set_sequence_number(
|
2578
|
+
self, sequence_number_action: Union[str, "SequenceNumberAction"],
|
2579
|
+
sequence_number: Optional[str] = None,
|
2580
|
+
**kwargs: Any
|
2581
|
+
) -> Dict[str, Union[str, datetime]]:
|
3384
2582
|
"""Sets the blob sequence number.
|
3385
2583
|
|
3386
2584
|
:param str sequence_number_action:
|
@@ -3426,40 +2624,14 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
3426
2624
|
:returns: Blob-updated property dict (Etag and last modified).
|
3427
2625
|
:rtype: dict(str, Any)
|
3428
2626
|
"""
|
3429
|
-
options =
|
3430
|
-
sequence_number_action, sequence_number=sequence_number, **kwargs)
|
2627
|
+
options = _set_sequence_number_options(sequence_number_action, sequence_number=sequence_number, **kwargs)
|
3431
2628
|
try:
|
3432
|
-
return self._client.page_blob.update_sequence_number(**options)
|
2629
|
+
return cast(Dict[str, Any], self._client.page_blob.update_sequence_number(**options))
|
3433
2630
|
except HttpResponseError as error:
|
3434
2631
|
process_storage_error(error)
|
3435
2632
|
|
3436
|
-
def _resize_blob_options(self, size, **kwargs):
|
3437
|
-
# type: (int, **Any) -> Dict[str, Any]
|
3438
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
3439
|
-
mod_conditions = get_modify_conditions(kwargs)
|
3440
|
-
if size is None:
|
3441
|
-
raise ValueError("A content length must be specified for a Page Blob.")
|
3442
|
-
|
3443
|
-
cpk = kwargs.pop('cpk', None)
|
3444
|
-
cpk_info = None
|
3445
|
-
if cpk:
|
3446
|
-
if self.scheme.lower() != 'https':
|
3447
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
3448
|
-
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
|
3449
|
-
encryption_algorithm=cpk.algorithm)
|
3450
|
-
options = {
|
3451
|
-
'blob_content_length': size,
|
3452
|
-
'timeout': kwargs.pop('timeout', None),
|
3453
|
-
'lease_access_conditions': access_conditions,
|
3454
|
-
'modified_access_conditions': mod_conditions,
|
3455
|
-
'cpk_info': cpk_info,
|
3456
|
-
'cls': return_response_headers}
|
3457
|
-
options.update(kwargs)
|
3458
|
-
return options
|
3459
|
-
|
3460
2633
|
@distributed_trace
|
3461
|
-
def resize_blob(self, size, **kwargs):
|
3462
|
-
# type: (int, **Any) -> Dict[str, Union[str, datetime]]
|
2634
|
+
def resize_blob(self, size: int, **kwargs: Any) -> Dict[str, Union[str, datetime]]:
|
3463
2635
|
"""Resizes a page blob to the specified size.
|
3464
2636
|
|
3465
2637
|
If the specified value is less than the current size of the blob,
|
@@ -3508,70 +2680,21 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
3508
2680
|
:returns: Blob-updated property dict (Etag and last modified).
|
3509
2681
|
:rtype: dict(str, Any)
|
3510
2682
|
"""
|
3511
|
-
|
2683
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
2684
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
2685
|
+
options = _resize_blob_options(size=size, **kwargs)
|
3512
2686
|
try:
|
3513
|
-
return self._client.page_blob.resize(**options)
|
2687
|
+
return cast(Dict[str, Any], self._client.page_blob.resize(**options))
|
3514
2688
|
except HttpResponseError as error:
|
3515
2689
|
process_storage_error(error)
|
3516
2690
|
|
3517
|
-
def _upload_page_options( # type: ignore
|
3518
|
-
self, page, # type: bytes
|
3519
|
-
offset, # type: int
|
3520
|
-
length, # type: int
|
3521
|
-
**kwargs
|
3522
|
-
):
|
3523
|
-
# type: (...) -> Dict[str, Any]
|
3524
|
-
if isinstance(page, str):
|
3525
|
-
page = page.encode(kwargs.pop('encoding', 'UTF-8'))
|
3526
|
-
if self.require_encryption or (self.key_encryption_key is not None):
|
3527
|
-
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
3528
|
-
|
3529
|
-
if offset is None or offset % 512 != 0:
|
3530
|
-
raise ValueError("offset must be an integer that aligns with 512 page size")
|
3531
|
-
if length is None or length % 512 != 0:
|
3532
|
-
raise ValueError("length must be an integer that aligns with 512 page size")
|
3533
|
-
end_range = offset + length - 1 # Reformat to an inclusive range index
|
3534
|
-
content_range = f'bytes={offset}-{end_range}' # type: ignore
|
3535
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
3536
|
-
seq_conditions = SequenceNumberAccessConditions(
|
3537
|
-
if_sequence_number_less_than_or_equal_to=kwargs.pop('if_sequence_number_lte', None),
|
3538
|
-
if_sequence_number_less_than=kwargs.pop('if_sequence_number_lt', None),
|
3539
|
-
if_sequence_number_equal_to=kwargs.pop('if_sequence_number_eq', None)
|
3540
|
-
)
|
3541
|
-
mod_conditions = get_modify_conditions(kwargs)
|
3542
|
-
cpk_scope_info = get_cpk_scope_info(kwargs)
|
3543
|
-
validate_content = kwargs.pop('validate_content', False)
|
3544
|
-
cpk = kwargs.pop('cpk', None)
|
3545
|
-
cpk_info = None
|
3546
|
-
if cpk:
|
3547
|
-
if self.scheme.lower() != 'https':
|
3548
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
3549
|
-
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
|
3550
|
-
encryption_algorithm=cpk.algorithm)
|
3551
|
-
options = {
|
3552
|
-
'body': page[:length],
|
3553
|
-
'content_length': length,
|
3554
|
-
'transactional_content_md5': None,
|
3555
|
-
'timeout': kwargs.pop('timeout', None),
|
3556
|
-
'range': content_range,
|
3557
|
-
'lease_access_conditions': access_conditions,
|
3558
|
-
'sequence_number_access_conditions': seq_conditions,
|
3559
|
-
'modified_access_conditions': mod_conditions,
|
3560
|
-
'validate_content': validate_content,
|
3561
|
-
'cpk_scope_info': cpk_scope_info,
|
3562
|
-
'cpk_info': cpk_info,
|
3563
|
-
'cls': return_response_headers}
|
3564
|
-
options.update(kwargs)
|
3565
|
-
return options
|
3566
|
-
|
3567
2691
|
@distributed_trace
|
3568
|
-
def upload_page(
|
3569
|
-
|
3570
|
-
|
3571
|
-
|
3572
|
-
|
3573
|
-
|
3574
|
-
# type: (...) -> Dict[str, Union[str, datetime]]
|
2692
|
+
def upload_page(
|
2693
|
+
self, page: bytes,
|
2694
|
+
offset: int,
|
2695
|
+
length: int,
|
2696
|
+
**kwargs: Any
|
2697
|
+
) -> Dict[str, Union[str, datetime]]:
|
3575
2698
|
"""The Upload Pages operation writes a range of pages to a page blob.
|
3576
2699
|
|
3577
2700
|
:param bytes page:
|
@@ -3653,85 +2776,28 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
3653
2776
|
:returns: Blob-updated property dict (Etag and last modified).
|
3654
2777
|
:rtype: dict(str, Any)
|
3655
2778
|
"""
|
3656
|
-
|
2779
|
+
if self.require_encryption or (self.key_encryption_key is not None):
|
2780
|
+
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
2781
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
2782
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
2783
|
+
options = _upload_page_options(
|
3657
2784
|
page=page,
|
3658
2785
|
offset=offset,
|
3659
2786
|
length=length,
|
3660
2787
|
**kwargs)
|
3661
2788
|
try:
|
3662
|
-
return self._client.page_blob.upload_pages(**options)
|
2789
|
+
return cast(Dict[str, Any], self._client.page_blob.upload_pages(**options))
|
3663
2790
|
except HttpResponseError as error:
|
3664
2791
|
process_storage_error(error)
|
3665
2792
|
|
3666
|
-
def _upload_pages_from_url_options( # type: ignore
|
3667
|
-
self, source_url, # type: str
|
3668
|
-
offset, # type: int
|
3669
|
-
length, # type: int
|
3670
|
-
source_offset, # type: int
|
3671
|
-
**kwargs
|
3672
|
-
):
|
3673
|
-
# type: (...) -> Dict[str, Any]
|
3674
|
-
if self.require_encryption or (self.key_encryption_key is not None):
|
3675
|
-
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
3676
|
-
|
3677
|
-
# TODO: extract the code to a method format_range
|
3678
|
-
if offset is None or offset % 512 != 0:
|
3679
|
-
raise ValueError("offset must be an integer that aligns with 512 page size")
|
3680
|
-
if length is None or length % 512 != 0:
|
3681
|
-
raise ValueError("length must be an integer that aligns with 512 page size")
|
3682
|
-
if source_offset is None or offset % 512 != 0:
|
3683
|
-
raise ValueError("source_offset must be an integer that aligns with 512 page size")
|
3684
|
-
|
3685
|
-
# Format range
|
3686
|
-
end_range = offset + length - 1
|
3687
|
-
destination_range = f'bytes={offset}-{end_range}'
|
3688
|
-
source_range = f'bytes={source_offset}-{source_offset + length - 1}' # should subtract 1 here?
|
3689
|
-
|
3690
|
-
seq_conditions = SequenceNumberAccessConditions(
|
3691
|
-
if_sequence_number_less_than_or_equal_to=kwargs.pop('if_sequence_number_lte', None),
|
3692
|
-
if_sequence_number_less_than=kwargs.pop('if_sequence_number_lt', None),
|
3693
|
-
if_sequence_number_equal_to=kwargs.pop('if_sequence_number_eq', None)
|
3694
|
-
)
|
3695
|
-
source_authorization = kwargs.pop('source_authorization', None)
|
3696
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
3697
|
-
mod_conditions = get_modify_conditions(kwargs)
|
3698
|
-
source_mod_conditions = get_source_conditions(kwargs)
|
3699
|
-
cpk_scope_info = get_cpk_scope_info(kwargs)
|
3700
|
-
source_content_md5 = kwargs.pop('source_content_md5', None)
|
3701
|
-
cpk = kwargs.pop('cpk', None)
|
3702
|
-
cpk_info = None
|
3703
|
-
if cpk:
|
3704
|
-
if self.scheme.lower() != 'https':
|
3705
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
3706
|
-
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
|
3707
|
-
encryption_algorithm=cpk.algorithm)
|
3708
|
-
|
3709
|
-
options = {
|
3710
|
-
'copy_source_authorization': source_authorization,
|
3711
|
-
'source_url': source_url,
|
3712
|
-
'content_length': 0,
|
3713
|
-
'source_range': source_range,
|
3714
|
-
'range': destination_range,
|
3715
|
-
'source_content_md5': bytearray(source_content_md5) if source_content_md5 else None,
|
3716
|
-
'timeout': kwargs.pop('timeout', None),
|
3717
|
-
'lease_access_conditions': access_conditions,
|
3718
|
-
'sequence_number_access_conditions': seq_conditions,
|
3719
|
-
'modified_access_conditions': mod_conditions,
|
3720
|
-
'source_modified_access_conditions': source_mod_conditions,
|
3721
|
-
'cpk_scope_info': cpk_scope_info,
|
3722
|
-
'cpk_info': cpk_info,
|
3723
|
-
'cls': return_response_headers}
|
3724
|
-
options.update(kwargs)
|
3725
|
-
return options
|
3726
|
-
|
3727
2793
|
@distributed_trace
|
3728
|
-
def upload_pages_from_url(
|
3729
|
-
|
3730
|
-
|
3731
|
-
|
3732
|
-
|
3733
|
-
|
3734
|
-
|
2794
|
+
def upload_pages_from_url(
|
2795
|
+
self, source_url: str,
|
2796
|
+
offset: int,
|
2797
|
+
length: int,
|
2798
|
+
source_offset: int,
|
2799
|
+
**kwargs: Any
|
2800
|
+
) -> Dict[str, Any]:
|
3735
2801
|
"""
|
3736
2802
|
The Upload Pages operation writes a range of pages to a page blob where
|
3737
2803
|
the contents are read from a URL.
|
@@ -3832,59 +2898,24 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
3832
2898
|
:returns: Response after uploading pages from specified URL.
|
3833
2899
|
:rtype: Dict[str, Any]
|
3834
2900
|
"""
|
3835
|
-
|
3836
|
-
|
2901
|
+
if self.require_encryption or (self.key_encryption_key is not None):
|
2902
|
+
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
2903
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
2904
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
2905
|
+
options = _upload_pages_from_url_options(
|
2906
|
+
source_url=source_url,
|
3837
2907
|
offset=offset,
|
3838
2908
|
length=length,
|
3839
2909
|
source_offset=source_offset,
|
3840
2910
|
**kwargs
|
3841
2911
|
)
|
3842
2912
|
try:
|
3843
|
-
return self._client.page_blob.upload_pages_from_url(**options)
|
2913
|
+
return cast(Dict[str, Any], self._client.page_blob.upload_pages_from_url(**options))
|
3844
2914
|
except HttpResponseError as error:
|
3845
2915
|
process_storage_error(error)
|
3846
2916
|
|
3847
|
-
def _clear_page_options(self, offset, length, **kwargs):
|
3848
|
-
# type: (int, int, **Any) -> Dict[str, Any]
|
3849
|
-
if self.require_encryption or (self.key_encryption_key is not None):
|
3850
|
-
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
3851
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
3852
|
-
seq_conditions = SequenceNumberAccessConditions(
|
3853
|
-
if_sequence_number_less_than_or_equal_to=kwargs.pop('if_sequence_number_lte', None),
|
3854
|
-
if_sequence_number_less_than=kwargs.pop('if_sequence_number_lt', None),
|
3855
|
-
if_sequence_number_equal_to=kwargs.pop('if_sequence_number_eq', None)
|
3856
|
-
)
|
3857
|
-
mod_conditions = get_modify_conditions(kwargs)
|
3858
|
-
if offset is None or offset % 512 != 0:
|
3859
|
-
raise ValueError("offset must be an integer that aligns with 512 page size")
|
3860
|
-
if length is None or length % 512 != 0:
|
3861
|
-
raise ValueError("length must be an integer that aligns with 512 page size")
|
3862
|
-
end_range = length + offset - 1 # Reformat to an inclusive range index
|
3863
|
-
content_range = f'bytes={offset}-{end_range}'
|
3864
|
-
|
3865
|
-
cpk = kwargs.pop('cpk', None)
|
3866
|
-
cpk_info = None
|
3867
|
-
if cpk:
|
3868
|
-
if self.scheme.lower() != 'https':
|
3869
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
3870
|
-
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
|
3871
|
-
encryption_algorithm=cpk.algorithm)
|
3872
|
-
|
3873
|
-
options = {
|
3874
|
-
'content_length': 0,
|
3875
|
-
'timeout': kwargs.pop('timeout', None),
|
3876
|
-
'range': content_range,
|
3877
|
-
'lease_access_conditions': access_conditions,
|
3878
|
-
'sequence_number_access_conditions': seq_conditions,
|
3879
|
-
'modified_access_conditions': mod_conditions,
|
3880
|
-
'cpk_info': cpk_info,
|
3881
|
-
'cls': return_response_headers}
|
3882
|
-
options.update(kwargs)
|
3883
|
-
return options
|
3884
|
-
|
3885
2917
|
@distributed_trace
|
3886
|
-
def clear_page(self, offset, length, **kwargs):
|
3887
|
-
# type: (int, int, **Any) -> Dict[str, Union[str, datetime]]
|
2918
|
+
def clear_page(self, offset: int, length: int, **kwargs: Any) -> Dict[str, Union[str, datetime]]:
|
3888
2919
|
"""Clears a range of pages.
|
3889
2920
|
|
3890
2921
|
:param int offset:
|
@@ -3947,73 +2978,26 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
3947
2978
|
:returns: Blob-updated property dict (Etag and last modified).
|
3948
2979
|
:rtype: dict(str, Any)
|
3949
2980
|
"""
|
3950
|
-
|
2981
|
+
if self.require_encryption or (self.key_encryption_key is not None):
|
2982
|
+
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
2983
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
2984
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
2985
|
+
options = _clear_page_options(
|
2986
|
+
offset=offset,
|
2987
|
+
length=length,
|
2988
|
+
**kwargs
|
2989
|
+
)
|
3951
2990
|
try:
|
3952
|
-
return self._client.page_blob.clear_pages(**options)
|
2991
|
+
return cast(Dict[str, Any], self._client.page_blob.clear_pages(**options))
|
3953
2992
|
except HttpResponseError as error:
|
3954
2993
|
process_storage_error(error)
|
3955
2994
|
|
3956
|
-
def _append_block_options( # type: ignore
|
3957
|
-
self, data, # type: Union[bytes, str, Iterable[AnyStr], IO[AnyStr]]
|
3958
|
-
length=None, # type: Optional[int]
|
3959
|
-
**kwargs
|
3960
|
-
):
|
3961
|
-
# type: (...) -> Dict[str, Any]
|
3962
|
-
if self.require_encryption or (self.key_encryption_key is not None):
|
3963
|
-
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
3964
|
-
|
3965
|
-
if isinstance(data, str):
|
3966
|
-
data = data.encode(kwargs.pop('encoding', 'UTF-8')) # type: ignore
|
3967
|
-
if length is None:
|
3968
|
-
length = get_length(data)
|
3969
|
-
if length is None:
|
3970
|
-
length, data = read_length(data)
|
3971
|
-
if length == 0:
|
3972
|
-
return {}
|
3973
|
-
if isinstance(data, bytes):
|
3974
|
-
data = data[:length]
|
3975
|
-
|
3976
|
-
appendpos_condition = kwargs.pop('appendpos_condition', None)
|
3977
|
-
maxsize_condition = kwargs.pop('maxsize_condition', None)
|
3978
|
-
validate_content = kwargs.pop('validate_content', False)
|
3979
|
-
append_conditions = None
|
3980
|
-
if maxsize_condition or appendpos_condition is not None:
|
3981
|
-
append_conditions = AppendPositionAccessConditions(
|
3982
|
-
max_size=maxsize_condition,
|
3983
|
-
append_position=appendpos_condition
|
3984
|
-
)
|
3985
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
3986
|
-
mod_conditions = get_modify_conditions(kwargs)
|
3987
|
-
cpk_scope_info = get_cpk_scope_info(kwargs)
|
3988
|
-
cpk = kwargs.pop('cpk', None)
|
3989
|
-
cpk_info = None
|
3990
|
-
if cpk:
|
3991
|
-
if self.scheme.lower() != 'https':
|
3992
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
3993
|
-
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
|
3994
|
-
encryption_algorithm=cpk.algorithm)
|
3995
|
-
options = {
|
3996
|
-
'body': data,
|
3997
|
-
'content_length': length,
|
3998
|
-
'timeout': kwargs.pop('timeout', None),
|
3999
|
-
'transactional_content_md5': None,
|
4000
|
-
'lease_access_conditions': access_conditions,
|
4001
|
-
'append_position_access_conditions': append_conditions,
|
4002
|
-
'modified_access_conditions': mod_conditions,
|
4003
|
-
'validate_content': validate_content,
|
4004
|
-
'cpk_scope_info': cpk_scope_info,
|
4005
|
-
'cpk_info': cpk_info,
|
4006
|
-
'cls': return_response_headers}
|
4007
|
-
options.update(kwargs)
|
4008
|
-
return options
|
4009
|
-
|
4010
2995
|
@distributed_trace
|
4011
|
-
def append_block(
|
4012
|
-
|
4013
|
-
|
4014
|
-
|
4015
|
-
|
4016
|
-
# type: (...) -> Dict[str, Union[str, datetime, int]]
|
2996
|
+
def append_block(
|
2997
|
+
self, data: Union[bytes, str, Iterable[AnyStr], IO[AnyStr]],
|
2998
|
+
length: Optional[int] = None,
|
2999
|
+
**kwargs: Any
|
3000
|
+
) -> Dict[str, Union[str, datetime, int]]:
|
4017
3001
|
"""Commits a new block of data to the end of the existing append blob.
|
4018
3002
|
|
4019
3003
|
:param data:
|
@@ -4091,83 +3075,27 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
4091
3075
|
:returns: Blob-updated property dict (Etag, last modified, append offset, committed block count).
|
4092
3076
|
:rtype: dict(str, Any)
|
4093
3077
|
"""
|
4094
|
-
|
4095
|
-
|
3078
|
+
if self.require_encryption or (self.key_encryption_key is not None):
|
3079
|
+
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
3080
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
3081
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
3082
|
+
options = _append_block_options(
|
3083
|
+
data=data,
|
4096
3084
|
length=length,
|
4097
3085
|
**kwargs
|
4098
3086
|
)
|
4099
3087
|
try:
|
4100
|
-
return self._client.append_blob.append_block(**options)
|
3088
|
+
return cast(Dict[str, Any], self._client.append_blob.append_block(**options))
|
4101
3089
|
except HttpResponseError as error:
|
4102
3090
|
process_storage_error(error)
|
4103
3091
|
|
4104
|
-
def _append_block_from_url_options( # type: ignore
|
4105
|
-
self, copy_source_url, # type: str
|
4106
|
-
source_offset=None, # type: Optional[int]
|
4107
|
-
source_length=None, # type: Optional[int]
|
4108
|
-
**kwargs
|
4109
|
-
):
|
4110
|
-
# type: (...) -> Dict[str, Any]
|
4111
|
-
if self.require_encryption or (self.key_encryption_key is not None):
|
4112
|
-
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
4113
|
-
|
4114
|
-
# If end range is provided, start range must be provided
|
4115
|
-
if source_length is not None and source_offset is None:
|
4116
|
-
raise ValueError("source_offset should also be specified if source_length is specified")
|
4117
|
-
# Format based on whether length is present
|
4118
|
-
source_range = None
|
4119
|
-
if source_length is not None:
|
4120
|
-
end_range = source_offset + source_length - 1
|
4121
|
-
source_range = f'bytes={source_offset}-{end_range}'
|
4122
|
-
elif source_offset is not None:
|
4123
|
-
source_range = f"bytes={source_offset}-"
|
4124
|
-
|
4125
|
-
appendpos_condition = kwargs.pop('appendpos_condition', None)
|
4126
|
-
maxsize_condition = kwargs.pop('maxsize_condition', None)
|
4127
|
-
source_content_md5 = kwargs.pop('source_content_md5', None)
|
4128
|
-
append_conditions = None
|
4129
|
-
if maxsize_condition or appendpos_condition is not None:
|
4130
|
-
append_conditions = AppendPositionAccessConditions(
|
4131
|
-
max_size=maxsize_condition,
|
4132
|
-
append_position=appendpos_condition
|
4133
|
-
)
|
4134
|
-
source_authorization = kwargs.pop('source_authorization', None)
|
4135
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
4136
|
-
mod_conditions = get_modify_conditions(kwargs)
|
4137
|
-
source_mod_conditions = get_source_conditions(kwargs)
|
4138
|
-
cpk_scope_info = get_cpk_scope_info(kwargs)
|
4139
|
-
cpk = kwargs.pop('cpk', None)
|
4140
|
-
cpk_info = None
|
4141
|
-
if cpk:
|
4142
|
-
if self.scheme.lower() != 'https':
|
4143
|
-
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
4144
|
-
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
|
4145
|
-
encryption_algorithm=cpk.algorithm)
|
4146
|
-
|
4147
|
-
options = {
|
4148
|
-
'copy_source_authorization': source_authorization,
|
4149
|
-
'source_url': copy_source_url,
|
4150
|
-
'content_length': 0,
|
4151
|
-
'source_range': source_range,
|
4152
|
-
'source_content_md5': source_content_md5,
|
4153
|
-
'transactional_content_md5': None,
|
4154
|
-
'lease_access_conditions': access_conditions,
|
4155
|
-
'append_position_access_conditions': append_conditions,
|
4156
|
-
'modified_access_conditions': mod_conditions,
|
4157
|
-
'source_modified_access_conditions': source_mod_conditions,
|
4158
|
-
'cpk_scope_info': cpk_scope_info,
|
4159
|
-
'cpk_info': cpk_info,
|
4160
|
-
'cls': return_response_headers,
|
4161
|
-
'timeout': kwargs.pop('timeout', None)}
|
4162
|
-
options.update(kwargs)
|
4163
|
-
return options
|
4164
|
-
|
4165
3092
|
@distributed_trace
|
4166
|
-
def append_block_from_url(
|
4167
|
-
|
4168
|
-
|
4169
|
-
|
4170
|
-
|
3093
|
+
def append_block_from_url(
|
3094
|
+
self, copy_source_url: str,
|
3095
|
+
source_offset: Optional[int] = None,
|
3096
|
+
source_length: Optional[int] = None,
|
3097
|
+
**kwargs: Any
|
3098
|
+
) -> Dict[str, Union[str, datetime, int]]:
|
4171
3099
|
"""
|
4172
3100
|
Creates a new block to be committed as part of a blob, where the contents are read from a source url.
|
4173
3101
|
|
@@ -4262,43 +3190,24 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
4262
3190
|
:returns: Result after appending a new block.
|
4263
3191
|
:rtype: Dict[str, Union[str, datetime, int]]
|
4264
3192
|
"""
|
4265
|
-
|
4266
|
-
|
3193
|
+
if self.require_encryption or (self.key_encryption_key is not None):
|
3194
|
+
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
3195
|
+
if kwargs.get('cpk') and self.scheme.lower() != 'https':
|
3196
|
+
raise ValueError("Customer provided encryption key must be used over HTTPS.")
|
3197
|
+
options = _append_block_from_url_options(
|
3198
|
+
copy_source_url=copy_source_url,
|
4267
3199
|
source_offset=source_offset,
|
4268
3200
|
source_length=source_length,
|
4269
3201
|
**kwargs
|
4270
3202
|
)
|
4271
3203
|
try:
|
4272
|
-
return
|
3204
|
+
return cast(Dict[str, Union[str, datetime, int]],
|
3205
|
+
self._client.append_blob.append_block_from_url(**options))
|
4273
3206
|
except HttpResponseError as error:
|
4274
3207
|
process_storage_error(error)
|
4275
3208
|
|
4276
|
-
def _seal_append_blob_options(self, **kwargs):
|
4277
|
-
# type: (...) -> Dict[str, Any]
|
4278
|
-
if self.require_encryption or (self.key_encryption_key is not None):
|
4279
|
-
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
4280
|
-
|
4281
|
-
appendpos_condition = kwargs.pop('appendpos_condition', None)
|
4282
|
-
append_conditions = None
|
4283
|
-
if appendpos_condition is not None:
|
4284
|
-
append_conditions = AppendPositionAccessConditions(
|
4285
|
-
append_position=appendpos_condition
|
4286
|
-
)
|
4287
|
-
access_conditions = get_access_conditions(kwargs.pop('lease', None))
|
4288
|
-
mod_conditions = get_modify_conditions(kwargs)
|
4289
|
-
|
4290
|
-
options = {
|
4291
|
-
'timeout': kwargs.pop('timeout', None),
|
4292
|
-
'lease_access_conditions': access_conditions,
|
4293
|
-
'append_position_access_conditions': append_conditions,
|
4294
|
-
'modified_access_conditions': mod_conditions,
|
4295
|
-
'cls': return_response_headers}
|
4296
|
-
options.update(kwargs)
|
4297
|
-
return options
|
4298
|
-
|
4299
3209
|
@distributed_trace
|
4300
|
-
def seal_append_blob(self, **kwargs):
|
4301
|
-
# type: (...) -> Dict[str, Union[str, datetime, int]]
|
3210
|
+
def seal_append_blob(self, **kwargs: Any) -> Dict[str, Union[str, datetime, int]]:
|
4302
3211
|
"""The Seal operation seals the Append Blob to make it read-only.
|
4303
3212
|
|
4304
3213
|
.. versionadded:: 12.4.0
|
@@ -4339,15 +3248,16 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
4339
3248
|
:returns: Blob-updated property dict (Etag, last modified, append offset, committed block count).
|
4340
3249
|
:rtype: dict(str, Any)
|
4341
3250
|
"""
|
4342
|
-
|
3251
|
+
if self.require_encryption or (self.key_encryption_key is not None):
|
3252
|
+
raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
|
3253
|
+
options = _seal_append_blob_options(**kwargs)
|
4343
3254
|
try:
|
4344
|
-
return self._client.append_blob.seal(**options)
|
3255
|
+
return cast(Dict[str, Any], self._client.append_blob.seal(**options))
|
4345
3256
|
except HttpResponseError as error:
|
4346
3257
|
process_storage_error(error)
|
4347
3258
|
|
4348
3259
|
@distributed_trace
|
4349
|
-
def _get_container_client(self)
|
4350
|
-
# type: (...) -> ContainerClient
|
3260
|
+
def _get_container_client(self) -> "ContainerClient":
|
4351
3261
|
"""Get a client to interact with the blob's parent container.
|
4352
3262
|
|
4353
3263
|
The container need not already exist. Defaults to current blob's credentials.
|
@@ -4371,7 +3281,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
|
|
4371
3281
|
policies=self._pipeline._impl_policies # pylint: disable = protected-access
|
4372
3282
|
)
|
4373
3283
|
else:
|
4374
|
-
_pipeline = self._pipeline
|
3284
|
+
_pipeline = self._pipeline # pylint: disable = protected-access
|
4375
3285
|
return ContainerClient(
|
4376
3286
|
f"{self.scheme}://{self.primary_hostname}", container_name=self.container_name,
|
4377
3287
|
credential=self._raw_credential, api_version=self.api_version, _configuration=self._config,
|