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.
Files changed (42) hide show
  1. azure/storage/blob/__init__.py +19 -18
  2. azure/storage/blob/_blob_client.py +417 -1507
  3. azure/storage/blob/_blob_client_helpers.py +1242 -0
  4. azure/storage/blob/_blob_service_client.py +82 -101
  5. azure/storage/blob/_blob_service_client_helpers.py +27 -0
  6. azure/storage/blob/_container_client.py +147 -356
  7. azure/storage/blob/_container_client_helpers.py +261 -0
  8. azure/storage/blob/_deserialize.py +68 -44
  9. azure/storage/blob/_download.py +114 -90
  10. azure/storage/blob/_encryption.py +14 -7
  11. azure/storage/blob/_lease.py +47 -58
  12. azure/storage/blob/_list_blobs_helper.py +129 -135
  13. azure/storage/blob/_models.py +479 -276
  14. azure/storage/blob/_quick_query_helper.py +30 -31
  15. azure/storage/blob/_serialize.py +38 -56
  16. azure/storage/blob/_shared/avro/datafile.py +1 -1
  17. azure/storage/blob/_shared/avro/datafile_async.py +1 -1
  18. azure/storage/blob/_shared/base_client.py +1 -1
  19. azure/storage/blob/_shared/base_client_async.py +1 -1
  20. azure/storage/blob/_shared/policies.py +8 -6
  21. azure/storage/blob/_shared/policies_async.py +3 -1
  22. azure/storage/blob/_shared/response_handlers.py +6 -2
  23. azure/storage/blob/_shared/shared_access_signature.py +2 -2
  24. azure/storage/blob/_shared/uploads.py +1 -1
  25. azure/storage/blob/_shared/uploads_async.py +1 -1
  26. azure/storage/blob/_shared_access_signature.py +70 -53
  27. azure/storage/blob/_upload_helpers.py +75 -68
  28. azure/storage/blob/_version.py +1 -1
  29. azure/storage/blob/aio/__init__.py +19 -11
  30. azure/storage/blob/aio/_blob_client_async.py +505 -255
  31. azure/storage/blob/aio/_blob_service_client_async.py +138 -87
  32. azure/storage/blob/aio/_container_client_async.py +260 -120
  33. azure/storage/blob/aio/_download_async.py +104 -87
  34. azure/storage/blob/aio/_lease_async.py +56 -55
  35. azure/storage/blob/aio/_list_blobs_helper.py +94 -96
  36. azure/storage/blob/aio/_models.py +60 -38
  37. azure/storage/blob/aio/_upload_helpers.py +75 -66
  38. {azure_storage_blob-12.21.0.dist-info → azure_storage_blob-12.22.0.dist-info}/METADATA +1 -1
  39. {azure_storage_blob-12.21.0.dist-info → azure_storage_blob-12.22.0.dist-info}/RECORD +42 -39
  40. {azure_storage_blob-12.21.0.dist-info → azure_storage_blob-12.22.0.dist-info}/LICENSE +0 -0
  41. {azure_storage_blob-12.21.0.dist-info → azure_storage_blob-12.22.0.dist-info}/WHEEL +0 -0
  42. {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, AsyncIterable, Dict, IO, Iterable, List, Optional, overload, Tuple, Union,
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 ResourceNotFoundError, HttpResponseError, ResourceExistsError
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 ._shared import encode_base64
24
- from ._shared.base_client import StorageAccountHostsMixin, parse_connection_str, parse_query, TransportWrapper
25
- from ._shared.uploads import IterStreamer
26
- from ._shared.uploads_async import AsyncIterStreamer
27
- from ._shared.request_handlers import (
28
- add_metadata_headers,
29
- get_length,
30
- read_length,
31
- validate_and_format_range_headers)
32
- from ._shared.response_handlers import return_response_headers, process_storage_error, return_headers_and_deserialized
33
- from ._generated import AzureBlobStorage
34
- from ._generated.models import (
35
- DeleteSnapshotsOptionType,
36
- BlobHTTPHeaders,
37
- BlockLookupList,
38
- AppendPositionAccessConditions,
39
- SequenceNumberAccessConditions,
40
- QueryRequest,
41
- CpkInfo)
42
- from ._serialize import (
43
- get_modify_conditions,
44
- get_source_conditions,
45
- get_cpk_scope_info,
46
- get_api_version,
47
- get_version_id,
48
- serialize_blob_tags_header,
49
- serialize_blob_tags,
50
- serialize_query_format, get_access_conditions
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
- deserialize_blob_stream,
56
- parse_tags,
57
- deserialize_pipeline_response_into_cls
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 modify_user_agent_for_encryption, StorageEncryptionMixin
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 BlobType, BlobBlock, BlobProperties, BlobQueryError, QuickQueryDialect, \
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
- upload_page_blob,
69
- _any_conditions
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 datetime import datetime
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
- StandardBlobTier,
81
- SequenceNumberAction
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
- self, account_url: str,
161
- container_name: str,
162
- blob_name: str,
163
- snapshot: Optional[Union[str, Dict[str, Any]]] = None,
164
- credential: Optional[Union[str, Dict[str, str], "AzureNamedKeyCredential", "AzureSasCredential", "TokenCredential"]] = None, # pylint: disable=line-too-long
165
- **kwargs: Any
166
- ) -> None:
167
- try:
168
- if not account_url.lower().startswith('http'):
169
- account_url = "https://" + account_url
170
- except AttributeError as exc:
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
- try:
184
- self.snapshot = snapshot.snapshot # type: ignore
185
- except AttributeError:
186
- try:
187
- self.snapshot = snapshot['snapshot'] # type: ignore
188
- except TypeError:
189
- self.snapshot = snapshot or path_snapshot
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
- container_name = self.container_name
202
- if isinstance(container_name, str):
203
- container_name = container_name.encode('UTF-8')
204
- return f"{self.scheme}://{hostname}/{quote(container_name)}/{quote(self.blob_name, safe='~/')}{self._query_str}"
205
-
206
- def _encode_source_url(self, source_url):
207
- parsed_source_url = urlparse(source_url)
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
- cls, blob_url: str,
220
- credential: Optional[Union[str, Dict[str, str], "AzureNamedKeyCredential", "AzureSasCredential", "TokenCredential"]] = None, # pylint: disable=line-too-long
221
- snapshot: Optional[Union[str, Dict[str, Any]]] = None,
222
- **kwargs: Any
223
- ) -> Self:
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
- try:
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
- cls, conn_str: str,
307
- container_name: str,
308
- blob_name: str,
309
- snapshot: Optional[Union[str, Dict[str, Any]]] = None,
310
- credential: Optional[Union[str, Dict[str, str], "AzureNamedKeyCredential", "AzureSasCredential", "TokenCredential"]] = None, # pylint: disable=line-too-long
311
- **kwargs: Any
312
- ) -> Self:
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) # type: ignore
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: Response from creating a new block blob for a given URL.
418
+ :returns: Blob-updated property Dict (Etag and last modified)
613
419
  :rtype: Dict[str, Any]
614
420
  """
615
- options = self._upload_blob_from_url_options(
616
- source_url=self._encode_source_url(source_url),
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
- self, data: Union[bytes, str, Iterable[AnyStr], IO[bytes]],
626
- blob_type: Union[str, BlobType] = BlobType.BlockBlob,
627
- length: Optional[int] = None,
628
- metadata: Optional[Dict[str, str]] = None,
629
- **kwargs
630
- ) -> Dict[str, Any]:
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 dict (Etag and last modified)
757
- :rtype: dict[str, Any]
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
- options = self._upload_blob_options(
769
- data,
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
- self, offset: int = None,
836
- length: int = None,
837
- *,
838
- encoding: str,
839
- **kwargs) -> StorageStreamDownloader[str]:
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
- self, offset: int = None,
845
- length: int = None,
846
- *,
847
- encoding: None = None,
848
- **kwargs) -> StorageStreamDownloader[bytes]:
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
- self, offset: int = None,
854
- length: int = None,
855
- *,
856
- encoding: Optional[str] = None,
857
- **kwargs) -> StorageStreamDownloader:
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
- options = self._download_blob_options(
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 list[~azure.storage.blob.ArrowDialect] or ~azure.storage.blob.QuickQueryDialect or str
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
- options, delimiter = self._quick_query_options(query_expression, **kwargs)
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 = self._delete_blob_options(delete_snapshots=delete_snapshots, **kwargs)
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 # type: ignore
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 = self._set_http_headers_options(content_settings=content_settings, **kwargs)
1132
+ options = _set_http_headers_options(content_settings=content_settings, **kwargs)
1460
1133
  try:
1461
- return self._client.blob.set_http_headers(**options) # type: ignore
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(self, metadata=None, **kwargs):
1493
- # type: (Optional[Dict[str, str]], **Any) -> Dict[str, Union[str, datetime]]
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
- options = self._set_blob_metadata_options(metadata=metadata, **kwargs)
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) # type: ignore
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(self, immutability_policy, **kwargs):
1558
- # type: (ImmutabilityPolicy, **Any) -> Dict[str, str]
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 self._client.blob.set_legal_hold(legal_hold, cls=return_response_headers, **kwargs)
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( # type: ignore
1694
- self, size, # type: int
1695
- content_settings=None, # type: Optional[ContentSettings]
1696
- metadata=None, # type: Optional[Dict[str, str]]
1697
- premium_page_blob_tier=None, # type: Optional[Union[str, PremiumPageBlobTier]]
1698
- **kwargs
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
- options = self._create_page_blob_options(
1786
- size,
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) # type: ignore
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(self, content_settings=None, metadata=None, **kwargs):
1847
- # type: (Optional[ContentSettings], Optional[Dict[str, str]], **Any) -> Dict[str, Union[str, datetime]]
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
- options = self._create_append_blob_options(
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) # type: ignore
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(self, metadata=None, **kwargs):
1960
- # type: (Optional[Dict[str, str]], **Any) -> Dict[str, Union[str, datetime]]
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
- options = self._create_snapshot_options(metadata=metadata, **kwargs)
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) # type: ignore
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(self, source_url, metadata=None, incremental_copy=False, **kwargs):
2116
- # type: (str, Optional[Dict[str, str]], bool, **Any) -> Dict[str, Union[str, datetime]]
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 = self._start_copy_from_url_options(
2294
- source_url=self._encode_source_url(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(self, copy_id, **kwargs):
2324
- # type: (Union[str, Dict[str, Any], BlobProperties], **Any) -> None
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 = self._abort_copy_options(copy_id, **kwargs)
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) # type: ignore
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
- self, block_id, # type: str
2523
- data, # type: Union[Iterable[AnyStr], IO[AnyStr]]
2524
- length=None, # type: Optional[int]
2525
- **kwargs
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
- options = self._stage_block_options(
2574
- block_id,
2575
- data,
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
- self, block_id, # type: Union[str, int]
2630
- source_url, # type: str
2631
- source_offset=None, # type: Optional[int]
2632
- source_length=None, # type: Optional[int]
2633
- source_content_md5=None, # type: Optional[Union[bytes, bytearray]]
2634
- **kwargs
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
- options = self._stage_block_from_url_options(
2681
- block_id,
2682
- source_url=self._encode_source_url(source_url),
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(self, block_list_type="committed", **kwargs):
2704
- # type: (Optional[str], **Any) -> Tuple[List[BlobBlock], List[BlobBlock]]
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: tuple(list(~azure.storage.blob.BlobBlock), list(~azure.storage.blob.BlobBlock))
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 self._get_block_list_result(blocks)
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( # type: ignore
2816
- self, block_list, # type: List[BlobBlock]
2817
- content_settings=None, # type: Optional[ContentSettings]
2818
- metadata=None, # type: Optional[Dict[str, str]]
2819
- **kwargs
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
- options = self._commit_block_list_options(
2914
- block_list,
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) # type: ignore
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
- options = self._set_blob_tags_options(tags=tags, **kwargs)
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
- options = self._get_blob_tags_options(**kwargs)
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) # pylint: disable=protected-access
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( # type: ignore
3114
- self, offset=None, # type: Optional[int]
3115
- length=None, # type: Optional[int]
3116
- previous_snapshot_diff=None, # type: Optional[Union[str, Dict[str, Any]]]
3117
- **kwargs
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 = self._get_page_ranges_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
- self,
3201
- *,
3202
- offset: Optional[int] = None,
3203
- length: Optional[int] = None,
3204
- previous_snapshot: Optional[Union[str, Dict[str, Any]]] = None,
3205
- **kwargs: Any
3206
- ) -> ItemPaged[PageRange]:
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 = self._get_page_ranges_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
- self, previous_snapshot_url, # type: str
3291
- offset=None, # type: Optional[int]
3292
- length=None, # type: Optional[int]
3293
- **kwargs
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 = self._get_page_ranges_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(self, sequence_number_action, sequence_number=None, **kwargs):
3383
- # type: (Union[str, SequenceNumberAction], Optional[str], **Any) -> Dict[str, Union[str, datetime]]
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 = self._set_sequence_number_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) # type: ignore
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
- options = self._resize_blob_options(size, **kwargs)
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) # type: ignore
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( # type: ignore
3569
- self, page, # type: bytes
3570
- offset, # type: int
3571
- length, # type: int
3572
- **kwargs
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
- options = self._upload_page_options(
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) # type: ignore
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(self, source_url, # type: str
3729
- offset, # type: int
3730
- length, # type: int
3731
- source_offset, # type: int
3732
- **kwargs
3733
- ):
3734
- # type: (...) -> Dict[str, Any]
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
- options = self._upload_pages_from_url_options(
3836
- source_url=self._encode_source_url(source_url),
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) # type: ignore
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
- options = self._clear_page_options(offset, length, **kwargs)
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) # type: ignore
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( # type: ignore
4012
- self, data, # type: Union[bytes, str, Iterable[AnyStr], IO[AnyStr]]
4013
- length=None, # type: Optional[int]
4014
- **kwargs
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
- options = self._append_block_options(
4095
- data,
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) # type: ignore
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(self, copy_source_url, # type: str
4167
- source_offset=None, # type: Optional[int]
4168
- source_length=None, # type: Optional[int]
4169
- **kwargs):
4170
- # type: (...) -> Dict[str, Union[str, datetime, int]]
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
- options = self._append_block_from_url_options(
4266
- copy_source_url=self._encode_source_url(copy_source_url),
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 self._client.append_blob.append_block_from_url(**options) # type: ignore
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
- options = self._seal_append_blob_options(**kwargs)
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) # type: ignore
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): # pylint: disable=client-method-missing-kwargs
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 # pylint: disable = protected-access
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,