azure-storage-blob 12.21.0b1__py3-none-any.whl → 12.23.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 (60) hide show
  1. azure/storage/blob/__init__.py +19 -18
  2. azure/storage/blob/_blob_client.py +470 -1555
  3. azure/storage/blob/_blob_client_helpers.py +1242 -0
  4. azure/storage/blob/_blob_service_client.py +93 -112
  5. azure/storage/blob/_blob_service_client_helpers.py +27 -0
  6. azure/storage/blob/_container_client.py +176 -377
  7. azure/storage/blob/_container_client_helpers.py +266 -0
  8. azure/storage/blob/_deserialize.py +68 -44
  9. azure/storage/blob/_download.py +375 -241
  10. azure/storage/blob/_encryption.py +14 -7
  11. azure/storage/blob/_generated/_azure_blob_storage.py +2 -1
  12. azure/storage/blob/_generated/_serialization.py +2 -0
  13. azure/storage/blob/_generated/aio/_azure_blob_storage.py +2 -1
  14. azure/storage/blob/_generated/aio/operations/_append_blob_operations.py +1 -7
  15. azure/storage/blob/_generated/aio/operations/_blob_operations.py +21 -47
  16. azure/storage/blob/_generated/aio/operations/_block_blob_operations.py +2 -10
  17. azure/storage/blob/_generated/aio/operations/_container_operations.py +13 -26
  18. azure/storage/blob/_generated/aio/operations/_page_blob_operations.py +3 -14
  19. azure/storage/blob/_generated/aio/operations/_service_operations.py +14 -17
  20. azure/storage/blob/_generated/operations/_append_blob_operations.py +1 -7
  21. azure/storage/blob/_generated/operations/_blob_operations.py +21 -47
  22. azure/storage/blob/_generated/operations/_block_blob_operations.py +2 -10
  23. azure/storage/blob/_generated/operations/_container_operations.py +13 -26
  24. azure/storage/blob/_generated/operations/_page_blob_operations.py +3 -14
  25. azure/storage/blob/_generated/operations/_service_operations.py +14 -17
  26. azure/storage/blob/_generated/py.typed +1 -0
  27. azure/storage/blob/_lease.py +52 -63
  28. azure/storage/blob/_list_blobs_helper.py +129 -135
  29. azure/storage/blob/_models.py +480 -277
  30. azure/storage/blob/_quick_query_helper.py +30 -31
  31. azure/storage/blob/_serialize.py +39 -56
  32. azure/storage/blob/_shared/avro/datafile.py +1 -1
  33. azure/storage/blob/_shared/avro/datafile_async.py +1 -1
  34. azure/storage/blob/_shared/base_client.py +3 -1
  35. azure/storage/blob/_shared/base_client_async.py +1 -1
  36. azure/storage/blob/_shared/policies.py +16 -15
  37. azure/storage/blob/_shared/policies_async.py +21 -6
  38. azure/storage/blob/_shared/response_handlers.py +6 -2
  39. azure/storage/blob/_shared/shared_access_signature.py +21 -3
  40. azure/storage/blob/_shared/uploads.py +1 -1
  41. azure/storage/blob/_shared/uploads_async.py +1 -1
  42. azure/storage/blob/_shared_access_signature.py +110 -52
  43. azure/storage/blob/_upload_helpers.py +75 -68
  44. azure/storage/blob/_version.py +1 -1
  45. azure/storage/blob/aio/__init__.py +19 -11
  46. azure/storage/blob/aio/_blob_client_async.py +554 -301
  47. azure/storage/blob/aio/_blob_service_client_async.py +148 -97
  48. azure/storage/blob/aio/_container_client_async.py +289 -140
  49. azure/storage/blob/aio/_download_async.py +485 -337
  50. azure/storage/blob/aio/_lease_async.py +61 -60
  51. azure/storage/blob/aio/_list_blobs_helper.py +94 -96
  52. azure/storage/blob/aio/_models.py +60 -38
  53. azure/storage/blob/aio/_upload_helpers.py +75 -66
  54. {azure_storage_blob-12.21.0b1.dist-info → azure_storage_blob-12.23.0.dist-info}/METADATA +7 -7
  55. azure_storage_blob-12.23.0.dist-info/RECORD +84 -0
  56. {azure_storage_blob-12.21.0b1.dist-info → azure_storage_blob-12.23.0.dist-info}/WHEEL +1 -1
  57. azure/storage/blob/_generated/_vendor.py +0 -16
  58. azure_storage_blob-12.21.0b1.dist-info/RECORD +0 -81
  59. {azure_storage_blob-12.21.0b1.dist-info → azure_storage_blob-12.23.0.dist-info}/LICENSE +0 -0
  60. {azure_storage_blob-12.21.0b1.dist-info → azure_storage_blob-12.23.0.dist-info}/top_level.txt +0 -0
@@ -5,93 +5,96 @@
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.
91
94
 
92
95
  For more optional configuration, please click
93
96
  `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
94
- #optional-configuration>`_.
97
+ #optional-configuration>`__.
95
98
 
96
99
  :param str account_url:
97
100
  The URI to the storage account. In order to create a client given the full URI to the blob,
@@ -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.
@@ -522,9 +328,9 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
522
328
  :param str source_url:
523
329
  A URL of up to 2 KB in length that specifies a file or blob.
524
330
  The value should be URL-encoded as it would appear in a request URI.
525
- If the source is in another account, the source must either be public
526
- or must be authenticated via a shared access signature. If the source
527
- is public, no authentication is required.
331
+ The source must either be public or must be authenticated via a shared
332
+ access signature as part of the url or using the source_authorization keyword.
333
+ If the source is public, no authentication is required.
528
334
  Examples:
529
335
  https://myaccount.blob.core.windows.net/mycontainer/myblob
530
336
 
@@ -541,7 +347,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
541
347
  The tag set may contain at most 10 tags. Tag keys must be between 1 and 128 characters,
542
348
  and tag values must be between 0 and 256 characters.
543
349
  Valid tag key and value characters include: lowercase and uppercase letters, digits (0-9),
544
- space (` `), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)
350
+ space (' '), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)
545
351
  :paramtype tags: dict(str, str)
546
352
  :keyword bytearray source_content_md5:
547
353
  Specify the md5 that is used to verify the integrity of the source bytes.
@@ -589,7 +395,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
589
395
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
590
396
  This value is not tracked or validated on the client. To configure client-side network timesouts
591
397
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
592
- #other-client--per-operation-configuration>`_.
398
+ #other-client--per-operation-configuration>`__.
593
399
  :keyword ~azure.storage.blob.ContentSettings content_settings:
594
400
  ContentSettings object used to set blob properties. Used to set content type, encoding,
595
401
  language, disposition, md5, and cache control.
@@ -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.
@@ -645,7 +453,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
645
453
  The tag set may contain at most 10 tags. Tag keys must be between 1 and 128 characters,
646
454
  and tag values must be between 0 and 256 characters.
647
455
  Valid tag key and value characters include: lowercase and uppercase letters, digits (0-9),
648
- space (` `), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)
456
+ space (' '), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)
649
457
 
650
458
  .. versionadded:: 12.4.0
651
459
 
@@ -751,10 +559,10 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
751
559
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
752
560
  This value is not tracked or validated on the client. To configure client-side network timesouts
753
561
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
754
- #other-client--per-operation-configuration>`_. This method may make multiple calls to the service and
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.
@@ -870,6 +641,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
870
641
  value that, when present, specifies the version of the blob to download.
871
642
 
872
643
  .. versionadded:: 12.4.0
644
+
873
645
  This keyword argument was introduced in API version '2019-12-12'.
874
646
 
875
647
  :keyword bool validate_content:
@@ -916,7 +688,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
916
688
  a secure connection must be established to transfer the key.
917
689
  :keyword int max_concurrency:
918
690
  The number of parallel connections with which to download.
919
- :keyword str encoding:
691
+ :keyword Optional[str] encoding:
920
692
  Encoding to decode the downloaded bytes. Default is None, i.e. no decoding.
921
693
  :keyword progress_hook:
922
694
  A callback to track the progress of a long running download. The signature is
@@ -928,7 +700,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
928
700
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
929
701
  This value is not tracked or validated on the client. To configure client-side network timesouts
930
702
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
931
- #other-client--per-operation-configuration>`_. This method may make multiple calls to the service and
703
+ #other-client--per-operation-configuration>`__. This method may make multiple calls to the service and
932
704
  the timeout will apply to each call individually.
933
705
  multiple calls to the Azure service and the timeout will apply to
934
706
  each call individually.
@@ -944,82 +716,33 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
944
716
  :dedent: 12
945
717
  :caption: Download a blob.
946
718
  """
947
- 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),
948
729
  offset=offset,
949
730
  length=length,
950
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,
951
741
  **kwargs)
952
742
  return StorageStreamDownloader(**options)
953
743
 
954
- def _quick_query_options(self, query_expression,
955
- **kwargs):
956
- # type: (str, **Any) -> Dict[str, Any]
957
- delimiter = '\n'
958
- input_format = kwargs.pop('blob_format', None)
959
- if input_format == QuickQueryDialect.DelimitedJson:
960
- input_format = DelimitedJsonDialect()
961
- if input_format == QuickQueryDialect.DelimitedText:
962
- input_format = DelimitedTextDialect()
963
- input_parquet_format = input_format == "ParquetDialect"
964
- if input_format and not input_parquet_format:
965
- try:
966
- delimiter = input_format.lineterminator
967
- except AttributeError:
968
- try:
969
- delimiter = input_format.delimiter
970
- except AttributeError as exc:
971
- raise ValueError("The Type of blob_format can only be DelimitedTextDialect or "
972
- "DelimitedJsonDialect or ParquetDialect") from exc
973
- output_format = kwargs.pop('output_format', None)
974
- if output_format == QuickQueryDialect.DelimitedJson:
975
- output_format = DelimitedJsonDialect()
976
- if output_format == QuickQueryDialect.DelimitedText:
977
- output_format = DelimitedTextDialect()
978
- if output_format:
979
- if output_format == "ParquetDialect":
980
- raise ValueError("ParquetDialect is invalid as an output format.")
981
- try:
982
- delimiter = output_format.lineterminator
983
- except AttributeError:
984
- try:
985
- delimiter = output_format.delimiter
986
- except AttributeError:
987
- pass
988
- else:
989
- output_format = input_format if not input_parquet_format else None
990
- query_request = QueryRequest(
991
- expression=query_expression,
992
- input_serialization=serialize_query_format(input_format),
993
- output_serialization=serialize_query_format(output_format)
994
- )
995
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
996
- mod_conditions = get_modify_conditions(kwargs)
997
-
998
- cpk = kwargs.pop('cpk', None)
999
- cpk_info = None
1000
- if cpk:
1001
- if self.scheme.lower() != 'https':
1002
- raise ValueError("Customer provided encryption key must be used over HTTPS.")
1003
- cpk_info = CpkInfo(
1004
- encryption_key=cpk.key_value,
1005
- encryption_key_sha256=cpk.key_hash,
1006
- encryption_algorithm=cpk.algorithm
1007
- )
1008
- options = {
1009
- 'query_request': query_request,
1010
- 'lease_access_conditions': access_conditions,
1011
- 'modified_access_conditions': mod_conditions,
1012
- 'cpk_info': cpk_info,
1013
- 'snapshot': self.snapshot,
1014
- 'timeout': kwargs.pop('timeout', None),
1015
- 'cls': return_headers_and_deserialized,
1016
- }
1017
- options.update(kwargs)
1018
- return options, delimiter
1019
-
1020
744
  @distributed_trace
1021
- def query_blob(self, query_expression, **kwargs):
1022
- # type: (str, **Any) -> BlobQueryReader
745
+ def query_blob(self, query_expression: str, **kwargs: Any) -> BlobQueryReader:
1023
746
  """Enables users to select/project on blob/or blob snapshot data by providing simple query expressions.
1024
747
  This operations returns a BlobQueryReader, users need to use readall() or readinto() to get query data.
1025
748
 
@@ -1046,7 +769,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1046
769
  This value can be a DelimitedTextDialect or a DelimitedJsonDialect or ArrowDialect.
1047
770
  These dialects can be passed through their respective classes, the QuickQueryDialect enum or as a string
1048
771
  :paramtype output_format: ~azure.storage.blob.DelimitedTextDialect or ~azure.storage.blob.DelimitedJsonDialect
1049
- 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
1050
773
  :keyword lease:
1051
774
  Required if the blob has an active lease. Value can be a BlobLeaseClient object
1052
775
  or the lease ID as a string.
@@ -1084,7 +807,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1084
807
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
1085
808
  This value is not tracked or validated on the client. To configure client-side network timesouts
1086
809
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
1087
- #other-client--per-operation-configuration>`_.
810
+ #other-client--per-operation-configuration>`__.
1088
811
  :returns: A streaming object (BlobQueryReader)
1089
812
  :rtype: ~azure.storage.blob.BlobQueryReader
1090
813
 
@@ -1100,7 +823,9 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1100
823
  errors = kwargs.pop("on_error", None)
1101
824
  error_cls = kwargs.pop("error_cls", BlobQueryError)
1102
825
  encoding = kwargs.pop("encoding", None)
1103
- 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)
1104
829
  try:
1105
830
  headers, raw_response_body = self._client.blob.query(**options)
1106
831
  except HttpResponseError as error:
@@ -1115,36 +840,8 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1115
840
  response=raw_response_body,
1116
841
  error_cls=error_cls)
1117
842
 
1118
- @staticmethod
1119
- def _generic_delete_blob_options(delete_snapshots=None, **kwargs):
1120
- # type: (str, **Any) -> Dict[str, Any]
1121
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
1122
- mod_conditions = get_modify_conditions(kwargs)
1123
- if delete_snapshots:
1124
- delete_snapshots = DeleteSnapshotsOptionType(delete_snapshots)
1125
- options = {
1126
- 'timeout': kwargs.pop('timeout', None),
1127
- 'snapshot': kwargs.pop('snapshot', None), # this is added for delete_blobs
1128
- 'delete_snapshots': delete_snapshots or None,
1129
- 'lease_access_conditions': access_conditions,
1130
- 'modified_access_conditions': mod_conditions}
1131
- options.update(kwargs)
1132
- return options
1133
-
1134
- def _delete_blob_options(self, delete_snapshots=None, **kwargs):
1135
- # type: (str, **Any) -> Dict[str, Any]
1136
- if self.snapshot and delete_snapshots:
1137
- raise ValueError("The delete_snapshots option cannot be used with a specific snapshot.")
1138
- version_id = get_version_id(self.version_id, kwargs)
1139
- options = self._generic_delete_blob_options(delete_snapshots, **kwargs)
1140
- options['snapshot'] = self.snapshot
1141
- options['version_id'] = version_id
1142
- options['blob_delete_type'] = kwargs.pop('blob_delete_type', None)
1143
- return options
1144
-
1145
843
  @distributed_trace
1146
- def delete_blob(self, delete_snapshots=None, **kwargs):
1147
- # type: (str, **Any) -> None
844
+ def delete_blob(self, delete_snapshots: Optional[str] = None, **kwargs: Any) -> None:
1148
845
  """Marks the specified blob for deletion.
1149
846
 
1150
847
  The blob is later deleted during garbage collection.
@@ -1158,15 +855,16 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1158
855
  Soft deleted blob is accessible through :func:`~ContainerClient.list_blobs()` specifying `include=['deleted']`
1159
856
  option. Soft-deleted blob can be restored using :func:`undelete` operation.
1160
857
 
1161
- :param str delete_snapshots:
858
+ :param Optional[str] delete_snapshots:
1162
859
  Required if the blob has associated snapshots. Values include:
1163
860
  - "only": Deletes only the blobs snapshots.
1164
861
  - "include": Deletes the blob along with all snapshots.
1165
- :keyword str version_id:
862
+ :keyword Optional[str] version_id:
1166
863
  The version id parameter is an opaque DateTime
1167
864
  value that, when present, specifies the version of the blob to delete.
1168
865
 
1169
866
  .. versionadded:: 12.4.0
867
+
1170
868
  This keyword argument was introduced in API version '2019-12-12'.
1171
869
 
1172
870
  :keyword lease:
@@ -1202,7 +900,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1202
900
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
1203
901
  This value is not tracked or validated on the client. To configure client-side network timesouts
1204
902
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
1205
- #other-client--per-operation-configuration>`_.
903
+ #other-client--per-operation-configuration>`__.
1206
904
  :rtype: None
1207
905
 
1208
906
  .. admonition:: Example:
@@ -1214,15 +912,18 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1214
912
  :dedent: 12
1215
913
  :caption: Delete a blob.
1216
914
  """
1217
- 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)
1218
920
  try:
1219
921
  self._client.blob.delete(**options)
1220
922
  except HttpResponseError as error:
1221
923
  process_storage_error(error)
1222
924
 
1223
925
  @distributed_trace
1224
- def undelete_blob(self, **kwargs):
1225
- # type: (**Any) -> None
926
+ def undelete_blob(self, **kwargs: Any) -> None:
1226
927
  """Restores soft-deleted blobs or snapshots.
1227
928
 
1228
929
  Operation will only be successful if used within the specified number of days
@@ -1237,7 +938,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1237
938
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
1238
939
  This value is not tracked or validated on the client. To configure client-side network timesouts
1239
940
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
1240
- #other-client--per-operation-configuration>`_.
941
+ #other-client--per-operation-configuration>`__.
1241
942
  :rtype: None
1242
943
 
1243
944
  .. admonition:: Example:
@@ -1255,8 +956,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1255
956
  process_storage_error(error)
1256
957
 
1257
958
  @distributed_trace
1258
- def exists(self, **kwargs):
1259
- # type: (**Any) -> bool
959
+ def exists(self, **kwargs: Any) -> bool:
1260
960
  """
1261
961
  Returns True if a blob exists with the defined parameters, and returns
1262
962
  False otherwise.
@@ -1269,7 +969,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1269
969
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
1270
970
  This value is not tracked or validated on the client. To configure client-side network timesouts
1271
971
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
1272
- #other-client--per-operation-configuration>`_.
972
+ #other-client--per-operation-configuration>`__.
1273
973
  :returns: boolean
1274
974
  :rtype: bool
1275
975
  """
@@ -1290,8 +990,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1290
990
  return False
1291
991
 
1292
992
  @distributed_trace
1293
- def get_blob_properties(self, **kwargs):
1294
- # type: (**Any) -> BlobProperties
993
+ def get_blob_properties(self, **kwargs: Any) -> BlobProperties:
1295
994
  """Returns all user-defined metadata, standard HTTP properties, and
1296
995
  system properties for the blob. It does not return the content of the blob.
1297
996
 
@@ -1304,6 +1003,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1304
1003
  value that, when present, specifies the version of the blob to get properties.
1305
1004
 
1306
1005
  .. versionadded:: 12.4.0
1006
+
1307
1007
  This keyword argument was introduced in API version '2019-12-12'.
1308
1008
 
1309
1009
  :keyword ~datetime.datetime if_modified_since:
@@ -1339,7 +1039,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1339
1039
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
1340
1040
  This value is not tracked or validated on the client. To configure client-side network timesouts
1341
1041
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
1342
- #other-client--per-operation-configuration>`_.
1042
+ #other-client--per-operation-configuration>`__.
1343
1043
  :returns: BlobProperties
1344
1044
  :rtype: ~azure.storage.blob.BlobProperties
1345
1045
 
@@ -1367,7 +1067,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1367
1067
  cls_method = kwargs.pop('cls', None)
1368
1068
  if cls_method:
1369
1069
  kwargs['cls'] = partial(deserialize_pipeline_response_into_cls, cls_method)
1370
- blob_props = self._client.blob.get_properties(
1070
+ blob_props = cast(BlobProperties, self._client.blob.get_properties(
1371
1071
  timeout=kwargs.pop('timeout', None),
1372
1072
  version_id=version_id,
1373
1073
  snapshot=self.snapshot,
@@ -1375,41 +1075,17 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1375
1075
  modified_access_conditions=mod_conditions,
1376
1076
  cls=kwargs.pop('cls', None) or deserialize_blob_properties,
1377
1077
  cpk_info=cpk_info,
1378
- **kwargs)
1078
+ **kwargs))
1379
1079
  except HttpResponseError as error:
1380
1080
  process_storage_error(error)
1381
1081
  blob_props.name = self.blob_name
1382
1082
  if isinstance(blob_props, BlobProperties):
1383
1083
  blob_props.container = self.container_name
1384
1084
  blob_props.snapshot = self.snapshot
1385
- return blob_props # type: ignore
1386
-
1387
- def _set_http_headers_options(self, content_settings=None, **kwargs):
1388
- # type: (Optional[ContentSettings], **Any) -> Dict[str, Any]
1389
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
1390
- mod_conditions = get_modify_conditions(kwargs)
1391
- blob_headers = None
1392
- if content_settings:
1393
- blob_headers = BlobHTTPHeaders(
1394
- blob_cache_control=content_settings.cache_control,
1395
- blob_content_type=content_settings.content_type,
1396
- blob_content_md5=content_settings.content_md5,
1397
- blob_content_encoding=content_settings.content_encoding,
1398
- blob_content_language=content_settings.content_language,
1399
- blob_content_disposition=content_settings.content_disposition
1400
- )
1401
- options = {
1402
- 'timeout': kwargs.pop('timeout', None),
1403
- 'blob_http_headers': blob_headers,
1404
- 'lease_access_conditions': access_conditions,
1405
- 'modified_access_conditions': mod_conditions,
1406
- 'cls': return_response_headers}
1407
- options.update(kwargs)
1408
- return options
1085
+ return blob_props
1409
1086
 
1410
1087
  @distributed_trace
1411
- def set_http_headers(self, content_settings=None, **kwargs):
1412
- # type: (Optional[ContentSettings], **Any) -> None
1088
+ def set_http_headers(self, content_settings: Optional["ContentSettings"] = None, **kwargs: Any) -> Dict[str, Any]:
1413
1089
  """Sets system properties on the blob.
1414
1090
 
1415
1091
  If one property is set for the content_settings, all properties will be overridden.
@@ -1449,45 +1125,21 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1449
1125
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
1450
1126
  This value is not tracked or validated on the client. To configure client-side network timesouts
1451
1127
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
1452
- #other-client--per-operation-configuration>`_.
1128
+ #other-client--per-operation-configuration>`__.
1453
1129
  :returns: Blob-updated property dict (Etag and last modified)
1454
1130
  :rtype: Dict[str, Any]
1455
1131
  """
1456
- options = self._set_http_headers_options(content_settings=content_settings, **kwargs)
1132
+ options = _set_http_headers_options(content_settings=content_settings, **kwargs)
1457
1133
  try:
1458
- return self._client.blob.set_http_headers(**options) # type: ignore
1134
+ return cast(Dict[str, Any], self._client.blob.set_http_headers(**options))
1459
1135
  except HttpResponseError as error:
1460
1136
  process_storage_error(error)
1461
1137
 
1462
- def _set_blob_metadata_options(self, metadata=None, **kwargs):
1463
- # type: (Optional[Dict[str, str]], **Any) -> Dict[str, Any]
1464
- headers = kwargs.pop('headers', {})
1465
- headers.update(add_metadata_headers(metadata))
1466
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
1467
- mod_conditions = get_modify_conditions(kwargs)
1468
- cpk_scope_info = get_cpk_scope_info(kwargs)
1469
-
1470
- cpk = kwargs.pop('cpk', None)
1471
- cpk_info = None
1472
- if cpk:
1473
- if self.scheme.lower() != 'https':
1474
- raise ValueError("Customer provided encryption key must be used over HTTPS.")
1475
- cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
1476
- encryption_algorithm=cpk.algorithm)
1477
- options = {
1478
- 'timeout': kwargs.pop('timeout', None),
1479
- 'lease_access_conditions': access_conditions,
1480
- 'modified_access_conditions': mod_conditions,
1481
- 'cpk_scope_info': cpk_scope_info,
1482
- 'cpk_info': cpk_info,
1483
- 'cls': return_response_headers,
1484
- 'headers': headers}
1485
- options.update(kwargs)
1486
- return options
1487
-
1488
1138
  @distributed_trace
1489
- def set_blob_metadata(self, metadata=None, **kwargs):
1490
- # 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]]:
1491
1143
  """Sets user-defined metadata for the blob as one or more name-value pairs.
1492
1144
 
1493
1145
  :param metadata:
@@ -1540,19 +1192,23 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1540
1192
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
1541
1193
  This value is not tracked or validated on the client. To configure client-side network timesouts
1542
1194
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
1543
- #other-client--per-operation-configuration>`_.
1195
+ #other-client--per-operation-configuration>`__.
1544
1196
  :returns: Blob-updated property dict (Etag and last modified)
1545
1197
  :rtype: Dict[str, Union[str, datetime]]
1546
1198
  """
1547
- 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)
1548
1202
  try:
1549
- return self._client.blob.set_metadata(**options) # type: ignore
1203
+ return cast(Dict[str, Union[str, datetime]], self._client.blob.set_metadata(**options))
1550
1204
  except HttpResponseError as error:
1551
1205
  process_storage_error(error)
1552
1206
 
1553
1207
  @distributed_trace
1554
- def set_immutability_policy(self, immutability_policy, **kwargs):
1555
- # type: (ImmutabilityPolicy, **Any) -> Dict[str, str]
1208
+ def set_immutability_policy(
1209
+ self, immutability_policy: "ImmutabilityPolicy",
1210
+ **kwargs: Any
1211
+ ) -> Dict[str, str]:
1556
1212
  """The Set Immutability Policy operation sets the immutability policy on the blob.
1557
1213
 
1558
1214
  .. versionadded:: 12.10.0
@@ -1569,18 +1225,17 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1569
1225
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
1570
1226
  This value is not tracked or validated on the client. To configure client-side network timesouts
1571
1227
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
1572
- #other-client--per-operation-configuration>`_.
1228
+ #other-client--per-operation-configuration>`__.
1573
1229
  :returns: Key value pairs of blob tags.
1574
1230
  :rtype: Dict[str, str]
1575
1231
  """
1576
1232
 
1577
1233
  kwargs['immutability_policy_expiry'] = immutability_policy.expiry_time
1578
1234
  kwargs['immutability_policy_mode'] = immutability_policy.policy_mode
1579
- 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))
1580
1236
 
1581
1237
  @distributed_trace
1582
- def delete_immutability_policy(self, **kwargs):
1583
- # type: (**Any) -> None
1238
+ def delete_immutability_policy(self, **kwargs: Any) -> None:
1584
1239
  """The Delete Immutability Policy operation deletes the immutability policy on the blob.
1585
1240
 
1586
1241
  .. versionadded:: 12.10.0
@@ -1591,7 +1246,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1591
1246
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
1592
1247
  This value is not tracked or validated on the client. To configure client-side network timesouts
1593
1248
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
1594
- #other-client--per-operation-configuration>`_.
1249
+ #other-client--per-operation-configuration>`__.
1595
1250
  :returns: Key value pairs of blob tags.
1596
1251
  :rtype: Dict[str, str]
1597
1252
  """
@@ -1599,8 +1254,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1599
1254
  self._client.blob.delete_immutability_policy(**kwargs)
1600
1255
 
1601
1256
  @distributed_trace
1602
- def set_legal_hold(self, legal_hold, **kwargs):
1603
- # 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]]:
1604
1258
  """The Set Legal Hold operation sets a legal hold on the blob.
1605
1259
 
1606
1260
  .. versionadded:: 12.10.0
@@ -1613,88 +1267,22 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1613
1267
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
1614
1268
  This value is not tracked or validated on the client. To configure client-side network timesouts
1615
1269
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
1616
- #other-client--per-operation-configuration>`_.
1270
+ #other-client--per-operation-configuration>`__.
1617
1271
  :returns: Key value pairs of blob tags.
1618
1272
  :rtype: Dict[str, Union[str, datetime, bool]]
1619
1273
  """
1620
1274
 
1621
- return self._client.blob.set_legal_hold(legal_hold, cls=return_response_headers, **kwargs)
1622
-
1623
- def _create_page_blob_options( # type: ignore
1624
- self, size, # type: int
1625
- content_settings=None, # type: Optional[ContentSettings]
1626
- metadata=None, # type: Optional[Dict[str, str]]
1627
- premium_page_blob_tier=None, # type: Optional[Union[str, PremiumPageBlobTier]]
1628
- **kwargs
1629
- ):
1630
- # type: (...) -> Dict[str, Any]
1631
- if self.require_encryption or (self.key_encryption_key is not None):
1632
- raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
1633
- headers = kwargs.pop('headers', {})
1634
- headers.update(add_metadata_headers(metadata))
1635
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
1636
- mod_conditions = get_modify_conditions(kwargs)
1637
- cpk_scope_info = get_cpk_scope_info(kwargs)
1638
- blob_headers = None
1639
- if content_settings:
1640
- blob_headers = BlobHTTPHeaders(
1641
- blob_cache_control=content_settings.cache_control,
1642
- blob_content_type=content_settings.content_type,
1643
- blob_content_md5=content_settings.content_md5,
1644
- blob_content_encoding=content_settings.content_encoding,
1645
- blob_content_language=content_settings.content_language,
1646
- blob_content_disposition=content_settings.content_disposition
1647
- )
1648
-
1649
- sequence_number = kwargs.pop('sequence_number', None)
1650
- cpk = kwargs.pop('cpk', None)
1651
- cpk_info = None
1652
- if cpk:
1653
- if self.scheme.lower() != 'https':
1654
- raise ValueError("Customer provided encryption key must be used over HTTPS.")
1655
- cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
1656
- encryption_algorithm=cpk.algorithm)
1657
-
1658
- immutability_policy = kwargs.pop('immutability_policy', None)
1659
- if immutability_policy:
1660
- kwargs['immutability_policy_expiry'] = immutability_policy.expiry_time
1661
- kwargs['immutability_policy_mode'] = immutability_policy.policy_mode
1662
-
1663
- tier = None
1664
- if premium_page_blob_tier:
1665
- try:
1666
- tier = premium_page_blob_tier.value # type: ignore
1667
- except AttributeError:
1668
- tier = premium_page_blob_tier # type: ignore
1669
-
1670
- blob_tags_string = serialize_blob_tags_header(kwargs.pop('tags', None))
1671
-
1672
- options = {
1673
- 'content_length': 0,
1674
- 'blob_content_length': size,
1675
- 'blob_sequence_number': sequence_number,
1676
- 'blob_http_headers': blob_headers,
1677
- 'timeout': kwargs.pop('timeout', None),
1678
- 'lease_access_conditions': access_conditions,
1679
- 'modified_access_conditions': mod_conditions,
1680
- 'cpk_scope_info': cpk_scope_info,
1681
- 'cpk_info': cpk_info,
1682
- 'blob_tags_string': blob_tags_string,
1683
- 'cls': return_response_headers,
1684
- "tier": tier,
1685
- 'headers': headers}
1686
- options.update(kwargs)
1687
- 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))
1688
1277
 
1689
1278
  @distributed_trace
1690
- def create_page_blob( # type: ignore
1691
- self, size, # type: int
1692
- content_settings=None, # type: Optional[ContentSettings]
1693
- metadata=None, # type: Optional[Dict[str, str]]
1694
- premium_page_blob_tier=None, # type: Optional[Union[str, PremiumPageBlobTier]]
1695
- **kwargs
1696
- ):
1697
- # 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]]:
1698
1286
  """Creates a new Page Blob of the specified size.
1699
1287
 
1700
1288
  :param int size:
@@ -1715,7 +1303,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1715
1303
  The tag set may contain at most 10 tags. Tag keys must be between 1 and 128 characters,
1716
1304
  and tag values must be between 0 and 256 characters.
1717
1305
  Valid tag key and value characters include: lowercase and uppercase letters, digits (0-9),
1718
- space (` `), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)
1306
+ space (' '), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)
1719
1307
 
1720
1308
  .. versionadded:: 12.4.0
1721
1309
 
@@ -1775,73 +1363,31 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1775
1363
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
1776
1364
  This value is not tracked or validated on the client. To configure client-side network timesouts
1777
1365
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
1778
- #other-client--per-operation-configuration>`_.
1366
+ #other-client--per-operation-configuration>`__.
1779
1367
  :returns: Blob-updated property dict (Etag and last modified).
1780
1368
  :rtype: dict[str, Any]
1781
1369
  """
1782
- options = self._create_page_blob_options(
1783
- 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,
1784
1376
  content_settings=content_settings,
1785
1377
  metadata=metadata,
1786
1378
  premium_page_blob_tier=premium_page_blob_tier,
1787
1379
  **kwargs)
1788
1380
  try:
1789
- return self._client.page_blob.create(**options) # type: ignore
1381
+ return cast(Dict[str, Any], self._client.page_blob.create(**options))
1790
1382
  except HttpResponseError as error:
1791
1383
  process_storage_error(error)
1792
1384
 
1793
- def _create_append_blob_options(self, content_settings=None, metadata=None, **kwargs):
1794
- # type: (Optional[ContentSettings], Optional[Dict[str, str]], **Any) -> Dict[str, Any]
1795
- if self.require_encryption or (self.key_encryption_key is not None):
1796
- raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
1797
- headers = kwargs.pop('headers', {})
1798
- headers.update(add_metadata_headers(metadata))
1799
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
1800
- mod_conditions = get_modify_conditions(kwargs)
1801
- cpk_scope_info = get_cpk_scope_info(kwargs)
1802
- blob_headers = None
1803
- if content_settings:
1804
- blob_headers = BlobHTTPHeaders(
1805
- blob_cache_control=content_settings.cache_control,
1806
- blob_content_type=content_settings.content_type,
1807
- blob_content_md5=content_settings.content_md5,
1808
- blob_content_encoding=content_settings.content_encoding,
1809
- blob_content_language=content_settings.content_language,
1810
- blob_content_disposition=content_settings.content_disposition
1811
- )
1812
-
1813
- cpk = kwargs.pop('cpk', None)
1814
- cpk_info = None
1815
- if cpk:
1816
- if self.scheme.lower() != 'https':
1817
- raise ValueError("Customer provided encryption key must be used over HTTPS.")
1818
- cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
1819
- encryption_algorithm=cpk.algorithm)
1820
-
1821
- immutability_policy = kwargs.pop('immutability_policy', None)
1822
- if immutability_policy:
1823
- kwargs['immutability_policy_expiry'] = immutability_policy.expiry_time
1824
- kwargs['immutability_policy_mode'] = immutability_policy.policy_mode
1825
-
1826
- blob_tags_string = serialize_blob_tags_header(kwargs.pop('tags', None))
1827
-
1828
- options = {
1829
- 'content_length': 0,
1830
- 'blob_http_headers': blob_headers,
1831
- 'timeout': kwargs.pop('timeout', None),
1832
- 'lease_access_conditions': access_conditions,
1833
- 'modified_access_conditions': mod_conditions,
1834
- 'cpk_scope_info': cpk_scope_info,
1835
- 'cpk_info': cpk_info,
1836
- 'blob_tags_string': blob_tags_string,
1837
- 'cls': return_response_headers,
1838
- 'headers': headers}
1839
- options.update(kwargs)
1840
- return options
1841
-
1842
1385
  @distributed_trace
1843
- def create_append_blob(self, content_settings=None, metadata=None, **kwargs):
1844
- # 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]]:
1845
1391
  """Creates a new Append Blob. This operation creates a new 0-length append blob. The content
1846
1392
  of any existing blob is overwritten with the newly initialized append blob. To add content to
1847
1393
  the append blob, call the :func:`append_block` or :func:`append_block_from_url` method.
@@ -1857,7 +1403,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1857
1403
  The tag set may contain at most 10 tags. Tag keys must be between 1 and 128 characters,
1858
1404
  and tag values must be between 0 and 256 characters.
1859
1405
  Valid tag key and value characters include: lowercase and uppercase letters, digits (0-9),
1860
- space (` `), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)
1406
+ space (' '), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)
1861
1407
 
1862
1408
  .. versionadded:: 12.4.0
1863
1409
 
@@ -1913,48 +1459,28 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
1913
1459
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
1914
1460
  This value is not tracked or validated on the client. To configure client-side network timesouts
1915
1461
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
1916
- #other-client--per-operation-configuration>`_.
1462
+ #other-client--per-operation-configuration>`__.
1917
1463
  :returns: Blob-updated property dict (Etag and last modified).
1918
1464
  :rtype: dict[str, Any]
1919
1465
  """
1920
- 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(
1921
1471
  content_settings=content_settings,
1922
1472
  metadata=metadata,
1923
1473
  **kwargs)
1924
1474
  try:
1925
- return self._client.append_blob.create(**options) # type: ignore
1475
+ return cast(Dict[str, Union[str, datetime]], self._client.append_blob.create(**options))
1926
1476
  except HttpResponseError as error:
1927
1477
  process_storage_error(error)
1928
1478
 
1929
- def _create_snapshot_options(self, metadata=None, **kwargs):
1930
- # type: (Optional[Dict[str, str]], **Any) -> Dict[str, Any]
1931
- headers = kwargs.pop('headers', {})
1932
- headers.update(add_metadata_headers(metadata))
1933
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
1934
- mod_conditions = get_modify_conditions(kwargs)
1935
- cpk_scope_info = get_cpk_scope_info(kwargs)
1936
- cpk = kwargs.pop('cpk', None)
1937
- cpk_info = None
1938
- if cpk:
1939
- if self.scheme.lower() != 'https':
1940
- raise ValueError("Customer provided encryption key must be used over HTTPS.")
1941
- cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
1942
- encryption_algorithm=cpk.algorithm)
1943
-
1944
- options = {
1945
- 'timeout': kwargs.pop('timeout', None),
1946
- 'lease_access_conditions': access_conditions,
1947
- 'modified_access_conditions': mod_conditions,
1948
- 'cpk_scope_info': cpk_scope_info,
1949
- 'cpk_info': cpk_info,
1950
- 'cls': return_response_headers,
1951
- 'headers': headers}
1952
- options.update(kwargs)
1953
- return options
1954
-
1955
1479
  @distributed_trace
1956
- def create_snapshot(self, metadata=None, **kwargs):
1957
- # 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]]:
1958
1484
  """Creates a snapshot of the blob.
1959
1485
 
1960
1486
  A snapshot is a read-only version of a blob that's taken at a point in time.
@@ -2012,7 +1538,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2012
1538
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
2013
1539
  This value is not tracked or validated on the client. To configure client-side network timesouts
2014
1540
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
2015
- #other-client--per-operation-configuration>`_.
1541
+ #other-client--per-operation-configuration>`__.
2016
1542
  :returns: Blob-updated property dict (Snapshot ID, Etag, and last modified).
2017
1543
  :rtype: dict[str, Any]
2018
1544
 
@@ -2025,92 +1551,21 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2025
1551
  :dedent: 8
2026
1552
  :caption: Create a snapshot of the blob.
2027
1553
  """
2028
- 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)
2029
1557
  try:
2030
- return self._client.blob.create_snapshot(**options) # type: ignore
1558
+ return cast(Dict[str, Any], self._client.blob.create_snapshot(**options))
2031
1559
  except HttpResponseError as error:
2032
1560
  process_storage_error(error)
2033
1561
 
2034
- def _start_copy_from_url_options(self, source_url, metadata=None, incremental_copy=False, **kwargs):
2035
- # type: (str, Optional[Dict[str, str]], bool, **Any) -> Dict[str, Any]
2036
- headers = kwargs.pop('headers', {})
2037
- headers.update(add_metadata_headers(metadata))
2038
- if 'source_lease' in kwargs:
2039
- source_lease = kwargs.pop('source_lease')
2040
- try:
2041
- headers['x-ms-source-lease-id'] = source_lease.id
2042
- except AttributeError:
2043
- headers['x-ms-source-lease-id'] = source_lease
2044
-
2045
- tier = kwargs.pop('premium_page_blob_tier', None) or kwargs.pop('standard_blob_tier', None)
2046
- tags = kwargs.pop('tags', None)
2047
-
2048
- # Options only available for sync copy
2049
- requires_sync = kwargs.pop('requires_sync', None)
2050
- encryption_scope_str = kwargs.pop('encryption_scope', None)
2051
- source_authorization = kwargs.pop('source_authorization', None)
2052
- # If tags is a str, interpret that as copy_source_tags
2053
- copy_source_tags = isinstance(tags, str)
2054
-
2055
- if incremental_copy:
2056
- if source_authorization:
2057
- raise ValueError("Source authorization tokens are not applicable for incremental copying.")
2058
- if copy_source_tags:
2059
- raise ValueError("Copying source tags is not applicable for incremental copying.")
2060
-
2061
- # TODO: refactor start_copy_from_url api in _blob_client.py. Call _generated/_blob_operations.py copy_from_url
2062
- # when requires_sync=True is set.
2063
- # Currently both sync copy and async copy are calling _generated/_blob_operations.py start_copy_from_url.
2064
- # As sync copy diverges more from async copy, more problem will surface.
2065
- if requires_sync is True:
2066
- headers['x-ms-requires-sync'] = str(requires_sync)
2067
- if encryption_scope_str:
2068
- headers['x-ms-encryption-scope'] = encryption_scope_str
2069
- if source_authorization:
2070
- headers['x-ms-copy-source-authorization'] = source_authorization
2071
- if copy_source_tags:
2072
- headers['x-ms-copy-source-tag-option'] = tags
2073
- else:
2074
- if encryption_scope_str:
2075
- raise ValueError(
2076
- "Encryption_scope is only supported for sync copy, please specify requires_sync=True")
2077
- if source_authorization:
2078
- raise ValueError(
2079
- "Source authorization tokens are only supported for sync copy, please specify requires_sync=True")
2080
- if copy_source_tags:
2081
- raise ValueError(
2082
- "Copying source tags is only supported for sync copy, please specify requires_sync=True")
2083
-
2084
- timeout = kwargs.pop('timeout', None)
2085
- dest_mod_conditions = get_modify_conditions(kwargs)
2086
- blob_tags_string = serialize_blob_tags_header(tags) if not copy_source_tags else None
2087
-
2088
- immutability_policy = kwargs.pop('immutability_policy', None)
2089
- if immutability_policy:
2090
- kwargs['immutability_policy_expiry'] = immutability_policy.expiry_time
2091
- kwargs['immutability_policy_mode'] = immutability_policy.policy_mode
2092
-
2093
- options = {
2094
- 'copy_source': source_url,
2095
- 'seal_blob': kwargs.pop('seal_destination_blob', None),
2096
- 'timeout': timeout,
2097
- 'modified_access_conditions': dest_mod_conditions,
2098
- 'blob_tags_string': blob_tags_string,
2099
- 'headers': headers,
2100
- 'cls': return_response_headers,
2101
- }
2102
- if not incremental_copy:
2103
- source_mod_conditions = get_source_conditions(kwargs)
2104
- dest_access_conditions = get_access_conditions(kwargs.pop('destination_lease', None))
2105
- options['source_modified_access_conditions'] = source_mod_conditions
2106
- options['lease_access_conditions'] = dest_access_conditions
2107
- options['tier'] = tier.value if tier else None
2108
- options.update(kwargs)
2109
- return options
2110
-
2111
1562
  @distributed_trace
2112
- def start_copy_from_url(self, source_url, metadata=None, incremental_copy=False, **kwargs):
2113
- # 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]]:
2114
1569
  """Copies a blob from the given URL.
2115
1570
 
2116
1571
  This operation returns a dictionary containing `copy_status` and `copy_id`,
@@ -2171,7 +1626,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2171
1626
  The tag set may contain at most 10 tags. Tag keys must be between 1 and 128 characters,
2172
1627
  and tag values must be between 0 and 256 characters.
2173
1628
  Valid tag key and value characters include: lowercase and uppercase letters, digits (0-9),
2174
- space (` `), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_).
1629
+ space (' '), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_).
2175
1630
 
2176
1631
  The (case-sensitive) literal "COPY" can instead be passed to copy tags from the source blob.
2177
1632
  This option is only available when `incremental_copy=False` and `requires_sync=True`.
@@ -2243,7 +1698,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2243
1698
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
2244
1699
  This value is not tracked or validated on the client. To configure client-side network timesouts
2245
1700
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
2246
- #other-client--per-operation-configuration>`_.
1701
+ #other-client--per-operation-configuration>`__.
2247
1702
  :keyword ~azure.storage.blob.PremiumPageBlobTier premium_page_blob_tier:
2248
1703
  A page blob tier value to set the blob to. The tier correlates to the size of the
2249
1704
  blob and number of allowed IOPS. This is only applicable to page blobs on
@@ -2287,38 +1742,23 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2287
1742
  :dedent: 12
2288
1743
  :caption: Copy a blob from a URL.
2289
1744
  """
2290
- options = self._start_copy_from_url_options(
2291
- source_url=self._encode_source_url(source_url),
1745
+ options = _start_copy_from_url_options(
1746
+ source_url=source_url,
2292
1747
  metadata=metadata,
2293
1748
  incremental_copy=incremental_copy,
2294
1749
  **kwargs)
2295
1750
  try:
2296
1751
  if incremental_copy:
2297
- return self._client.page_blob.copy_incremental(**options)
2298
- 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))
2299
1754
  except HttpResponseError as error:
2300
1755
  process_storage_error(error)
2301
1756
 
2302
- def _abort_copy_options(self, copy_id, **kwargs):
2303
- # type: (Union[str, Dict[str, Any], BlobProperties], **Any) -> Dict[str, Any]
2304
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
2305
- try:
2306
- copy_id = copy_id.copy.id
2307
- except AttributeError:
2308
- try:
2309
- copy_id = copy_id['copy_id']
2310
- except TypeError:
2311
- pass
2312
- options = {
2313
- 'copy_id': copy_id,
2314
- 'lease_access_conditions': access_conditions,
2315
- 'timeout': kwargs.pop('timeout', None)}
2316
- options.update(kwargs)
2317
- return options
2318
-
2319
1757
  @distributed_trace
2320
- def abort_copy(self, copy_id, **kwargs):
2321
- # 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:
2322
1762
  """Abort an ongoing copy operation.
2323
1763
 
2324
1764
  This will leave a destination blob with zero length and full metadata.
@@ -2339,15 +1779,14 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2339
1779
  :dedent: 12
2340
1780
  :caption: Abort copying a blob from URL.
2341
1781
  """
2342
- options = self._abort_copy_options(copy_id, **kwargs)
1782
+ options = _abort_copy_options(copy_id, **kwargs)
2343
1783
  try:
2344
1784
  self._client.blob.abort_copy_from_url(**options)
2345
1785
  except HttpResponseError as error:
2346
1786
  process_storage_error(error)
2347
1787
 
2348
1788
  @distributed_trace
2349
- def acquire_lease(self, lease_duration=-1, lease_id=None, **kwargs):
2350
- # type: (int, Optional[str], **Any) -> BlobLeaseClient
1789
+ def acquire_lease(self, lease_duration: int =-1, lease_id: Optional[str] = None, **kwargs: Any) -> BlobLeaseClient:
2351
1790
  """Requests a new lease.
2352
1791
 
2353
1792
  If the blob does not have an active lease, the Blob
@@ -2390,7 +1829,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2390
1829
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
2391
1830
  This value is not tracked or validated on the client. To configure client-side network timesouts
2392
1831
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
2393
- #other-client--per-operation-configuration>`_.
1832
+ #other-client--per-operation-configuration>`__.
2394
1833
  :returns: A BlobLeaseClient object.
2395
1834
  :rtype: ~azure.storage.blob.BlobLeaseClient
2396
1835
 
@@ -2403,13 +1842,12 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2403
1842
  :dedent: 8
2404
1843
  :caption: Acquiring a lease on a blob.
2405
1844
  """
2406
- lease = BlobLeaseClient(self, lease_id=lease_id) # type: ignore
1845
+ lease = BlobLeaseClient(self, lease_id=lease_id)
2407
1846
  lease.acquire(lease_duration=lease_duration, **kwargs)
2408
1847
  return lease
2409
1848
 
2410
1849
  @distributed_trace
2411
- def set_standard_blob_tier(self, standard_blob_tier, **kwargs):
2412
- # type: (Union[str, StandardBlobTier], Any) -> None
1850
+ def set_standard_blob_tier(self, standard_blob_tier: Union[str, "StandardBlobTier"], **kwargs: Any) -> None:
2413
1851
  """This operation sets the tier on a block blob.
2414
1852
 
2415
1853
  A block blob's tier determines Hot/Cool/Archive storage type.
@@ -2430,18 +1868,20 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2430
1868
  value that, when present, specifies the version of the blob to download.
2431
1869
 
2432
1870
  .. versionadded:: 12.4.0
1871
+
2433
1872
  This keyword argument was introduced in API version '2019-12-12'.
2434
1873
  :keyword str if_tags_match_condition:
2435
1874
  Specify a SQL where clause on blob tags to operate only on blob with a matching value.
2436
1875
  eg. ``\"\\\"tagname\\\"='my tag'\"``
2437
1876
 
2438
1877
  .. versionadded:: 12.4.0
1878
+
2439
1879
  :keyword int timeout:
2440
1880
  Sets the server-side timeout for the operation in seconds. For more details see
2441
1881
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
2442
1882
  This value is not tracked or validated on the client. To configure client-side network timesouts
2443
1883
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
2444
- #other-client--per-operation-configuration>`_.
1884
+ #other-client--per-operation-configuration>`__.
2445
1885
  :keyword lease:
2446
1886
  Required if the blob has an active lease. Value can be a BlobLeaseClient object
2447
1887
  or the lease ID as a string.
@@ -2467,66 +1907,20 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2467
1907
  except HttpResponseError as error:
2468
1908
  process_storage_error(error)
2469
1909
 
2470
- def _stage_block_options(
2471
- self, block_id, # type: str
2472
- data, # type: Union[Iterable[AnyStr], IO[AnyStr]]
2473
- length=None, # type: Optional[int]
2474
- **kwargs
2475
- ):
2476
- # type: (...) -> Dict[str, Any]
2477
- if self.require_encryption or (self.key_encryption_key is not None):
2478
- raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
2479
- block_id = encode_base64(str(block_id))
2480
- if isinstance(data, str):
2481
- data = data.encode(kwargs.pop('encoding', 'UTF-8')) # type: ignore
2482
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
2483
- if length is None:
2484
- length = get_length(data)
2485
- if length is None:
2486
- length, data = read_length(data)
2487
- if isinstance(data, bytes):
2488
- data = data[:length]
2489
-
2490
- validate_content = kwargs.pop('validate_content', False)
2491
- cpk_scope_info = get_cpk_scope_info(kwargs)
2492
- cpk = kwargs.pop('cpk', None)
2493
- cpk_info = None
2494
- if cpk:
2495
- if self.scheme.lower() != 'https':
2496
- raise ValueError("Customer provided encryption key must be used over HTTPS.")
2497
- cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
2498
- encryption_algorithm=cpk.algorithm)
2499
-
2500
- options = {
2501
- 'block_id': block_id,
2502
- 'content_length': length,
2503
- 'body': data,
2504
- 'transactional_content_md5': None,
2505
- 'timeout': kwargs.pop('timeout', None),
2506
- 'lease_access_conditions': access_conditions,
2507
- 'validate_content': validate_content,
2508
- 'cpk_scope_info': cpk_scope_info,
2509
- 'cpk_info': cpk_info,
2510
- 'cls': return_response_headers,
2511
- }
2512
- options.update(kwargs)
2513
- return options
2514
-
2515
1910
  @distributed_trace
2516
1911
  def stage_block(
2517
- self, block_id, # type: str
2518
- data, # type: Union[Iterable[AnyStr], IO[AnyStr]]
2519
- length=None, # type: Optional[int]
2520
- **kwargs
2521
- ):
2522
- # 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]:
2523
1917
  """Creates a new block to be committed as part of a blob.
2524
1918
 
2525
1919
  :param str block_id: A string value that identifies the block.
2526
1920
  The string should be less than or equal to 64 bytes in size.
2527
1921
  For a given blob, the block_id must be the same size for each block.
2528
1922
  :param data: The blob data.
2529
- :type data: Union[Iterable[AnyStr], IO[AnyStr]]
1923
+ :type data: Union[bytes, str, Iterable[AnyStr], IO[AnyStr]]
2530
1924
  :param int length: Size of the block.
2531
1925
  :keyword bool validate_content:
2532
1926
  If true, calculates an MD5 hash for each chunk of the blob. The storage
@@ -2561,74 +1955,33 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2561
1955
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
2562
1956
  This value is not tracked or validated on the client. To configure client-side network timesouts
2563
1957
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
2564
- #other-client--per-operation-configuration>`_.
1958
+ #other-client--per-operation-configuration>`__.
2565
1959
  :returns: Blob property dict.
2566
1960
  :rtype: dict[str, Any]
2567
1961
  """
2568
- options = self._stage_block_options(
2569
- block_id,
2570
- 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,
2571
1969
  length=length,
2572
1970
  **kwargs)
2573
1971
  try:
2574
- return self._client.block_blob.stage_block(**options)
1972
+ return cast(Dict[str, Any], self._client.block_blob.stage_block(**options))
2575
1973
  except HttpResponseError as error:
2576
1974
  process_storage_error(error)
2577
1975
 
2578
- def _stage_block_from_url_options(
2579
- self, block_id, # type: str
2580
- source_url, # type: str
2581
- source_offset=None, # type: Optional[int]
2582
- source_length=None, # type: Optional[int]
2583
- source_content_md5=None, # type: Optional[Union[bytes, bytearray]]
2584
- **kwargs
2585
- ):
2586
- # type: (...) -> Dict[str, Any]
2587
- source_authorization = kwargs.pop('source_authorization', None)
2588
- if source_length is not None and source_offset is None:
2589
- raise ValueError("Source offset value must not be None if length is set.")
2590
- if source_length is not None:
2591
- source_length = source_offset + source_length - 1
2592
- block_id = encode_base64(str(block_id))
2593
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
2594
- range_header = None
2595
- if source_offset is not None:
2596
- range_header, _ = validate_and_format_range_headers(source_offset, source_length)
2597
-
2598
- cpk_scope_info = get_cpk_scope_info(kwargs)
2599
- cpk = kwargs.pop('cpk', None)
2600
- cpk_info = None
2601
- if cpk:
2602
- if self.scheme.lower() != 'https':
2603
- raise ValueError("Customer provided encryption key must be used over HTTPS.")
2604
- cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
2605
- encryption_algorithm=cpk.algorithm)
2606
- options = {
2607
- 'copy_source_authorization': source_authorization,
2608
- 'block_id': block_id,
2609
- 'content_length': 0,
2610
- 'source_url': source_url,
2611
- 'source_range': range_header,
2612
- 'source_content_md5': bytearray(source_content_md5) if source_content_md5 else None,
2613
- 'timeout': kwargs.pop('timeout', None),
2614
- 'lease_access_conditions': access_conditions,
2615
- 'cpk_scope_info': cpk_scope_info,
2616
- 'cpk_info': cpk_info,
2617
- 'cls': return_response_headers,
2618
- }
2619
- options.update(kwargs)
2620
- return options
2621
-
2622
1976
  @distributed_trace
2623
1977
  def stage_block_from_url(
2624
- self, block_id, # type: Union[str, int]
2625
- source_url, # type: str
2626
- source_offset=None, # type: Optional[int]
2627
- source_length=None, # type: Optional[int]
2628
- source_content_md5=None, # type: Optional[Union[bytes, bytearray]]
2629
- **kwargs
2630
- ):
2631
- # 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]:
2632
1985
  """Creates a new block to be committed as part of a blob where
2633
1986
  the contents are read from a URL.
2634
1987
 
@@ -2665,38 +2018,32 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2665
2018
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
2666
2019
  This value is not tracked or validated on the client. To configure client-side network timesouts
2667
2020
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
2668
- #other-client--per-operation-configuration>`_.
2021
+ #other-client--per-operation-configuration>`__.
2669
2022
  :keyword str source_authorization:
2670
2023
  Authenticate as a service principal using a client secret to access a source blob. Ensure "bearer " is
2671
2024
  the prefix of the source_authorization string.
2672
2025
  :returns: Blob property dict.
2673
2026
  :rtype: dict[str, Any]
2674
2027
  """
2675
- options = self._stage_block_from_url_options(
2676
- block_id,
2677
- 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,
2678
2033
  source_offset=source_offset,
2679
2034
  source_length=source_length,
2680
2035
  source_content_md5=source_content_md5,
2681
2036
  **kwargs)
2682
2037
  try:
2683
- 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))
2684
2039
  except HttpResponseError as error:
2685
2040
  process_storage_error(error)
2686
2041
 
2687
- def _get_block_list_result(self, blocks):
2688
- # type: (BlockList) -> Tuple[List[BlobBlock], List[BlobBlock]]
2689
- committed = [] # type: List
2690
- uncommitted = [] # type: List
2691
- if blocks.committed_blocks:
2692
- committed = [BlobBlock._from_generated(b) for b in blocks.committed_blocks] # pylint: disable=protected-access
2693
- if blocks.uncommitted_blocks:
2694
- uncommitted = [BlobBlock._from_generated(b) for b in blocks.uncommitted_blocks] # pylint: disable=protected-access
2695
- return committed, uncommitted
2696
-
2697
2042
  @distributed_trace
2698
- def get_block_list(self, block_list_type="committed", **kwargs):
2699
- # 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]]:
2700
2047
  """The Get Block List operation retrieves the list of blocks that have
2701
2048
  been uploaded as part of a block blob.
2702
2049
 
@@ -2718,9 +2065,9 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2718
2065
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
2719
2066
  This value is not tracked or validated on the client. To configure client-side network timesouts
2720
2067
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
2721
- #other-client--per-operation-configuration>`_.
2068
+ #other-client--per-operation-configuration>`__.
2722
2069
  :returns: A tuple of two lists - committed and uncommitted blocks
2723
- :rtype: tuple(list(~azure.storage.blob.BlobBlock), list(~azure.storage.blob.BlobBlock))
2070
+ :rtype: Tuple[List[BlobBlock], List[BlobBlock]]
2724
2071
  """
2725
2072
  access_conditions = get_access_conditions(kwargs.pop('lease', None))
2726
2073
  mod_conditions = get_modify_conditions(kwargs)
@@ -2734,86 +2081,15 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2734
2081
  **kwargs)
2735
2082
  except HttpResponseError as error:
2736
2083
  process_storage_error(error)
2737
- return self._get_block_list_result(blocks)
2738
-
2739
- def _commit_block_list_options( # type: ignore
2740
- self, block_list, # type: List[BlobBlock]
2741
- content_settings=None, # type: Optional[ContentSettings]
2742
- metadata=None, # type: Optional[Dict[str, str]]
2743
- **kwargs
2744
- ):
2745
- # type: (...) -> Dict[str, Any]
2746
- if self.require_encryption or (self.key_encryption_key is not None):
2747
- raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
2748
- block_lookup = BlockLookupList(committed=[], uncommitted=[], latest=[])
2749
- for block in block_list:
2750
- try:
2751
- if block.state.value == 'committed':
2752
- block_lookup.committed.append(encode_base64(str(block.id)))
2753
- elif block.state.value == 'uncommitted':
2754
- block_lookup.uncommitted.append(encode_base64(str(block.id)))
2755
- else:
2756
- block_lookup.latest.append(encode_base64(str(block.id)))
2757
- except AttributeError:
2758
- block_lookup.latest.append(encode_base64(str(block)))
2759
- headers = kwargs.pop('headers', {})
2760
- headers.update(add_metadata_headers(metadata))
2761
- blob_headers = None
2762
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
2763
- mod_conditions = get_modify_conditions(kwargs)
2764
- if content_settings:
2765
- blob_headers = BlobHTTPHeaders(
2766
- blob_cache_control=content_settings.cache_control,
2767
- blob_content_type=content_settings.content_type,
2768
- blob_content_md5=content_settings.content_md5,
2769
- blob_content_encoding=content_settings.content_encoding,
2770
- blob_content_language=content_settings.content_language,
2771
- blob_content_disposition=content_settings.content_disposition
2772
- )
2773
-
2774
- validate_content = kwargs.pop('validate_content', False)
2775
- cpk_scope_info = get_cpk_scope_info(kwargs)
2776
- cpk = kwargs.pop('cpk', None)
2777
- cpk_info = None
2778
- if cpk:
2779
- if self.scheme.lower() != 'https':
2780
- raise ValueError("Customer provided encryption key must be used over HTTPS.")
2781
- cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
2782
- encryption_algorithm=cpk.algorithm)
2783
-
2784
- immutability_policy = kwargs.pop('immutability_policy', None)
2785
- if immutability_policy:
2786
- kwargs['immutability_policy_expiry'] = immutability_policy.expiry_time
2787
- kwargs['immutability_policy_mode'] = immutability_policy.policy_mode
2788
-
2789
- tier = kwargs.pop('standard_blob_tier', None)
2790
- blob_tags_string = serialize_blob_tags_header(kwargs.pop('tags', None))
2791
-
2792
- options = {
2793
- 'blocks': block_lookup,
2794
- 'blob_http_headers': blob_headers,
2795
- 'lease_access_conditions': access_conditions,
2796
- 'timeout': kwargs.pop('timeout', None),
2797
- 'modified_access_conditions': mod_conditions,
2798
- 'cls': return_response_headers,
2799
- 'validate_content': validate_content,
2800
- 'cpk_scope_info': cpk_scope_info,
2801
- 'cpk_info': cpk_info,
2802
- 'tier': tier.value if tier else None,
2803
- 'blob_tags_string': blob_tags_string,
2804
- 'headers': headers
2805
- }
2806
- options.update(kwargs)
2807
- return options
2084
+ return _get_block_list_result(blocks)
2808
2085
 
2809
2086
  @distributed_trace
2810
- def commit_block_list( # type: ignore
2811
- self, block_list, # type: List[BlobBlock]
2812
- content_settings=None, # type: Optional[ContentSettings]
2813
- metadata=None, # type: Optional[Dict[str, str]]
2814
- **kwargs
2815
- ):
2816
- # 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]]:
2817
2093
  """The Commit Block List operation writes a blob by specifying the list of
2818
2094
  block IDs that make up the blob.
2819
2095
 
@@ -2830,7 +2106,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2830
2106
  The tag set may contain at most 10 tags. Tag keys must be between 1 and 128 characters,
2831
2107
  and tag values must be between 0 and 256 characters.
2832
2108
  Valid tag key and value characters include: lowercase and uppercase letters, digits (0-9),
2833
- space (` `), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)
2109
+ space (' '), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)
2834
2110
 
2835
2111
  .. versionadded:: 12.4.0
2836
2112
 
@@ -2901,23 +2177,26 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2901
2177
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
2902
2178
  This value is not tracked or validated on the client. To configure client-side network timesouts
2903
2179
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
2904
- #other-client--per-operation-configuration>`_.
2180
+ #other-client--per-operation-configuration>`__.
2905
2181
  :returns: Blob-updated property dict (Etag and last modified).
2906
2182
  :rtype: dict(str, Any)
2907
2183
  """
2908
- options = self._commit_block_list_options(
2909
- 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,
2910
2190
  content_settings=content_settings,
2911
2191
  metadata=metadata,
2912
2192
  **kwargs)
2913
2193
  try:
2914
- 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))
2915
2195
  except HttpResponseError as error:
2916
2196
  process_storage_error(error)
2917
2197
 
2918
2198
  @distributed_trace
2919
- def set_premium_page_blob_tier(self, premium_page_blob_tier, **kwargs):
2920
- # type: (Union[str, PremiumPageBlobTier], **Any) -> None
2199
+ def set_premium_page_blob_tier(self, premium_page_blob_tier: "PremiumPageBlobTier", **kwargs: Any) -> None:
2921
2200
  """Sets the page blob tiers on the blob. This API is only supported for page blobs on premium accounts.
2922
2201
 
2923
2202
  :param premium_page_blob_tier:
@@ -2936,7 +2215,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2936
2215
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
2937
2216
  This value is not tracked or validated on the client. To configure client-side network timesouts
2938
2217
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
2939
- #other-client--per-operation-configuration>`_.
2218
+ #other-client--per-operation-configuration>`__.
2940
2219
  :keyword lease:
2941
2220
  Required if the blob has an active lease. Value can be a BlobLeaseClient object
2942
2221
  or the lease ID as a string.
@@ -2957,25 +2236,8 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2957
2236
  except HttpResponseError as error:
2958
2237
  process_storage_error(error)
2959
2238
 
2960
- def _set_blob_tags_options(self, tags=None, **kwargs):
2961
- # type: (Optional[Dict[str, str]], **Any) -> Dict[str, Any]
2962
- tags = serialize_blob_tags(tags)
2963
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
2964
- mod_conditions = get_modify_conditions(kwargs)
2965
- version_id = get_version_id(self.version_id, kwargs)
2966
-
2967
- options = {
2968
- 'tags': tags,
2969
- 'lease_access_conditions': access_conditions,
2970
- 'modified_access_conditions': mod_conditions,
2971
- 'version_id': version_id,
2972
- 'cls': return_response_headers}
2973
- options.update(kwargs)
2974
- return options
2975
-
2976
2239
  @distributed_trace
2977
- def set_blob_tags(self, tags=None, **kwargs):
2978
- # 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]:
2979
2241
  """The Set Tags operation enables users to set tags on a blob or specific blob version, but not snapshot.
2980
2242
  Each call to this operation replaces all existing tags attached to the blob. To remove all
2981
2243
  tags from the blob, call this operation with no tags set.
@@ -2988,7 +2250,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
2988
2250
  The tag set may contain at most 10 tags. Tag keys must be between 1 and 128 characters,
2989
2251
  and tag values must be between 0 and 256 characters.
2990
2252
  Valid tag key and value characters include: lowercase and uppercase letters, digits (0-9),
2991
- space (` `), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)
2253
+ space (' '), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)
2992
2254
  :type tags: dict(str, str)
2993
2255
  :keyword str version_id:
2994
2256
  The version id parameter is an opaque DateTime
@@ -3012,40 +2274,25 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
3012
2274
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
3013
2275
  This value is not tracked or validated on the client. To configure client-side network timesouts
3014
2276
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
3015
- #other-client--per-operation-configuration>`_.
2277
+ #other-client--per-operation-configuration>`__.
3016
2278
  :returns: Blob-updated property dict (Etag and last modified)
3017
2279
  :rtype: Dict[str, Any]
3018
2280
  """
3019
- 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)
3020
2283
  try:
3021
- return self._client.blob.set_tags(**options)
2284
+ return cast(Dict[str, Any], self._client.blob.set_tags(**options))
3022
2285
  except HttpResponseError as error:
3023
2286
  process_storage_error(error)
3024
2287
 
3025
- def _get_blob_tags_options(self, **kwargs):
3026
- # type: (**Any) -> Dict[str, str]
3027
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
3028
- mod_conditions = get_modify_conditions(kwargs)
3029
- version_id = get_version_id(self.version_id, kwargs)
3030
-
3031
- options = {
3032
- 'version_id': version_id,
3033
- 'snapshot': self.snapshot,
3034
- 'lease_access_conditions': access_conditions,
3035
- 'modified_access_conditions': mod_conditions,
3036
- 'timeout': kwargs.pop('timeout', None),
3037
- 'cls': return_headers_and_deserialized}
3038
- return options
3039
-
3040
2288
  @distributed_trace
3041
- def get_blob_tags(self, **kwargs):
3042
- # type: (**Any) -> Dict[str, str]
2289
+ def get_blob_tags(self, **kwargs: Any) -> Dict[str, str]:
3043
2290
  """The Get Tags operation enables users to get tags on a blob or specific blob version, or snapshot.
3044
2291
 
3045
2292
  .. versionadded:: 12.4.0
3046
2293
  This operation was introduced in API version '2019-12-12'.
3047
2294
 
3048
- :keyword str version_id:
2295
+ :keyword Optional[str] version_id:
3049
2296
  The version id parameter is an opaque DateTime
3050
2297
  value that, when present, specifies the version of the blob to add tags to.
3051
2298
  :keyword str if_tags_match_condition:
@@ -3060,58 +2307,25 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
3060
2307
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
3061
2308
  This value is not tracked or validated on the client. To configure client-side network timesouts
3062
2309
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
3063
- #other-client--per-operation-configuration>`_.
2310
+ #other-client--per-operation-configuration>`__.
3064
2311
  :returns: Key value pairs of blob tags.
3065
2312
  :rtype: Dict[str, str]
3066
2313
  """
3067
- 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)
3068
2316
  try:
3069
2317
  _, tags = self._client.blob.get_tags(**options)
3070
- return parse_tags(tags) # pylint: disable=protected-access
2318
+ return cast(Dict[str, str], parse_tags(tags))
3071
2319
  except HttpResponseError as error:
3072
2320
  process_storage_error(error)
3073
2321
 
3074
- def _get_page_ranges_options( # type: ignore
3075
- self, offset=None, # type: Optional[int]
3076
- length=None, # type: Optional[int]
3077
- previous_snapshot_diff=None, # type: Optional[Union[str, Dict[str, Any]]]
3078
- **kwargs
3079
- ):
3080
- # type: (...) -> Dict[str, Any]
3081
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
3082
- mod_conditions = get_modify_conditions(kwargs)
3083
- if length is not None and offset is None:
3084
- raise ValueError("Offset value must not be None if length is set.")
3085
- if length is not None:
3086
- length = offset + length - 1 # Reformat to an inclusive range index
3087
- page_range, _ = validate_and_format_range_headers(
3088
- offset, length, start_range_required=False, end_range_required=False, align_to_page=True
3089
- )
3090
- options = {
3091
- 'snapshot': self.snapshot,
3092
- 'lease_access_conditions': access_conditions,
3093
- 'modified_access_conditions': mod_conditions,
3094
- 'timeout': kwargs.pop('timeout', None),
3095
- 'range': page_range}
3096
- if previous_snapshot_diff:
3097
- try:
3098
- options['prevsnapshot'] = previous_snapshot_diff.snapshot # type: ignore
3099
- except AttributeError:
3100
- try:
3101
- options['prevsnapshot'] = previous_snapshot_diff['snapshot'] # type: ignore
3102
- except TypeError:
3103
- options['prevsnapshot'] = previous_snapshot_diff
3104
- options.update(kwargs)
3105
- return options
3106
-
3107
2322
  @distributed_trace
3108
- def get_page_ranges( # type: ignore
3109
- self, offset=None, # type: Optional[int]
3110
- length=None, # type: Optional[int]
3111
- previous_snapshot_diff=None, # type: Optional[Union[str, Dict[str, Any]]]
3112
- **kwargs
3113
- ):
3114
- # 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]]]:
3115
2329
  """DEPRECATED: Returns the list of valid page ranges for a Page Blob or snapshot
3116
2330
  of a page blob.
3117
2331
 
@@ -3165,7 +2379,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
3165
2379
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
3166
2380
  This value is not tracked or validated on the client. To configure client-side network timesouts
3167
2381
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
3168
- #other-client--per-operation-configuration>`_.
2382
+ #other-client--per-operation-configuration>`__.
3169
2383
  :returns:
3170
2384
  A tuple of two lists of page ranges as dictionaries with 'start' and 'end' keys.
3171
2385
  The first element are filled page ranges, the 2nd element is cleared page ranges.
@@ -3176,7 +2390,8 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
3176
2390
  DeprecationWarning
3177
2391
  )
3178
2392
 
3179
- options = self._get_page_ranges_options(
2393
+ options = _get_page_ranges_options(
2394
+ snapshot=self.snapshot,
3180
2395
  offset=offset,
3181
2396
  length=length,
3182
2397
  previous_snapshot_diff=previous_snapshot_diff,
@@ -3192,13 +2407,13 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
3192
2407
 
3193
2408
  @distributed_trace
3194
2409
  def list_page_ranges(
3195
- self,
3196
- *,
3197
- offset: Optional[int] = None,
3198
- length: Optional[int] = None,
3199
- previous_snapshot: Optional[Union[str, Dict[str, Any]]] = None,
3200
- **kwargs: Any
3201
- ) -> 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]:
3202
2417
  """Returns the list of valid page ranges for a Page Blob or snapshot
3203
2418
  of a page blob. If `previous_snapshot` is specified, the result will be
3204
2419
  a diff of changes between the target blob and the previous snapshot.
@@ -3257,12 +2472,13 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
3257
2472
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
3258
2473
  This value is not tracked or validated on the client. To configure client-side network timesouts
3259
2474
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
3260
- #other-client--per-operation-configuration>`_.
2475
+ #other-client--per-operation-configuration>`__.
3261
2476
  :returns: An iterable (auto-paging) of PageRange.
3262
2477
  :rtype: ~azure.core.paging.ItemPaged[~azure.storage.blob.PageRange]
3263
2478
  """
3264
2479
  results_per_page = kwargs.pop('results_per_page', None)
3265
- options = self._get_page_ranges_options(
2480
+ options = _get_page_ranges_options(
2481
+ snapshot=self.snapshot,
3266
2482
  offset=offset,
3267
2483
  length=length,
3268
2484
  previous_snapshot_diff=previous_snapshot,
@@ -3282,12 +2498,11 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
3282
2498
 
3283
2499
  @distributed_trace
3284
2500
  def get_page_range_diff_for_managed_disk(
3285
- self, previous_snapshot_url, # type: str
3286
- offset=None, # type: Optional[int]
3287
- length=None, # type: Optional[int]
3288
- **kwargs
3289
- ):
3290
- # 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]]]:
3291
2506
  """Returns the list of valid page ranges for a managed disk or snapshot.
3292
2507
 
3293
2508
  .. note::
@@ -3340,13 +2555,14 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
3340
2555
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
3341
2556
  This value is not tracked or validated on the client. To configure client-side network timesouts
3342
2557
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
3343
- #other-client--per-operation-configuration>`_.
2558
+ #other-client--per-operation-configuration>`__.
3344
2559
  :returns:
3345
2560
  A tuple of two lists of page ranges as dictionaries with 'start' and 'end' keys.
3346
2561
  The first element are filled page ranges, the 2nd element is cleared page ranges.
3347
2562
  :rtype: tuple(list(dict(str, str), list(dict(str, str))
3348
2563
  """
3349
- options = self._get_page_ranges_options(
2564
+ options = _get_page_ranges_options(
2565
+ snapshot=self.snapshot,
3350
2566
  offset=offset,
3351
2567
  length=length,
3352
2568
  prev_snapshot_url=previous_snapshot_url,
@@ -3357,25 +2573,12 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
3357
2573
  process_storage_error(error)
3358
2574
  return get_page_ranges_result(ranges)
3359
2575
 
3360
- def _set_sequence_number_options(self, sequence_number_action, sequence_number=None, **kwargs):
3361
- # type: (Union[str, SequenceNumberAction], Optional[str], **Any) -> Dict[str, Any]
3362
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
3363
- mod_conditions = get_modify_conditions(kwargs)
3364
- if sequence_number_action is None:
3365
- raise ValueError("A sequence number action must be specified")
3366
- options = {
3367
- 'sequence_number_action': sequence_number_action,
3368
- 'timeout': kwargs.pop('timeout', None),
3369
- 'blob_sequence_number': sequence_number,
3370
- 'lease_access_conditions': access_conditions,
3371
- 'modified_access_conditions': mod_conditions,
3372
- 'cls': return_response_headers}
3373
- options.update(kwargs)
3374
- return options
3375
-
3376
2576
  @distributed_trace
3377
- def set_sequence_number(self, sequence_number_action, sequence_number=None, **kwargs):
3378
- # 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]]:
3379
2582
  """Sets the blob sequence number.
3380
2583
 
3381
2584
  :param str sequence_number_action:
@@ -3417,44 +2620,18 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
3417
2620
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
3418
2621
  This value is not tracked or validated on the client. To configure client-side network timesouts
3419
2622
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
3420
- #other-client--per-operation-configuration>`_.
2623
+ #other-client--per-operation-configuration>`__.
3421
2624
  :returns: Blob-updated property dict (Etag and last modified).
3422
2625
  :rtype: dict(str, Any)
3423
2626
  """
3424
- options = self._set_sequence_number_options(
3425
- sequence_number_action, sequence_number=sequence_number, **kwargs)
2627
+ options = _set_sequence_number_options(sequence_number_action, sequence_number=sequence_number, **kwargs)
3426
2628
  try:
3427
- 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))
3428
2630
  except HttpResponseError as error:
3429
2631
  process_storage_error(error)
3430
2632
 
3431
- def _resize_blob_options(self, size, **kwargs):
3432
- # type: (int, **Any) -> Dict[str, Any]
3433
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
3434
- mod_conditions = get_modify_conditions(kwargs)
3435
- if size is None:
3436
- raise ValueError("A content length must be specified for a Page Blob.")
3437
-
3438
- cpk = kwargs.pop('cpk', None)
3439
- cpk_info = None
3440
- if cpk:
3441
- if self.scheme.lower() != 'https':
3442
- raise ValueError("Customer provided encryption key must be used over HTTPS.")
3443
- cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
3444
- encryption_algorithm=cpk.algorithm)
3445
- options = {
3446
- 'blob_content_length': size,
3447
- 'timeout': kwargs.pop('timeout', None),
3448
- 'lease_access_conditions': access_conditions,
3449
- 'modified_access_conditions': mod_conditions,
3450
- 'cpk_info': cpk_info,
3451
- 'cls': return_response_headers}
3452
- options.update(kwargs)
3453
- return options
3454
-
3455
2633
  @distributed_trace
3456
- def resize_blob(self, size, **kwargs):
3457
- # type: (int, **Any) -> Dict[str, Union[str, datetime]]
2634
+ def resize_blob(self, size: int, **kwargs: Any) -> Dict[str, Union[str, datetime]]:
3458
2635
  """Resizes a page blob to the specified size.
3459
2636
 
3460
2637
  If the specified value is less than the current size of the blob,
@@ -3499,74 +2676,25 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
3499
2676
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
3500
2677
  This value is not tracked or validated on the client. To configure client-side network timesouts
3501
2678
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
3502
- #other-client--per-operation-configuration>`_.
2679
+ #other-client--per-operation-configuration>`__.
3503
2680
  :returns: Blob-updated property dict (Etag and last modified).
3504
2681
  :rtype: dict(str, Any)
3505
2682
  """
3506
- 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)
3507
2686
  try:
3508
- return self._client.page_blob.resize(**options) # type: ignore
2687
+ return cast(Dict[str, Any], self._client.page_blob.resize(**options))
3509
2688
  except HttpResponseError as error:
3510
2689
  process_storage_error(error)
3511
2690
 
3512
- def _upload_page_options( # type: ignore
3513
- self, page, # type: bytes
3514
- offset, # type: int
3515
- length, # type: int
3516
- **kwargs
3517
- ):
3518
- # type: (...) -> Dict[str, Any]
3519
- if isinstance(page, str):
3520
- page = page.encode(kwargs.pop('encoding', 'UTF-8'))
3521
- if self.require_encryption or (self.key_encryption_key is not None):
3522
- raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
3523
-
3524
- if offset is None or offset % 512 != 0:
3525
- raise ValueError("offset must be an integer that aligns with 512 page size")
3526
- if length is None or length % 512 != 0:
3527
- raise ValueError("length must be an integer that aligns with 512 page size")
3528
- end_range = offset + length - 1 # Reformat to an inclusive range index
3529
- content_range = f'bytes={offset}-{end_range}' # type: ignore
3530
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
3531
- seq_conditions = SequenceNumberAccessConditions(
3532
- if_sequence_number_less_than_or_equal_to=kwargs.pop('if_sequence_number_lte', None),
3533
- if_sequence_number_less_than=kwargs.pop('if_sequence_number_lt', None),
3534
- if_sequence_number_equal_to=kwargs.pop('if_sequence_number_eq', None)
3535
- )
3536
- mod_conditions = get_modify_conditions(kwargs)
3537
- cpk_scope_info = get_cpk_scope_info(kwargs)
3538
- validate_content = kwargs.pop('validate_content', False)
3539
- cpk = kwargs.pop('cpk', None)
3540
- cpk_info = None
3541
- if cpk:
3542
- if self.scheme.lower() != 'https':
3543
- raise ValueError("Customer provided encryption key must be used over HTTPS.")
3544
- cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
3545
- encryption_algorithm=cpk.algorithm)
3546
- options = {
3547
- 'body': page[:length],
3548
- 'content_length': length,
3549
- 'transactional_content_md5': None,
3550
- 'timeout': kwargs.pop('timeout', None),
3551
- 'range': content_range,
3552
- 'lease_access_conditions': access_conditions,
3553
- 'sequence_number_access_conditions': seq_conditions,
3554
- 'modified_access_conditions': mod_conditions,
3555
- 'validate_content': validate_content,
3556
- 'cpk_scope_info': cpk_scope_info,
3557
- 'cpk_info': cpk_info,
3558
- 'cls': return_response_headers}
3559
- options.update(kwargs)
3560
- return options
3561
-
3562
2691
  @distributed_trace
3563
- def upload_page( # type: ignore
3564
- self, page, # type: bytes
3565
- offset, # type: int
3566
- length, # type: int
3567
- **kwargs
3568
- ):
3569
- # 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]]:
3570
2698
  """The Upload Pages operation writes a range of pages to a page blob.
3571
2699
 
3572
2700
  :param bytes page:
@@ -3644,89 +2772,32 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
3644
2772
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
3645
2773
  This value is not tracked or validated on the client. To configure client-side network timesouts
3646
2774
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
3647
- #other-client--per-operation-configuration>`_.
2775
+ #other-client--per-operation-configuration>`__.
3648
2776
  :returns: Blob-updated property dict (Etag and last modified).
3649
2777
  :rtype: dict(str, Any)
3650
2778
  """
3651
- 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(
3652
2784
  page=page,
3653
2785
  offset=offset,
3654
2786
  length=length,
3655
2787
  **kwargs)
3656
2788
  try:
3657
- return self._client.page_blob.upload_pages(**options) # type: ignore
2789
+ return cast(Dict[str, Any], self._client.page_blob.upload_pages(**options))
3658
2790
  except HttpResponseError as error:
3659
2791
  process_storage_error(error)
3660
2792
 
3661
- def _upload_pages_from_url_options( # type: ignore
3662
- self, source_url, # type: str
3663
- offset, # type: int
3664
- length, # type: int
3665
- source_offset, # type: int
3666
- **kwargs
3667
- ):
3668
- # type: (...) -> Dict[str, Any]
3669
- if self.require_encryption or (self.key_encryption_key is not None):
3670
- raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
3671
-
3672
- # TODO: extract the code to a method format_range
3673
- if offset is None or offset % 512 != 0:
3674
- raise ValueError("offset must be an integer that aligns with 512 page size")
3675
- if length is None or length % 512 != 0:
3676
- raise ValueError("length must be an integer that aligns with 512 page size")
3677
- if source_offset is None or offset % 512 != 0:
3678
- raise ValueError("source_offset must be an integer that aligns with 512 page size")
3679
-
3680
- # Format range
3681
- end_range = offset + length - 1
3682
- destination_range = f'bytes={offset}-{end_range}'
3683
- source_range = f'bytes={source_offset}-{source_offset + length - 1}' # should subtract 1 here?
3684
-
3685
- seq_conditions = SequenceNumberAccessConditions(
3686
- if_sequence_number_less_than_or_equal_to=kwargs.pop('if_sequence_number_lte', None),
3687
- if_sequence_number_less_than=kwargs.pop('if_sequence_number_lt', None),
3688
- if_sequence_number_equal_to=kwargs.pop('if_sequence_number_eq', None)
3689
- )
3690
- source_authorization = kwargs.pop('source_authorization', None)
3691
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
3692
- mod_conditions = get_modify_conditions(kwargs)
3693
- source_mod_conditions = get_source_conditions(kwargs)
3694
- cpk_scope_info = get_cpk_scope_info(kwargs)
3695
- source_content_md5 = kwargs.pop('source_content_md5', None)
3696
- cpk = kwargs.pop('cpk', None)
3697
- cpk_info = None
3698
- if cpk:
3699
- if self.scheme.lower() != 'https':
3700
- raise ValueError("Customer provided encryption key must be used over HTTPS.")
3701
- cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
3702
- encryption_algorithm=cpk.algorithm)
3703
-
3704
- options = {
3705
- 'copy_source_authorization': source_authorization,
3706
- 'source_url': source_url,
3707
- 'content_length': 0,
3708
- 'source_range': source_range,
3709
- 'range': destination_range,
3710
- 'source_content_md5': bytearray(source_content_md5) if source_content_md5 else None,
3711
- 'timeout': kwargs.pop('timeout', None),
3712
- 'lease_access_conditions': access_conditions,
3713
- 'sequence_number_access_conditions': seq_conditions,
3714
- 'modified_access_conditions': mod_conditions,
3715
- 'source_modified_access_conditions': source_mod_conditions,
3716
- 'cpk_scope_info': cpk_scope_info,
3717
- 'cpk_info': cpk_info,
3718
- 'cls': return_response_headers}
3719
- options.update(kwargs)
3720
- return options
3721
-
3722
2793
  @distributed_trace
3723
- def upload_pages_from_url(self, source_url, # type: str
3724
- offset, # type: int
3725
- length, # type: int
3726
- source_offset, # type: int
3727
- **kwargs
3728
- ):
3729
- # 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]:
3730
2801
  """
3731
2802
  The Upload Pages operation writes a range of pages to a page blob where
3732
2803
  the contents are read from a URL.
@@ -3820,66 +2891,31 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
3820
2891
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
3821
2892
  This value is not tracked or validated on the client. To configure client-side network timesouts
3822
2893
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
3823
- #other-client--per-operation-configuration>`_.
2894
+ #other-client--per-operation-configuration>`__.
3824
2895
  :keyword str source_authorization:
3825
2896
  Authenticate as a service principal using a client secret to access a source blob. Ensure "bearer " is
3826
2897
  the prefix of the source_authorization string.
3827
2898
  :returns: Response after uploading pages from specified URL.
3828
2899
  :rtype: Dict[str, Any]
3829
2900
  """
3830
- options = self._upload_pages_from_url_options(
3831
- 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,
3832
2907
  offset=offset,
3833
2908
  length=length,
3834
2909
  source_offset=source_offset,
3835
2910
  **kwargs
3836
2911
  )
3837
2912
  try:
3838
- 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))
3839
2914
  except HttpResponseError as error:
3840
2915
  process_storage_error(error)
3841
2916
 
3842
- def _clear_page_options(self, offset, length, **kwargs):
3843
- # type: (int, int, **Any) -> Dict[str, Any]
3844
- if self.require_encryption or (self.key_encryption_key is not None):
3845
- raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
3846
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
3847
- seq_conditions = SequenceNumberAccessConditions(
3848
- if_sequence_number_less_than_or_equal_to=kwargs.pop('if_sequence_number_lte', None),
3849
- if_sequence_number_less_than=kwargs.pop('if_sequence_number_lt', None),
3850
- if_sequence_number_equal_to=kwargs.pop('if_sequence_number_eq', None)
3851
- )
3852
- mod_conditions = get_modify_conditions(kwargs)
3853
- if offset is None or offset % 512 != 0:
3854
- raise ValueError("offset must be an integer that aligns with 512 page size")
3855
- if length is None or length % 512 != 0:
3856
- raise ValueError("length must be an integer that aligns with 512 page size")
3857
- end_range = length + offset - 1 # Reformat to an inclusive range index
3858
- content_range = f'bytes={offset}-{end_range}'
3859
-
3860
- cpk = kwargs.pop('cpk', None)
3861
- cpk_info = None
3862
- if cpk:
3863
- if self.scheme.lower() != 'https':
3864
- raise ValueError("Customer provided encryption key must be used over HTTPS.")
3865
- cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
3866
- encryption_algorithm=cpk.algorithm)
3867
-
3868
- options = {
3869
- 'content_length': 0,
3870
- 'timeout': kwargs.pop('timeout', None),
3871
- 'range': content_range,
3872
- 'lease_access_conditions': access_conditions,
3873
- 'sequence_number_access_conditions': seq_conditions,
3874
- 'modified_access_conditions': mod_conditions,
3875
- 'cpk_info': cpk_info,
3876
- 'cls': return_response_headers}
3877
- options.update(kwargs)
3878
- return options
3879
-
3880
2917
  @distributed_trace
3881
- def clear_page(self, offset, length, **kwargs):
3882
- # 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]]:
3883
2919
  """Clears a range of pages.
3884
2920
 
3885
2921
  :param int offset:
@@ -3938,77 +2974,30 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
3938
2974
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
3939
2975
  This value is not tracked or validated on the client. To configure client-side network timesouts
3940
2976
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
3941
- #other-client--per-operation-configuration>`_.
2977
+ #other-client--per-operation-configuration>`__.
3942
2978
  :returns: Blob-updated property dict (Etag and last modified).
3943
2979
  :rtype: dict(str, Any)
3944
2980
  """
3945
- 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
+ )
3946
2990
  try:
3947
- return self._client.page_blob.clear_pages(**options) # type: ignore
2991
+ return cast(Dict[str, Any], self._client.page_blob.clear_pages(**options))
3948
2992
  except HttpResponseError as error:
3949
2993
  process_storage_error(error)
3950
2994
 
3951
- def _append_block_options( # type: ignore
3952
- self, data, # type: Union[bytes, str, Iterable[AnyStr], IO[AnyStr]]
3953
- length=None, # type: Optional[int]
3954
- **kwargs
3955
- ):
3956
- # type: (...) -> Dict[str, Any]
3957
- if self.require_encryption or (self.key_encryption_key is not None):
3958
- raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
3959
-
3960
- if isinstance(data, str):
3961
- data = data.encode(kwargs.pop('encoding', 'UTF-8')) # type: ignore
3962
- if length is None:
3963
- length = get_length(data)
3964
- if length is None:
3965
- length, data = read_length(data)
3966
- if length == 0:
3967
- return {}
3968
- if isinstance(data, bytes):
3969
- data = data[:length]
3970
-
3971
- appendpos_condition = kwargs.pop('appendpos_condition', None)
3972
- maxsize_condition = kwargs.pop('maxsize_condition', None)
3973
- validate_content = kwargs.pop('validate_content', False)
3974
- append_conditions = None
3975
- if maxsize_condition or appendpos_condition is not None:
3976
- append_conditions = AppendPositionAccessConditions(
3977
- max_size=maxsize_condition,
3978
- append_position=appendpos_condition
3979
- )
3980
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
3981
- mod_conditions = get_modify_conditions(kwargs)
3982
- cpk_scope_info = get_cpk_scope_info(kwargs)
3983
- cpk = kwargs.pop('cpk', None)
3984
- cpk_info = None
3985
- if cpk:
3986
- if self.scheme.lower() != 'https':
3987
- raise ValueError("Customer provided encryption key must be used over HTTPS.")
3988
- cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
3989
- encryption_algorithm=cpk.algorithm)
3990
- options = {
3991
- 'body': data,
3992
- 'content_length': length,
3993
- 'timeout': kwargs.pop('timeout', None),
3994
- 'transactional_content_md5': None,
3995
- 'lease_access_conditions': access_conditions,
3996
- 'append_position_access_conditions': append_conditions,
3997
- 'modified_access_conditions': mod_conditions,
3998
- 'validate_content': validate_content,
3999
- 'cpk_scope_info': cpk_scope_info,
4000
- 'cpk_info': cpk_info,
4001
- 'cls': return_response_headers}
4002
- options.update(kwargs)
4003
- return options
4004
-
4005
2995
  @distributed_trace
4006
- def append_block( # type: ignore
4007
- self, data, # type: Union[bytes, str, Iterable[AnyStr], IO[AnyStr]]
4008
- length=None, # type: Optional[int]
4009
- **kwargs
4010
- ):
4011
- # 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]]:
4012
3001
  """Commits a new block of data to the end of the existing append blob.
4013
3002
 
4014
3003
  :param data:
@@ -4082,87 +3071,31 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
4082
3071
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
4083
3072
  This value is not tracked or validated on the client. To configure client-side network timesouts
4084
3073
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
4085
- #other-client--per-operation-configuration>`_.
3074
+ #other-client--per-operation-configuration>`__.
4086
3075
  :returns: Blob-updated property dict (Etag, last modified, append offset, committed block count).
4087
3076
  :rtype: dict(str, Any)
4088
3077
  """
4089
- options = self._append_block_options(
4090
- 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,
4091
3084
  length=length,
4092
3085
  **kwargs
4093
3086
  )
4094
3087
  try:
4095
- return self._client.append_blob.append_block(**options) # type: ignore
3088
+ return cast(Dict[str, Any], self._client.append_blob.append_block(**options))
4096
3089
  except HttpResponseError as error:
4097
3090
  process_storage_error(error)
4098
3091
 
4099
- def _append_block_from_url_options( # type: ignore
4100
- self, copy_source_url, # type: str
4101
- source_offset=None, # type: Optional[int]
4102
- source_length=None, # type: Optional[int]
4103
- **kwargs
4104
- ):
4105
- # type: (...) -> Dict[str, Any]
4106
- if self.require_encryption or (self.key_encryption_key is not None):
4107
- raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
4108
-
4109
- # If end range is provided, start range must be provided
4110
- if source_length is not None and source_offset is None:
4111
- raise ValueError("source_offset should also be specified if source_length is specified")
4112
- # Format based on whether length is present
4113
- source_range = None
4114
- if source_length is not None:
4115
- end_range = source_offset + source_length - 1
4116
- source_range = f'bytes={source_offset}-{end_range}'
4117
- elif source_offset is not None:
4118
- source_range = f"bytes={source_offset}-"
4119
-
4120
- appendpos_condition = kwargs.pop('appendpos_condition', None)
4121
- maxsize_condition = kwargs.pop('maxsize_condition', None)
4122
- source_content_md5 = kwargs.pop('source_content_md5', None)
4123
- append_conditions = None
4124
- if maxsize_condition or appendpos_condition is not None:
4125
- append_conditions = AppendPositionAccessConditions(
4126
- max_size=maxsize_condition,
4127
- append_position=appendpos_condition
4128
- )
4129
- source_authorization = kwargs.pop('source_authorization', None)
4130
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
4131
- mod_conditions = get_modify_conditions(kwargs)
4132
- source_mod_conditions = get_source_conditions(kwargs)
4133
- cpk_scope_info = get_cpk_scope_info(kwargs)
4134
- cpk = kwargs.pop('cpk', None)
4135
- cpk_info = None
4136
- if cpk:
4137
- if self.scheme.lower() != 'https':
4138
- raise ValueError("Customer provided encryption key must be used over HTTPS.")
4139
- cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
4140
- encryption_algorithm=cpk.algorithm)
4141
-
4142
- options = {
4143
- 'copy_source_authorization': source_authorization,
4144
- 'source_url': copy_source_url,
4145
- 'content_length': 0,
4146
- 'source_range': source_range,
4147
- 'source_content_md5': source_content_md5,
4148
- 'transactional_content_md5': None,
4149
- 'lease_access_conditions': access_conditions,
4150
- 'append_position_access_conditions': append_conditions,
4151
- 'modified_access_conditions': mod_conditions,
4152
- 'source_modified_access_conditions': source_mod_conditions,
4153
- 'cpk_scope_info': cpk_scope_info,
4154
- 'cpk_info': cpk_info,
4155
- 'cls': return_response_headers,
4156
- 'timeout': kwargs.pop('timeout', None)}
4157
- options.update(kwargs)
4158
- return options
4159
-
4160
3092
  @distributed_trace
4161
- def append_block_from_url(self, copy_source_url, # type: str
4162
- source_offset=None, # type: Optional[int]
4163
- source_length=None, # type: Optional[int]
4164
- **kwargs):
4165
- # 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]]:
4166
3099
  """
4167
3100
  Creates a new block to be committed as part of a blob, where the contents are read from a source url.
4168
3101
 
@@ -4250,50 +3183,31 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
4250
3183
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
4251
3184
  This value is not tracked or validated on the client. To configure client-side network timesouts
4252
3185
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
4253
- #other-client--per-operation-configuration>`_.
3186
+ #other-client--per-operation-configuration>`__.
4254
3187
  :keyword str source_authorization:
4255
3188
  Authenticate as a service principal using a client secret to access a source blob. Ensure "bearer " is
4256
3189
  the prefix of the source_authorization string.
4257
3190
  :returns: Result after appending a new block.
4258
3191
  :rtype: Dict[str, Union[str, datetime, int]]
4259
3192
  """
4260
- options = self._append_block_from_url_options(
4261
- 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,
4262
3199
  source_offset=source_offset,
4263
3200
  source_length=source_length,
4264
3201
  **kwargs
4265
3202
  )
4266
3203
  try:
4267
- 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))
4268
3206
  except HttpResponseError as error:
4269
3207
  process_storage_error(error)
4270
3208
 
4271
- def _seal_append_blob_options(self, **kwargs):
4272
- # type: (...) -> Dict[str, Any]
4273
- if self.require_encryption or (self.key_encryption_key is not None):
4274
- raise ValueError(_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION)
4275
-
4276
- appendpos_condition = kwargs.pop('appendpos_condition', None)
4277
- append_conditions = None
4278
- if appendpos_condition is not None:
4279
- append_conditions = AppendPositionAccessConditions(
4280
- append_position=appendpos_condition
4281
- )
4282
- access_conditions = get_access_conditions(kwargs.pop('lease', None))
4283
- mod_conditions = get_modify_conditions(kwargs)
4284
-
4285
- options = {
4286
- 'timeout': kwargs.pop('timeout', None),
4287
- 'lease_access_conditions': access_conditions,
4288
- 'append_position_access_conditions': append_conditions,
4289
- 'modified_access_conditions': mod_conditions,
4290
- 'cls': return_response_headers}
4291
- options.update(kwargs)
4292
- return options
4293
-
4294
3209
  @distributed_trace
4295
- def seal_append_blob(self, **kwargs):
4296
- # type: (...) -> Dict[str, Union[str, datetime, int]]
3210
+ def seal_append_blob(self, **kwargs: Any) -> Dict[str, Union[str, datetime, int]]:
4297
3211
  """The Seal operation seals the Append Blob to make it read-only.
4298
3212
 
4299
3213
  .. versionadded:: 12.4.0
@@ -4330,19 +3244,20 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
4330
3244
  https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
4331
3245
  This value is not tracked or validated on the client. To configure client-side network timesouts
4332
3246
  see `here <https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
4333
- #other-client--per-operation-configuration>`_.
3247
+ #other-client--per-operation-configuration>`__.
4334
3248
  :returns: Blob-updated property dict (Etag, last modified, append offset, committed block count).
4335
3249
  :rtype: dict(str, Any)
4336
3250
  """
4337
- 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)
4338
3254
  try:
4339
- return self._client.append_blob.seal(**options) # type: ignore
3255
+ return cast(Dict[str, Any], self._client.append_blob.seal(**options))
4340
3256
  except HttpResponseError as error:
4341
3257
  process_storage_error(error)
4342
3258
 
4343
3259
  @distributed_trace
4344
- def _get_container_client(self): # pylint: disable=client-method-missing-kwargs
4345
- # type: (...) -> ContainerClient
3260
+ def _get_container_client(self) -> "ContainerClient":
4346
3261
  """Get a client to interact with the blob's parent container.
4347
3262
 
4348
3263
  The container need not already exist. Defaults to current blob's credentials.
@@ -4366,7 +3281,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
4366
3281
  policies=self._pipeline._impl_policies # pylint: disable = protected-access
4367
3282
  )
4368
3283
  else:
4369
- _pipeline = self._pipeline # pylint: disable = protected-access
3284
+ _pipeline = self._pipeline # pylint: disable = protected-access
4370
3285
  return ContainerClient(
4371
3286
  f"{self.scheme}://{self.primary_hostname}", container_name=self.container_name,
4372
3287
  credential=self._raw_credential, api_version=self.api_version, _configuration=self._config,