azure-storage-blob 12.23.0b1__py3-none-any.whl → 12.23.1__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 (30) hide show
  1. azure/storage/blob/_container_client.py +6 -0
  2. azure/storage/blob/_container_client_helpers.py +7 -2
  3. azure/storage/blob/_encryption.py +13 -10
  4. azure/storage/blob/_generated/_azure_blob_storage.py +2 -1
  5. azure/storage/blob/_generated/_serialization.py +2 -0
  6. azure/storage/blob/_generated/aio/_azure_blob_storage.py +2 -1
  7. azure/storage/blob/_generated/aio/operations/_append_blob_operations.py +1 -7
  8. azure/storage/blob/_generated/aio/operations/_blob_operations.py +21 -47
  9. azure/storage/blob/_generated/aio/operations/_block_blob_operations.py +2 -10
  10. azure/storage/blob/_generated/aio/operations/_container_operations.py +13 -26
  11. azure/storage/blob/_generated/aio/operations/_page_blob_operations.py +3 -14
  12. azure/storage/blob/_generated/aio/operations/_service_operations.py +14 -17
  13. azure/storage/blob/_generated/operations/_append_blob_operations.py +1 -7
  14. azure/storage/blob/_generated/operations/_blob_operations.py +21 -47
  15. azure/storage/blob/_generated/operations/_block_blob_operations.py +2 -10
  16. azure/storage/blob/_generated/operations/_container_operations.py +13 -26
  17. azure/storage/blob/_generated/operations/_page_blob_operations.py +3 -14
  18. azure/storage/blob/_generated/operations/_service_operations.py +14 -17
  19. azure/storage/blob/_shared/base_client.py +2 -0
  20. azure/storage/blob/_shared/policies.py +8 -9
  21. azure/storage/blob/_shared/policies_async.py +18 -5
  22. azure/storage/blob/_version.py +1 -1
  23. azure/storage/blob/aio/_container_client_async.py +6 -0
  24. azure/storage/blob/aio/_download_async.py +94 -71
  25. {azure_storage_blob-12.23.0b1.dist-info → azure_storage_blob-12.23.1.dist-info}/METADATA +2 -2
  26. {azure_storage_blob-12.23.0b1.dist-info → azure_storage_blob-12.23.1.dist-info}/RECORD +29 -30
  27. {azure_storage_blob-12.23.0b1.dist-info → azure_storage_blob-12.23.1.dist-info}/WHEEL +1 -1
  28. azure/storage/blob/_generated/_vendor.py +0 -16
  29. {azure_storage_blob-12.23.0b1.dist-info → azure_storage_blob-12.23.1.dist-info}/LICENSE +0 -0
  30. {azure_storage_blob-12.23.0b1.dist-info → azure_storage_blob-12.23.1.dist-info}/top_level.txt +0 -0
@@ -15,17 +15,17 @@ from azure.core.exceptions import (
15
15
  ResourceExistsError,
16
16
  ResourceNotFoundError,
17
17
  ResourceNotModifiedError,
18
+ StreamClosedError,
19
+ StreamConsumedError,
18
20
  map_error,
19
21
  )
20
22
  from azure.core.pipeline import PipelineResponse
21
- from azure.core.pipeline.transport import HttpResponse
22
- from azure.core.rest import HttpRequest
23
+ from azure.core.rest import HttpRequest, HttpResponse
23
24
  from azure.core.tracing.decorator import distributed_trace
24
25
  from azure.core.utils import case_insensitive_dict
25
26
 
26
27
  from .. import models as _models
27
28
  from .._serialization import Serializer
28
- from .._vendor import _convert_request
29
29
 
30
30
  if sys.version_info >= (3, 9):
31
31
  from collections.abc import MutableMapping
@@ -427,7 +427,6 @@ class ServiceOperations:
427
427
  headers=_headers,
428
428
  params=_params,
429
429
  )
430
- _request = _convert_request(_request)
431
430
  _request.url = self._client.format_url(_request.url)
432
431
 
433
432
  _stream = False
@@ -497,7 +496,6 @@ class ServiceOperations:
497
496
  headers=_headers,
498
497
  params=_params,
499
498
  )
500
- _request = _convert_request(_request)
501
499
  _request.url = self._client.format_url(_request.url)
502
500
 
503
501
  _stream = False
@@ -519,7 +517,7 @@ class ServiceOperations:
519
517
  response_headers["x-ms-request-id"] = self._deserialize("str", response.headers.get("x-ms-request-id"))
520
518
  response_headers["x-ms-version"] = self._deserialize("str", response.headers.get("x-ms-version"))
521
519
 
522
- deserialized = self._deserialize("StorageServiceProperties", pipeline_response)
520
+ deserialized = self._deserialize("StorageServiceProperties", pipeline_response.http_response)
523
521
 
524
522
  if cls:
525
523
  return cls(pipeline_response, deserialized, response_headers) # type: ignore
@@ -572,7 +570,6 @@ class ServiceOperations:
572
570
  headers=_headers,
573
571
  params=_params,
574
572
  )
575
- _request = _convert_request(_request)
576
573
  _request.url = self._client.format_url(_request.url)
577
574
 
578
575
  _stream = False
@@ -595,7 +592,7 @@ class ServiceOperations:
595
592
  response_headers["x-ms-version"] = self._deserialize("str", response.headers.get("x-ms-version"))
596
593
  response_headers["Date"] = self._deserialize("rfc-1123", response.headers.get("Date"))
597
594
 
598
- deserialized = self._deserialize("StorageServiceStats", pipeline_response)
595
+ deserialized = self._deserialize("StorageServiceStats", pipeline_response.http_response)
599
596
 
600
597
  if cls:
601
598
  return cls(pipeline_response, deserialized, response_headers) # type: ignore
@@ -676,7 +673,6 @@ class ServiceOperations:
676
673
  headers=_headers,
677
674
  params=_params,
678
675
  )
679
- _request = _convert_request(_request)
680
676
  _request.url = self._client.format_url(_request.url)
681
677
 
682
678
  _stream = False
@@ -698,7 +694,7 @@ class ServiceOperations:
698
694
  response_headers["x-ms-request-id"] = self._deserialize("str", response.headers.get("x-ms-request-id"))
699
695
  response_headers["x-ms-version"] = self._deserialize("str", response.headers.get("x-ms-version"))
700
696
 
701
- deserialized = self._deserialize("ListContainersSegmentResponse", pipeline_response)
697
+ deserialized = self._deserialize("ListContainersSegmentResponse", pipeline_response.http_response)
702
698
 
703
699
  if cls:
704
700
  return cls(pipeline_response, deserialized, response_headers) # type: ignore
@@ -761,7 +757,6 @@ class ServiceOperations:
761
757
  headers=_headers,
762
758
  params=_params,
763
759
  )
764
- _request = _convert_request(_request)
765
760
  _request.url = self._client.format_url(_request.url)
766
761
 
767
762
  _stream = False
@@ -784,7 +779,7 @@ class ServiceOperations:
784
779
  response_headers["x-ms-version"] = self._deserialize("str", response.headers.get("x-ms-version"))
785
780
  response_headers["Date"] = self._deserialize("rfc-1123", response.headers.get("Date"))
786
781
 
787
- deserialized = self._deserialize("UserDelegationKey", pipeline_response)
782
+ deserialized = self._deserialize("UserDelegationKey", pipeline_response.http_response)
788
783
 
789
784
  if cls:
790
785
  return cls(pipeline_response, deserialized, response_headers) # type: ignore
@@ -835,7 +830,6 @@ class ServiceOperations:
835
830
  headers=_headers,
836
831
  params=_params,
837
832
  )
838
- _request = _convert_request(_request)
839
833
  _request.url = self._client.format_url(_request.url)
840
834
 
841
835
  _stream = False
@@ -923,9 +917,9 @@ class ServiceOperations:
923
917
  headers=_headers,
924
918
  params=_params,
925
919
  )
926
- _request = _convert_request(_request)
927
920
  _request.url = self._client.format_url(_request.url)
928
921
 
922
+ _decompress = kwargs.pop("decompress", True)
929
923
  _stream = True
930
924
  pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access
931
925
  _request, stream=_stream, **kwargs
@@ -934,6 +928,10 @@ class ServiceOperations:
934
928
  response = pipeline_response.http_response
935
929
 
936
930
  if response.status_code not in [200]:
931
+ try:
932
+ response.read() # Load the body in memory and close the socket
933
+ except (StreamConsumedError, StreamClosedError):
934
+ pass
937
935
  map_error(status_code=response.status_code, response=response, error_map=error_map)
938
936
  error = self._deserialize.failsafe_deserialize(_models.StorageError, pipeline_response)
939
937
  raise HttpResponseError(response=response, model=error)
@@ -943,7 +941,7 @@ class ServiceOperations:
943
941
  response_headers["x-ms-request-id"] = self._deserialize("str", response.headers.get("x-ms-request-id"))
944
942
  response_headers["x-ms-version"] = self._deserialize("str", response.headers.get("x-ms-version"))
945
943
 
946
- deserialized = response.stream_download(self._client._pipeline)
944
+ deserialized = response.stream_download(self._client._pipeline, decompress=_decompress)
947
945
 
948
946
  if cls:
949
947
  return cls(pipeline_response, deserialized, response_headers) # type: ignore
@@ -1025,7 +1023,6 @@ class ServiceOperations:
1025
1023
  headers=_headers,
1026
1024
  params=_params,
1027
1025
  )
1028
- _request = _convert_request(_request)
1029
1026
  _request.url = self._client.format_url(_request.url)
1030
1027
 
1031
1028
  _stream = False
@@ -1048,7 +1045,7 @@ class ServiceOperations:
1048
1045
  response_headers["x-ms-version"] = self._deserialize("str", response.headers.get("x-ms-version"))
1049
1046
  response_headers["Date"] = self._deserialize("rfc-1123", response.headers.get("Date"))
1050
1047
 
1051
- deserialized = self._deserialize("FilterBlobSegment", pipeline_response)
1048
+ deserialized = self._deserialize("FilterBlobSegment", pipeline_response.http_response)
1052
1049
 
1053
1050
  if cls:
1054
1051
  return cls(pipeline_response, deserialized, response_headers) # type: ignore
@@ -76,6 +76,7 @@ class StorageAccountHostsMixin(object): # pylint: disable=too-many-instance-att
76
76
  self._location_mode = kwargs.get("_location_mode", LocationMode.PRIMARY)
77
77
  self._hosts = kwargs.get("_hosts")
78
78
  self.scheme = parsed_url.scheme
79
+ self._is_localhost = False
79
80
 
80
81
  if service not in ["blob", "queue", "file-share", "dfs"]:
81
82
  raise ValueError(f"Invalid service: {service}")
@@ -85,6 +86,7 @@ class StorageAccountHostsMixin(object): # pylint: disable=too-many-instance-att
85
86
  self.account_name = account[0] if len(account) > 1 else None
86
87
  if not self.account_name and parsed_url.netloc.startswith("localhost") \
87
88
  or parsed_url.netloc.startswith("127.0.0.1"):
89
+ self._is_localhost = True
88
90
  self.account_name = parsed_url.path.strip("/")
89
91
 
90
92
  self.credential = _format_shared_key_credential(self.account_name, credential)
@@ -35,11 +35,6 @@ from .authentication import AzureSigningError, StorageHttpChallenge
35
35
  from .constants import DEFAULT_OAUTH_SCOPE
36
36
  from .models import LocationMode
37
37
 
38
- try:
39
- _unicode_type = unicode # type: ignore
40
- except NameError:
41
- _unicode_type = str
42
-
43
38
  if TYPE_CHECKING:
44
39
  from azure.core.credentials import TokenCredential
45
40
  from azure.core.pipeline.transport import ( # pylint: disable=non-abstract-transport-import
@@ -52,7 +47,7 @@ _LOGGER = logging.getLogger(__name__)
52
47
 
53
48
 
54
49
  def encode_base64(data):
55
- if isinstance(data, _unicode_type):
50
+ if isinstance(data, str):
56
51
  data = data.encode('utf-8')
57
52
  encoded = base64.b64encode(data)
58
53
  return encoded.decode('utf-8')
@@ -95,10 +90,14 @@ def is_retry(response, mode): # pylint: disable=too-many-return-statements
95
90
  if status in [501, 505]:
96
91
  return False
97
92
  return True
93
+ return False
94
+
95
+
96
+ def is_checksum_retry(response):
98
97
  # retry if invalid content md5
99
98
  if response.context.get('validate_content', False) and response.http_response.headers.get('content-md5'):
100
99
  computed_md5 = response.http_request.headers.get('content-md5', None) or \
101
- encode_base64(StorageContentValidation.get_content_md5(response.http_response.body()))
100
+ encode_base64(StorageContentValidation.get_content_md5(response.http_response.body()))
102
101
  if response.http_response.headers['content-md5'] != computed_md5:
103
102
  return True
104
103
  return False
@@ -301,7 +300,7 @@ class StorageResponseHook(HTTPPolicy):
301
300
 
302
301
  response = self.next.send(request)
303
302
 
304
- will_retry = is_retry(response, request.context.options.get('mode'))
303
+ will_retry = is_retry(response, request.context.options.get('mode')) or is_checksum_retry(response)
305
304
  # Auth error could come from Bearer challenge, in which case this request will be made again
306
305
  is_auth_error = response.http_response.status_code == 401
307
306
  should_update_counts = not (will_retry or is_auth_error)
@@ -527,7 +526,7 @@ class StorageRetryPolicy(HTTPPolicy):
527
526
  while retries_remaining:
528
527
  try:
529
528
  response = self.next.send(request)
530
- if is_retry(response, retry_settings['mode']):
529
+ if is_retry(response, retry_settings['mode']) or is_checksum_retry(response):
531
530
  retries_remaining = self.increment(
532
531
  retry_settings,
533
532
  request=request.http_request,
@@ -10,12 +10,12 @@ import logging
10
10
  import random
11
11
  from typing import Any, Dict, TYPE_CHECKING
12
12
 
13
- from azure.core.exceptions import AzureError
13
+ from azure.core.exceptions import AzureError, StreamClosedError, StreamConsumedError
14
14
  from azure.core.pipeline.policies import AsyncBearerTokenCredentialPolicy, AsyncHTTPPolicy
15
15
 
16
16
  from .authentication import AzureSigningError, StorageHttpChallenge
17
17
  from .constants import DEFAULT_OAUTH_SCOPE
18
- from .policies import is_retry, StorageRetryPolicy
18
+ from .policies import encode_base64, is_retry, StorageContentValidation, StorageRetryPolicy
19
19
 
20
20
  if TYPE_CHECKING:
21
21
  from azure.core.credentials_async import AsyncTokenCredential
@@ -42,6 +42,20 @@ async def retry_hook(settings, **kwargs):
42
42
  **kwargs)
43
43
 
44
44
 
45
+ async def is_checksum_retry(response):
46
+ # retry if invalid content md5
47
+ if response.context.get('validate_content', False) and response.http_response.headers.get('content-md5'):
48
+ try:
49
+ await response.http_response.read() # Load the body in memory and close the socket
50
+ except (StreamClosedError, StreamConsumedError):
51
+ pass
52
+ computed_md5 = response.http_request.headers.get('content-md5', None) or \
53
+ encode_base64(StorageContentValidation.get_content_md5(response.http_response.content))
54
+ if response.http_response.headers['content-md5'] != computed_md5:
55
+ return True
56
+ return False
57
+
58
+
45
59
  class AsyncStorageResponseHook(AsyncHTTPPolicy):
46
60
 
47
61
  def __init__(self, **kwargs): # pylint: disable=unused-argument
@@ -64,9 +78,8 @@ class AsyncStorageResponseHook(AsyncHTTPPolicy):
64
78
  request.context.options.pop('raw_response_hook', self._response_callback)
65
79
 
66
80
  response = await self.next.send(request)
67
- await response.http_response.load_body()
81
+ will_retry = is_retry(response, request.context.options.get('mode')) or await is_checksum_retry(response)
68
82
 
69
- will_retry = is_retry(response, request.context.options.get('mode'))
70
83
  # Auth error could come from Bearer challenge, in which case this request will be made again
71
84
  is_auth_error = response.http_response.status_code == 401
72
85
  should_update_counts = not (will_retry or is_auth_error)
@@ -112,7 +125,7 @@ class AsyncStorageRetryPolicy(StorageRetryPolicy):
112
125
  while retries_remaining:
113
126
  try:
114
127
  response = await self.next.send(request)
115
- if is_retry(response, retry_settings['mode']):
128
+ if is_retry(response, retry_settings['mode']) or await is_checksum_retry(response):
116
129
  retries_remaining = self.increment(
117
130
  retry_settings,
118
131
  request=request.http_request,
@@ -4,4 +4,4 @@
4
4
  # license information.
5
5
  # --------------------------------------------------------------------------
6
6
 
7
- VERSION = "12.23.0b1"
7
+ VERSION = "12.23.1"
@@ -1406,6 +1406,8 @@ class ContainerClient(AsyncStorageAccountHostsMixin, StorageAccountHostsMixin, S
1406
1406
  """
1407
1407
  if len(blobs) == 0:
1408
1408
  return AsyncList([])
1409
+ if self._is_localhost:
1410
+ kwargs['url_prepend'] = self.account_name
1409
1411
 
1410
1412
  reqs, options = _generate_delete_blobs_options(
1411
1413
  self._query_str,
@@ -1485,6 +1487,8 @@ class ContainerClient(AsyncStorageAccountHostsMixin, StorageAccountHostsMixin, S
1485
1487
  :return: An async iterator of responses, one for each blob in order
1486
1488
  :rtype: asynciterator[~azure.core.pipeline.transport.AsyncHttpResponse]
1487
1489
  """
1490
+ if self._is_localhost:
1491
+ kwargs['url_prepend'] = self.account_name
1488
1492
  reqs, options = _generate_set_tiers_options(
1489
1493
  self._query_str,
1490
1494
  self.container_name,
@@ -1544,6 +1548,8 @@ class ContainerClient(AsyncStorageAccountHostsMixin, StorageAccountHostsMixin, S
1544
1548
  :return: An async iterator of responses, one for each blob in order
1545
1549
  :rtype: asynciterator[~azure.core.pipeline.transport.AsyncHttpResponse]
1546
1550
  """
1551
+ if self._is_localhost:
1552
+ kwargs['url_prepend'] = self.account_name
1547
1553
  reqs, options = _generate_set_tiers_options(
1548
1554
  self._query_str,
1549
1555
  self.container_name,
@@ -19,7 +19,7 @@ from typing import (
19
19
  Tuple, TypeVar, Union, TYPE_CHECKING
20
20
  )
21
21
 
22
- from azure.core.exceptions import HttpResponseError
22
+ from azure.core.exceptions import DecodeError, HttpResponseError, IncompleteReadError
23
23
 
24
24
  from .._shared.request_handlers import validate_and_format_range_headers
25
25
  from .._shared.response_handlers import parse_length_from_content_range, process_storage_error
@@ -46,7 +46,8 @@ T = TypeVar('T', bytes, str)
46
46
  async def process_content(data: Any, start_offset: int, end_offset: int, encryption: Dict[str, Any]) -> bytes:
47
47
  if data is None:
48
48
  raise ValueError("Response cannot be None.")
49
- content = cast(bytes, data.response.body())
49
+ await data.response.read()
50
+ content = cast(bytes, data.response.content)
50
51
  if encryption.get('key') is not None or encryption.get('resolver') is not None:
51
52
  try:
52
53
  return decrypt_blob(
@@ -120,20 +121,30 @@ class _AsyncChunkDownloader(_ChunkDownloader):
120
121
  download_range[1],
121
122
  check_content_md5=self.validate_content
122
123
  )
123
- try:
124
- _, response = await cast(Awaitable[Any], self.client.download(
125
- range=range_header,
126
- range_get_content_md5=range_validation,
127
- validate_content=self.validate_content,
128
- data_stream_total=self.total_size,
129
- download_stream_current=self.progress_total,
130
- **self.request_options
131
- ))
132
124
 
133
- except HttpResponseError as error:
134
- process_storage_error(error)
125
+ retry_active = True
126
+ retry_total = 3
127
+ while retry_active:
128
+ try:
129
+ _, response = await cast(Awaitable[Any], self.client.download(
130
+ range=range_header,
131
+ range_get_content_md5=range_validation,
132
+ validate_content=self.validate_content,
133
+ data_stream_total=self.total_size,
134
+ download_stream_current=self.progress_total,
135
+ **self.request_options
136
+ ))
137
+ except HttpResponseError as error:
138
+ process_storage_error(error)
135
139
 
136
- chunk_data = await process_content(response, offset[0], offset[1], self.encryption_options)
140
+ try:
141
+ chunk_data = await process_content(response, offset[0], offset[1], self.encryption_options)
142
+ retry_active = False
143
+ except (IncompleteReadError, HttpResponseError, DecodeError) as error:
144
+ retry_total -= 1
145
+ if retry_total <= 0:
146
+ raise HttpResponseError(error, error=error) from error
147
+ await asyncio.sleep(1)
137
148
  content_length = response.content_length
138
149
 
139
150
  # This makes sure that if_match is set so that we can validate
@@ -342,66 +353,78 @@ class StorageStreamDownloader(Generic[T]): # pylint: disable=too-many-instance-
342
353
  self._initial_range[1],
343
354
  start_range_required=False,
344
355
  end_range_required=False,
345
- check_content_md5=self._validate_content)
356
+ check_content_md5=self._validate_content
357
+ )
346
358
 
347
- try:
348
- location_mode, response = cast(Tuple[Optional[str], Any], await self._clients.blob.download(
349
- range=range_header,
350
- range_get_content_md5=range_validation,
351
- validate_content=self._validate_content,
352
- data_stream_total=None,
353
- download_stream_current=0,
354
- **self._request_options))
355
-
356
- # Check the location we read from to ensure we use the same one
357
- # for subsequent requests.
358
- self._location_mode = location_mode
359
-
360
- # Parse the total file size and adjust the download size if ranges
361
- # were specified
362
- self._file_size = parse_length_from_content_range(response.properties.content_range)
363
- if self._file_size is None:
364
- raise ValueError("Required Content-Range response header is missing or malformed.")
365
- # Remove any extra encryption data size from blob size
366
- self._file_size = adjust_blob_size_for_encryption(self._file_size, self._encryption_data)
367
-
368
- if self._end_range is not None and self._start_range is not None:
369
- # Use the length unless it is over the end of the file
370
- self.size = min(self._file_size - self._start_range, self._end_range - self._start_range + 1)
371
- elif self._start_range is not None:
372
- self.size = self._file_size - self._start_range
373
- else:
374
- self.size = self._file_size
359
+ retry_active = True
360
+ retry_total = 3
361
+ while retry_active:
362
+ try:
363
+ location_mode, response = cast(Tuple[Optional[str], Any], await self._clients.blob.download(
364
+ range=range_header,
365
+ range_get_content_md5=range_validation,
366
+ validate_content=self._validate_content,
367
+ data_stream_total=None,
368
+ download_stream_current=0,
369
+ **self._request_options
370
+ ))
375
371
 
376
- except HttpResponseError as error:
377
- if self._start_range is None and error.response and error.status_code == 416:
378
- # Get range will fail on an empty file. If the user did not
379
- # request a range, do a regular get request in order to get
380
- # any properties.
381
- try:
382
- _, response = cast(Tuple[Optional[Any], Any], await self._clients.blob.download(
383
- validate_content=self._validate_content,
384
- data_stream_total=0,
385
- download_stream_current=0,
386
- **self._request_options))
387
- except HttpResponseError as e:
388
- process_storage_error(e)
389
-
390
- # Set the download size to empty
391
- self.size = 0
392
- self._file_size = 0
393
- else:
394
- process_storage_error(error)
372
+ # Check the location we read from to ensure we use the same one
373
+ # for subsequent requests.
374
+ self._location_mode = location_mode
375
+
376
+ # Parse the total file size and adjust the download size if ranges
377
+ # were specified
378
+ self._file_size = parse_length_from_content_range(response.properties.content_range)
379
+ if self._file_size is None:
380
+ raise ValueError("Required Content-Range response header is missing or malformed.")
381
+ # Remove any extra encryption data size from blob size
382
+ self._file_size = adjust_blob_size_for_encryption(self._file_size, self._encryption_data)
383
+
384
+ if self._end_range is not None and self._start_range is not None:
385
+ # Use the length unless it is over the end of the file
386
+ self.size = min(self._file_size - self._start_range, self._end_range - self._start_range + 1)
387
+ elif self._start_range is not None:
388
+ self.size = self._file_size - self._start_range
389
+ else:
390
+ self.size = self._file_size
395
391
 
396
- if self.size == 0:
397
- self._current_content = b""
398
- else:
399
- self._current_content = await process_content(
400
- response,
401
- self._initial_offset[0],
402
- self._initial_offset[1],
403
- self._encryption_options
404
- )
392
+ except HttpResponseError as error:
393
+ if self._start_range is None and error.response and error.status_code == 416:
394
+ # Get range will fail on an empty file. If the user did not
395
+ # request a range, do a regular get request in order to get
396
+ # any properties.
397
+ try:
398
+ _, response = cast(Tuple[Optional[Any], Any], await self._clients.blob.download(
399
+ validate_content=self._validate_content,
400
+ data_stream_total=0,
401
+ download_stream_current=0,
402
+ **self._request_options))
403
+ except HttpResponseError as e:
404
+ process_storage_error(e)
405
+
406
+ # Set the download size to empty
407
+ self.size = 0
408
+ self._file_size = 0
409
+ else:
410
+ process_storage_error(error)
411
+
412
+ try:
413
+ if self.size == 0:
414
+ self._current_content = b""
415
+ else:
416
+ self._current_content = await process_content(
417
+ response,
418
+ self._initial_offset[0],
419
+ self._initial_offset[1],
420
+ self._encryption_options
421
+ )
422
+ retry_active = False
423
+ except (IncompleteReadError, HttpResponseError, DecodeError) as error:
424
+ retry_total -= 1
425
+ if retry_total <= 0:
426
+ raise HttpResponseError(error, error=error) from error
427
+ await asyncio.sleep(1)
405
428
  self._download_offset += len(self._current_content)
406
429
  self._raw_download_offset += response.content_length
407
430
 
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: azure-storage-blob
3
- Version: 12.23.0b1
3
+ Version: 12.23.1
4
4
  Summary: Microsoft Azure Blob Storage Client Library for Python
5
5
  Home-page: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/storage/azure-storage-blob
6
6
  Author: Microsoft Corporation
7
7
  Author-email: ascl@microsoft.com
8
8
  License: MIT License
9
9
  Keywords: azure,azure sdk
10
- Classifier: Development Status :: 4 - Beta
10
+ Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: Programming Language :: Python
12
12
  Classifier: Programming Language :: Python :: 3 :: Only
13
13
  Classifier: Programming Language :: Python :: 3
@@ -3,11 +3,11 @@ azure/storage/blob/_blob_client.py,sha256=VFLd4oyTWawbxD1H7DBMfJHJcJNAUSXR-nzVxt
3
3
  azure/storage/blob/_blob_client_helpers.py,sha256=7tfQzgpV-cnwYc4i-lZEr4YibDDkyl5RCPybQtJZ-i0,51905
4
4
  azure/storage/blob/_blob_service_client.py,sha256=AKoFLHYt4pMREIeQQ3k892xs0XRfS6VV73365KLck-I,40366
5
5
  azure/storage/blob/_blob_service_client_helpers.py,sha256=8jNCrF5rsgdJyAJQTdRR_mcOYuDCw4Nt9AirZk2RYUY,997
6
- azure/storage/blob/_container_client.py,sha256=GKT43Z3PONwHZNTY026Cy4ddl1icYHLkh_QB9sPKK9g,84467
7
- azure/storage/blob/_container_client_helpers.py,sha256=Kp77eGkKgTMrFlwdOn_cQs3_jM-qipoQwqdhHRaUdJU,12359
6
+ azure/storage/blob/_container_client.py,sha256=piG3Jv1lciW3FifKp_K1trQOtCorm4TTElbq2lMyiT0,84722
7
+ azure/storage/blob/_container_client_helpers.py,sha256=7H8-506B1iDoCvvwnx3masFjiFSzL1jt5gzIhPmnafA,12596
8
8
  azure/storage/blob/_deserialize.py,sha256=VisgOi6WtpfkeOZ9lMcEAiZyg3A6AqR7oZO52WUXaWU,9937
9
9
  azure/storage/blob/_download.py,sha256=nvj_IBZuSQWV1fO2iB0n_LAndv95SRhbscuGmxu9hHE,40069
10
- azure/storage/blob/_encryption.py,sha256=yw1T7bw7WWSxi4utqCvbpcDTwiMBdsjw0-Eqvud_Ulc,47238
10
+ azure/storage/blob/_encryption.py,sha256=hRSsODop-NFAwO5HHu8sT5zQ1NuN4Absnr_NMhlCL44,47445
11
11
  azure/storage/blob/_lease.py,sha256=ReF0nVfZE8p_clUGpebZPprBdXJ7lOGEqXmJUhvsX5U,18336
12
12
  azure/storage/blob/_list_blobs_helper.py,sha256=smnTcpGSVkk93G0RI7YczhkIM0s0gx4bSGWj_DB8t_s,13160
13
13
  azure/storage/blob/_models.py,sha256=c01JsL1fCAWecXfUUD6Dn50qpjV9r5ZiViXXCwNZVN4,66018
@@ -15,48 +15,47 @@ azure/storage/blob/_quick_query_helper.py,sha256=HO6ufvSEWQSaFJ4CanujE4IN7FYB6y1
15
15
  azure/storage/blob/_serialize.py,sha256=5_MsQx2hVJnhNqlxP6_O7rksxEoGJXXSJSG3WIUd-iw,8146
16
16
  azure/storage/blob/_shared_access_signature.py,sha256=VkoKyo5apDnKQ8wuEBp1C6MaKlqDHAZOf5wlqRcqdOA,35354
17
17
  azure/storage/blob/_upload_helpers.py,sha256=-ZpqzST-wFdWqCm_I4oWGLTMQ5N0aYb3RHxaMvmf9Q4,14688
18
- azure/storage/blob/_version.py,sha256=Q6fsDbychx6wPqxZrmiA442MB6e6EId7rWNDbWmnhlg,333
18
+ azure/storage/blob/_version.py,sha256=UJeQ8inMo-t88amR6znjwTdSjvmma6OAm0OZr0xls6Q,331
19
19
  azure/storage/blob/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  azure/storage/blob/_generated/__init__.py,sha256=J2H2yiFhRSsMCNKUI7gaYFIQ4_AAbWjPtzXdOsHFQFI,835
21
- azure/storage/blob/_generated/_azure_blob_storage.py,sha256=Xpt7ZrX5n2nN0l5x8EU9tX8H1ZPaK0vOV0GEFNxhYxs,5716
21
+ azure/storage/blob/_generated/_azure_blob_storage.py,sha256=VKK-9xxlF9DfNHuxlOxG6LB0XOZDLFdviW8O4W2AqeI,5737
22
22
  azure/storage/blob/_generated/_configuration.py,sha256=PV4kKjbnHhg6nD30e_acUENnsLuEKKjYRHz1VqEk9UQ,2566
23
23
  azure/storage/blob/_generated/_patch.py,sha256=MdyWs5y2w9_vYRWulELR-RV2uRYkjYpdB7nTVz2tVY4,1532
24
- azure/storage/blob/_generated/_serialization.py,sha256=UL45pN1JUtWi96uVT5L9kbXGDtWUBHW0r4e2MeHpsKY,78893
25
- azure/storage/blob/_generated/_vendor.py,sha256=e3w-rd6okoiCIB8rNMtF0fehAYFWNlshwiwTsIRkEH4,778
24
+ azure/storage/blob/_generated/_serialization.py,sha256=KsZWmQKoQbf9OUEEXmJ0UV-b9-lZRA1tCkkYt_a-4AI,78971
26
25
  azure/storage/blob/_generated/py.typed,sha256=dcrsqJrcYfTX-ckLFJMTaj6mD8aDe2u0tkQG-ZYxnEg,26
27
26
  azure/storage/blob/_generated/aio/__init__.py,sha256=J2H2yiFhRSsMCNKUI7gaYFIQ4_AAbWjPtzXdOsHFQFI,835
28
- azure/storage/blob/_generated/aio/_azure_blob_storage.py,sha256=79r9sIDwid96ZMyrO3u0S4UnfmR3O3g-M8JDmsTULXU,5859
27
+ azure/storage/blob/_generated/aio/_azure_blob_storage.py,sha256=FPnirEqJ8Nb3mWn0JPL3qb8_myyaz8UCdUQNfOW8xDs,5880
29
28
  azure/storage/blob/_generated/aio/_configuration.py,sha256=Q4jfjKwpMOvSe2gS9lOdvwwsHvVtsJZN37AYrf4ySgg,2576
30
29
  azure/storage/blob/_generated/aio/_patch.py,sha256=MdyWs5y2w9_vYRWulELR-RV2uRYkjYpdB7nTVz2tVY4,1532
31
30
  azure/storage/blob/_generated/aio/operations/__init__.py,sha256=a5HiO2T3KzSSX8reO6fCwbKcwXqd0A6GIeF4t63XmTA,1181
32
- azure/storage/blob/_generated/aio/operations/_append_blob_operations.py,sha256=vJwsxrx2W6ZJigTPIiKshqF9uEEGvBdVeczTFOShFIs,37574
33
- azure/storage/blob/_generated/aio/operations/_blob_operations.py,sha256=MAf_mMTpX5T6-wKjLcn_dgF4jiwJzW-zhzeTTKsKs94,167651
34
- azure/storage/blob/_generated/aio/operations/_block_blob_operations.py,sha256=WPYthyypwq8EfgekTQpMKMIWWd6TM-TOkqW-V4oxrY0,60953
35
- azure/storage/blob/_generated/aio/operations/_container_operations.py,sha256=fr2_IzLrRYQ3UakGkwqg4rCrhUdC9s0WJaNOrLvnO-w,91268
36
- azure/storage/blob/_generated/aio/operations/_page_blob_operations.py,sha256=_BT4F56xZ8-nkcf9h-SbDIWwExGJ4nXpBWxPikYPFiY,75443
31
+ azure/storage/blob/_generated/aio/operations/_append_blob_operations.py,sha256=FtuAMOUlqQp0fGAowxhghw7Py0jeYblsScLPMAgRn_8,37309
32
+ azure/storage/blob/_generated/aio/operations/_blob_operations.py,sha256=x6rz3T7RZNBJtOmyc0TcnOYJMOKA8TbYi8emYIbbKkE,166403
33
+ azure/storage/blob/_generated/aio/operations/_block_blob_operations.py,sha256=DUu5kWJwMfjvKHI-5SIB_Nn9_Ea8H4KctB_YtdixICk,60610
34
+ azure/storage/blob/_generated/aio/operations/_container_operations.py,sha256=8BJPTAGM1pn8gvB4jQqDq2gYxp6Pb8lzkzJ5FzC9XzY,90725
35
+ azure/storage/blob/_generated/aio/operations/_page_blob_operations.py,sha256=jp5RocDr6Lp3gQRoGnNug8sVoX_G9G7vG-658KS8Z84,74976
37
36
  azure/storage/blob/_generated/aio/operations/_patch.py,sha256=pYl0jxVFr3Yu0RHRFIgN3NwFrEZr1uL-7xbEXGgJzBw,794
38
- azure/storage/blob/_generated/aio/operations/_service_operations.py,sha256=SUJkK6dCZpVV539AHl_h0CfpKieC4qY9RJ-XcK8oFEI,36029
37
+ azure/storage/blob/_generated/aio/operations/_service_operations.py,sha256=8UAsZEKJjT1a4sulVCWZ7B2_PVhL4WSVnFpU0BIlqrs,35960
39
38
  azure/storage/blob/_generated/models/__init__.py,sha256=qOh_WzGPNB7Do1XSXfRosHQJ94zx1G5dVXpZdkNKLuM,6303
40
39
  azure/storage/blob/_generated/models/_azure_blob_storage_enums.py,sha256=o1I_SPnUKEsx2Aec-goLDw6eqZMyTVqFxg7tKpSYg0I,13049
41
40
  azure/storage/blob/_generated/models/_models_py3.py,sha256=JXhdrOvO8VKo_Vhz-cqnXI1gfDf6nkrcBDC7Cz0rL_s,110612
42
41
  azure/storage/blob/_generated/models/_patch.py,sha256=pYl0jxVFr3Yu0RHRFIgN3NwFrEZr1uL-7xbEXGgJzBw,794
43
42
  azure/storage/blob/_generated/operations/__init__.py,sha256=a5HiO2T3KzSSX8reO6fCwbKcwXqd0A6GIeF4t63XmTA,1181
44
- azure/storage/blob/_generated/operations/_append_blob_operations.py,sha256=r-BCOjTSNBma3dxqIp26wnaLDmcdADTOI6ONSimW0ew,56245
45
- azure/storage/blob/_generated/operations/_blob_operations.py,sha256=fUtRL4dxM_UMLd0TgPXRE7E3oUqNdr_400CDT-gRqrc,233836
46
- azure/storage/blob/_generated/operations/_block_blob_operations.py,sha256=PtQDvIhTiY6-MZ2aal2nKO9mCcKTYUPRToKNjbZAi4Q,91756
47
- azure/storage/blob/_generated/operations/_container_operations.py,sha256=PjPuxlFz6hO_CeaZYCO7UjuESjg2OoqhXlqZAW9iPGI,128899
48
- azure/storage/blob/_generated/operations/_page_blob_operations.py,sha256=TPUQNbsZ8GbOIizKjzc2LwclyZ4la3lRZRSk8TtViO4,112841
43
+ azure/storage/blob/_generated/operations/_append_blob_operations.py,sha256=T7Kqy8hKbPTmN7nF0M3XgKP-Pewyn0oGQ5mKTmhbnTs,55981
44
+ azure/storage/blob/_generated/operations/_blob_operations.py,sha256=AGGNddKS8creiGVfpf3mPxvP_0FvgnUV3xHMDml36xk,232577
45
+ azure/storage/blob/_generated/operations/_block_blob_operations.py,sha256=XgZ4fwZpbBrEw8ztdq7aeCKE4t7yfsGZXKkxlw72gqY,91414
46
+ azure/storage/blob/_generated/operations/_container_operations.py,sha256=XuWEdsPzEwdDeM9FpD1s6S9q5-YggBUUn848snvWCKU,128351
47
+ azure/storage/blob/_generated/operations/_page_blob_operations.py,sha256=Gj6szO76q2_3YGUNdznMz9E9UNMZKvarm4DCc5IO18o,112375
49
48
  azure/storage/blob/_generated/operations/_patch.py,sha256=pYl0jxVFr3Yu0RHRFIgN3NwFrEZr1uL-7xbEXGgJzBw,794
50
- azure/storage/blob/_generated/operations/_service_operations.py,sha256=CSR4vFYxQ5AmLlH8KcC8pJplcFDYWDwjtFQt-5MzPvc,49892
49
+ azure/storage/blob/_generated/operations/_service_operations.py,sha256=nq9AqOcEM340nLxhzaUlzta3gD0KyOZZhCI_808uYT0,49818
51
50
  azure/storage/blob/_shared/__init__.py,sha256=Ohb4NSCuB9VXGEqjU2o9VZ5L98-a7c8KWZvrujnSFk8,1477
52
51
  azure/storage/blob/_shared/authentication.py,sha256=KfUKWkjItNJxUTWNcCNusYfnENy-XbVelHlZM8fWc0Y,9450
53
- azure/storage/blob/_shared/base_client.py,sha256=m8APWNQ2cbvMFWdR6y8a1iA4h9BxSA-nQ0ovQr2tuwA,18555
52
+ azure/storage/blob/_shared/base_client.py,sha256=JJ0xwN2GGzfwB2a37TEyotxcL1WL-rvalAc1RnPWl9A,18628
54
53
  azure/storage/blob/_shared/base_client_async.py,sha256=z1dyRk2XSurRn69CwKA_lQYC7kRFOMkwhIr-InPP5t8,11918
55
54
  azure/storage/blob/_shared/constants.py,sha256=0TnhBNEaZpVq0vECmLoXWSzCajtn9WOlfOfzbMApRb4,620
56
55
  azure/storage/blob/_shared/models.py,sha256=aDydzgBj2_-WfnlT1-nOhJt-FHxOU8BG0T0K68BejNk,24907
57
56
  azure/storage/blob/_shared/parser.py,sha256=ACpdtwf6lhzmA0ukT3PmxpGVpimVXTy_mMSdixC55R8,1955
58
- azure/storage/blob/_shared/policies.py,sha256=XuoVxFgyXd2-6h-rniGlvUU4-y0SpsjMdwTdVTRSBjw,30899
59
- azure/storage/blob/_shared/policies_async.py,sha256=aVLOV8mugAI7K2rWaaBbUkXd_UCfsw9DH08gZtfLp2A,12713
57
+ azure/storage/blob/_shared/policies.py,sha256=ZesS3wGCCtDvUGXmNUKGiRlRA9wKYFeYdiElhKpHJo0,30917
58
+ azure/storage/blob/_shared/policies_async.py,sha256=Fo0zEvHU5rBSSuD55mmYroD0XCSXEx3L6STcr27kMnE,13497
60
59
  azure/storage/blob/_shared/request_handlers.py,sha256=0G9eyzMY_8BlLfHA6jbJF75ENcu3xqZ33bHfSRi9HTM,9755
61
60
  azure/storage/blob/_shared/response_handlers.py,sha256=wqZ1hGRDTwh3GkRB0gPSjgm_7TP2quZc_ex4pYThW-8,9190
62
61
  azure/storage/blob/_shared/shared_access_signature.py,sha256=ov8h9CStKwlfZBtlj54jeckpzuVjYcYvJNKgxQByZ9o,11130
@@ -71,15 +70,15 @@ azure/storage/blob/_shared/avro/schema.py,sha256=Z9qcHIEBDbXxkBBs_HYbEWHlZtAbvT3
71
70
  azure/storage/blob/aio/__init__.py,sha256=tVgeGWdfxG32uyJxAE32waysCOGt93S_EeuQLJz7vEM,8344
72
71
  azure/storage/blob/aio/_blob_client_async.py,sha256=wx76W_LrUDQ2vqBbgvS3RzKLtrnXbAe2INRtLXjtu2g,180606
73
72
  azure/storage/blob/aio/_blob_service_client_async.py,sha256=3rOewtzrDmjUoYKwM0EBUwYLizqp3KHx71lEHDcN_Yw,41260
74
- azure/storage/blob/aio/_container_client_async.py,sha256=uYmMm_jWvui5EXDXeRSkbhYnVLcukiOpt_657w2cwzc,85147
75
- azure/storage/blob/aio/_download_async.py,sha256=QnOf6nZRAcYDvziAOXKSmZ9qAIs106mPTWqMks-RefA,36848
73
+ azure/storage/blob/aio/_container_client_async.py,sha256=sJaxiKPqA2iDBaDQtI4dnPIrsdmLc_aqSXPk_AJ4QME,85402
74
+ azure/storage/blob/aio/_download_async.py,sha256=zneaszZj69VblrUkWocBjvhvXedQJfJweL4uTqc9TpE,38078
76
75
  azure/storage/blob/aio/_encryption_async.py,sha256=spbWeycNMj38H5ynZ03FRtRu0L0tnl1lQn5UJT6HMAY,2518
77
76
  azure/storage/blob/aio/_lease_async.py,sha256=dy4_KZYuIhlxEvYO4GLTKdZz4UzFkpxcm7zfino6geE,18638
78
77
  azure/storage/blob/aio/_list_blobs_helper.py,sha256=cbrJcaGVfOvVCcLYd5dGx-jV3JjSvKfDIi2AQjf79qs,9920
79
78
  azure/storage/blob/aio/_models.py,sha256=fdv7OQc6utrGBIS8FSNuBhYK5Q65o1TbKvdeeQaeUOc,8143
80
79
  azure/storage/blob/aio/_upload_helpers.py,sha256=zROsVN6PK2Cn59Ysq08Ide5T1IGG2yH7oK9ZCn5uQXs,14038
81
- azure_storage_blob-12.23.0b1.dist-info/LICENSE,sha256=_VMkgdgo4ToLE8y1mOAjOKNhd0BnWoYu5r3BVBto6T0,1073
82
- azure_storage_blob-12.23.0b1.dist-info/METADATA,sha256=tgrAaS4hQ05OXzOYADD8yCJ-DyR-vgnLBkoszoX2z8c,26241
83
- azure_storage_blob-12.23.0b1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
84
- azure_storage_blob-12.23.0b1.dist-info/top_level.txt,sha256=S7DhWV9m80TBzAhOFjxDUiNbKszzoThbnrSz5MpbHSQ,6
85
- azure_storage_blob-12.23.0b1.dist-info/RECORD,,
80
+ azure_storage_blob-12.23.1.dist-info/LICENSE,sha256=_VMkgdgo4ToLE8y1mOAjOKNhd0BnWoYu5r3BVBto6T0,1073
81
+ azure_storage_blob-12.23.1.dist-info/METADATA,sha256=MsP96yVjSewFFWu2tOQgUL0AiNG1-MlHxML8LCReiyY,26252
82
+ azure_storage_blob-12.23.1.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
83
+ azure_storage_blob-12.23.1.dist-info/top_level.txt,sha256=S7DhWV9m80TBzAhOFjxDUiNbKszzoThbnrSz5MpbHSQ,6
84
+ azure_storage_blob-12.23.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (72.2.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5