azure-storage-blob 12.25.1__py3-none-any.whl → 12.26.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- azure/storage/blob/__init__.py +3 -2
- azure/storage/blob/_blob_client.py +94 -41
- azure/storage/blob/_blob_client_helpers.py +19 -4
- azure/storage/blob/_blob_service_client.py +16 -13
- azure/storage/blob/_container_client.py +25 -22
- azure/storage/blob/_deserialize.py +1 -1
- azure/storage/blob/_download.py +7 -7
- azure/storage/blob/_encryption.py +177 -184
- azure/storage/blob/_generated/_azure_blob_storage.py +1 -1
- azure/storage/blob/_generated/_configuration.py +2 -2
- azure/storage/blob/_generated/_serialization.py +3 -3
- azure/storage/blob/_generated/aio/_azure_blob_storage.py +1 -1
- azure/storage/blob/_generated/aio/_configuration.py +2 -2
- azure/storage/blob/_generated/aio/operations/_append_blob_operations.py +5 -4
- azure/storage/blob/_generated/aio/operations/_blob_operations.py +5 -25
- azure/storage/blob/_generated/aio/operations/_block_blob_operations.py +9 -7
- azure/storage/blob/_generated/aio/operations/_container_operations.py +1 -19
- azure/storage/blob/_generated/aio/operations/_page_blob_operations.py +5 -10
- azure/storage/blob/_generated/aio/operations/_service_operations.py +1 -8
- azure/storage/blob/_generated/models/__init__.py +2 -0
- azure/storage/blob/_generated/models/_azure_blob_storage_enums.py +6 -0
- azure/storage/blob/_generated/operations/_append_blob_operations.py +12 -9
- azure/storage/blob/_generated/operations/_blob_operations.py +32 -49
- azure/storage/blob/_generated/operations/_block_blob_operations.py +21 -13
- azure/storage/blob/_generated/operations/_container_operations.py +19 -37
- azure/storage/blob/_generated/operations/_page_blob_operations.py +17 -19
- azure/storage/blob/_generated/operations/_service_operations.py +9 -17
- azure/storage/blob/_lease.py +1 -0
- azure/storage/blob/_quick_query_helper.py +20 -24
- azure/storage/blob/_serialize.py +1 -0
- azure/storage/blob/_shared/__init__.py +7 -7
- azure/storage/blob/_shared/authentication.py +49 -32
- azure/storage/blob/_shared/avro/avro_io.py +44 -42
- azure/storage/blob/_shared/avro/avro_io_async.py +42 -41
- azure/storage/blob/_shared/avro/datafile.py +24 -21
- azure/storage/blob/_shared/avro/datafile_async.py +15 -15
- azure/storage/blob/_shared/avro/schema.py +196 -217
- azure/storage/blob/_shared/base_client.py +82 -59
- azure/storage/blob/_shared/base_client_async.py +58 -51
- azure/storage/blob/_shared/constants.py +1 -1
- azure/storage/blob/_shared/models.py +93 -92
- azure/storage/blob/_shared/parser.py +3 -3
- azure/storage/blob/_shared/policies.py +176 -145
- azure/storage/blob/_shared/policies_async.py +58 -69
- azure/storage/blob/_shared/request_handlers.py +50 -45
- azure/storage/blob/_shared/response_handlers.py +49 -45
- azure/storage/blob/_shared/shared_access_signature.py +67 -71
- azure/storage/blob/_shared/uploads.py +56 -49
- azure/storage/blob/_shared/uploads_async.py +70 -58
- azure/storage/blob/_shared_access_signature.py +3 -1
- azure/storage/blob/_version.py +1 -1
- azure/storage/blob/aio/__init__.py +3 -2
- azure/storage/blob/aio/_blob_client_async.py +241 -44
- azure/storage/blob/aio/_blob_service_client_async.py +13 -11
- azure/storage/blob/aio/_container_client_async.py +28 -25
- azure/storage/blob/aio/_download_async.py +15 -11
- azure/storage/blob/aio/_lease_async.py +1 -0
- azure/storage/blob/aio/_quick_query_helper_async.py +194 -0
- {azure_storage_blob-12.25.1.dist-info → azure_storage_blob-12.26.0.dist-info}/METADATA +1 -1
- azure_storage_blob-12.26.0.dist-info/RECORD +85 -0
- azure_storage_blob-12.25.1.dist-info/RECORD +0 -84
- {azure_storage_blob-12.25.1.dist-info → azure_storage_blob-12.26.0.dist-info}/LICENSE +0 -0
- {azure_storage_blob-12.25.1.dist-info → azure_storage_blob-12.26.0.dist-info}/WHEEL +0 -0
- {azure_storage_blob-12.25.1.dist-info → azure_storage_blob-12.26.0.dist-info}/top_level.txt +0 -0
@@ -12,7 +12,7 @@ from itertools import islice
|
|
12
12
|
from math import ceil
|
13
13
|
from typing import AsyncGenerator, Union
|
14
14
|
|
15
|
-
from .import encode_base64, url_quote
|
15
|
+
from . import encode_base64, url_quote
|
16
16
|
from .request_handlers import get_length
|
17
17
|
from .response_handlers import return_response_headers
|
18
18
|
from .uploads import SubStream, IterStreamer # pylint: disable=unused-import
|
@@ -59,19 +59,20 @@ async def _parallel_uploads(uploader, pending, running):
|
|
59
59
|
|
60
60
|
|
61
61
|
async def upload_data_chunks(
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
62
|
+
service=None,
|
63
|
+
uploader_class=None,
|
64
|
+
total_size=None,
|
65
|
+
chunk_size=None,
|
66
|
+
max_concurrency=None,
|
67
|
+
stream=None,
|
68
|
+
progress_hook=None,
|
69
|
+
**kwargs,
|
70
|
+
):
|
70
71
|
|
71
72
|
parallel = max_concurrency > 1
|
72
|
-
if parallel and
|
73
|
+
if parallel and "modified_access_conditions" in kwargs:
|
73
74
|
# Access conditions do not work with parallelism
|
74
|
-
kwargs[
|
75
|
+
kwargs["modified_access_conditions"] = None
|
75
76
|
|
76
77
|
uploader = uploader_class(
|
77
78
|
service=service,
|
@@ -80,7 +81,8 @@ async def upload_data_chunks(
|
|
80
81
|
stream=stream,
|
81
82
|
parallel=parallel,
|
82
83
|
progress_hook=progress_hook,
|
83
|
-
**kwargs
|
84
|
+
**kwargs,
|
85
|
+
)
|
84
86
|
|
85
87
|
if parallel:
|
86
88
|
upload_tasks = uploader.get_chunk_streams()
|
@@ -104,18 +106,19 @@ async def upload_data_chunks(
|
|
104
106
|
|
105
107
|
|
106
108
|
async def upload_substream_blocks(
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
109
|
+
service=None,
|
110
|
+
uploader_class=None,
|
111
|
+
total_size=None,
|
112
|
+
chunk_size=None,
|
113
|
+
max_concurrency=None,
|
114
|
+
stream=None,
|
115
|
+
progress_hook=None,
|
116
|
+
**kwargs,
|
117
|
+
):
|
115
118
|
parallel = max_concurrency > 1
|
116
|
-
if parallel and
|
119
|
+
if parallel and "modified_access_conditions" in kwargs:
|
117
120
|
# Access conditions do not work with parallelism
|
118
|
-
kwargs[
|
121
|
+
kwargs["modified_access_conditions"] = None
|
119
122
|
uploader = uploader_class(
|
120
123
|
service=service,
|
121
124
|
total_size=total_size,
|
@@ -123,13 +126,13 @@ async def upload_substream_blocks(
|
|
123
126
|
stream=stream,
|
124
127
|
parallel=parallel,
|
125
128
|
progress_hook=progress_hook,
|
126
|
-
**kwargs
|
129
|
+
**kwargs,
|
130
|
+
)
|
127
131
|
|
128
132
|
if parallel:
|
129
133
|
upload_tasks = uploader.get_substream_blocks()
|
130
134
|
running_futures = [
|
131
|
-
asyncio.ensure_future(uploader.process_substream_block(u))
|
132
|
-
for u in islice(upload_tasks, 0, max_concurrency)
|
135
|
+
asyncio.ensure_future(uploader.process_substream_block(u)) for u in islice(upload_tasks, 0, max_concurrency)
|
133
136
|
]
|
134
137
|
range_ids = await _parallel_uploads(uploader.process_substream_block, upload_tasks, running_futures)
|
135
138
|
else:
|
@@ -144,15 +147,17 @@ async def upload_substream_blocks(
|
|
144
147
|
class _ChunkUploader(object): # pylint: disable=too-many-instance-attributes
|
145
148
|
|
146
149
|
def __init__(
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
150
|
+
self,
|
151
|
+
service,
|
152
|
+
total_size,
|
153
|
+
chunk_size,
|
154
|
+
stream,
|
155
|
+
parallel,
|
156
|
+
encryptor=None,
|
157
|
+
padder=None,
|
158
|
+
progress_hook=None,
|
159
|
+
**kwargs,
|
160
|
+
):
|
156
161
|
self.service = service
|
157
162
|
self.total_size = total_size
|
158
163
|
self.chunk_size = chunk_size
|
@@ -178,7 +183,7 @@ class _ChunkUploader(object): # pylint: disable=too-many-instance-attributes
|
|
178
183
|
async def get_chunk_streams(self):
|
179
184
|
index = 0
|
180
185
|
while True:
|
181
|
-
data = b
|
186
|
+
data = b""
|
182
187
|
read_size = self.chunk_size
|
183
188
|
|
184
189
|
# Buffer until we either reach the end of the stream or get a whole chunk.
|
@@ -189,12 +194,12 @@ class _ChunkUploader(object): # pylint: disable=too-many-instance-attributes
|
|
189
194
|
if inspect.isawaitable(temp):
|
190
195
|
temp = await temp
|
191
196
|
if not isinstance(temp, bytes):
|
192
|
-
raise TypeError(
|
197
|
+
raise TypeError("Blob data should be of type bytes.")
|
193
198
|
data += temp or b""
|
194
199
|
|
195
200
|
# We have read an empty string and so are at the end
|
196
201
|
# of the buffer or we have read a full chunk.
|
197
|
-
if temp == b
|
202
|
+
if temp == b"" or len(data) == self.chunk_size:
|
198
203
|
break
|
199
204
|
|
200
205
|
if len(data) == self.chunk_size:
|
@@ -273,13 +278,13 @@ class _ChunkUploader(object): # pylint: disable=too-many-instance-attributes
|
|
273
278
|
class BlockBlobChunkUploader(_ChunkUploader):
|
274
279
|
|
275
280
|
def __init__(self, *args, **kwargs):
|
276
|
-
kwargs.pop(
|
281
|
+
kwargs.pop("modified_access_conditions", None)
|
277
282
|
super(BlockBlobChunkUploader, self).__init__(*args, **kwargs)
|
278
283
|
self.current_length = None
|
279
284
|
|
280
285
|
async def _upload_chunk(self, chunk_offset, chunk_data):
|
281
286
|
# TODO: This is incorrect, but works with recording.
|
282
|
-
index = f
|
287
|
+
index = f"{chunk_offset:032d}"
|
283
288
|
block_id = encode_base64(url_quote(encode_base64(index)))
|
284
289
|
await self.service.stage_block(
|
285
290
|
block_id,
|
@@ -287,19 +292,21 @@ class BlockBlobChunkUploader(_ChunkUploader):
|
|
287
292
|
body=chunk_data,
|
288
293
|
data_stream_total=self.total_size,
|
289
294
|
upload_stream_current=self.progress_total,
|
290
|
-
**self.request_options
|
295
|
+
**self.request_options,
|
296
|
+
)
|
291
297
|
return index, block_id
|
292
298
|
|
293
299
|
async def _upload_substream_block(self, index, block_stream):
|
294
300
|
try:
|
295
|
-
block_id = f
|
301
|
+
block_id = f"BlockId{(index//self.chunk_size):05}"
|
296
302
|
await self.service.stage_block(
|
297
303
|
block_id,
|
298
304
|
len(block_stream),
|
299
305
|
block_stream,
|
300
306
|
data_stream_total=self.total_size,
|
301
307
|
upload_stream_current=self.progress_total,
|
302
|
-
**self.request_options
|
308
|
+
**self.request_options,
|
309
|
+
)
|
303
310
|
finally:
|
304
311
|
block_stream.close()
|
305
312
|
return block_id
|
@@ -311,7 +318,7 @@ class PageBlobChunkUploader(_ChunkUploader):
|
|
311
318
|
# read until non-zero byte is encountered
|
312
319
|
# if reached the end without returning, then chunk_data is all 0's
|
313
320
|
for each_byte in chunk_data:
|
314
|
-
if each_byte not in [0, b
|
321
|
+
if each_byte not in [0, b"\x00"]:
|
315
322
|
return False
|
316
323
|
return True
|
317
324
|
|
@@ -319,7 +326,7 @@ class PageBlobChunkUploader(_ChunkUploader):
|
|
319
326
|
# avoid uploading the empty pages
|
320
327
|
if not self._is_chunk_empty(chunk_data):
|
321
328
|
chunk_end = chunk_offset + len(chunk_data) - 1
|
322
|
-
content_range = f
|
329
|
+
content_range = f"bytes={chunk_offset}-{chunk_end}"
|
323
330
|
computed_md5 = None
|
324
331
|
self.response_headers = await self.service.upload_pages(
|
325
332
|
body=chunk_data,
|
@@ -329,10 +336,11 @@ class PageBlobChunkUploader(_ChunkUploader):
|
|
329
336
|
cls=return_response_headers,
|
330
337
|
data_stream_total=self.total_size,
|
331
338
|
upload_stream_current=self.progress_total,
|
332
|
-
**self.request_options
|
339
|
+
**self.request_options,
|
340
|
+
)
|
333
341
|
|
334
|
-
if not self.parallel and self.request_options.get(
|
335
|
-
self.request_options[
|
342
|
+
if not self.parallel and self.request_options.get("modified_access_conditions"):
|
343
|
+
self.request_options["modified_access_conditions"].if_match = self.response_headers["etag"]
|
336
344
|
|
337
345
|
async def _upload_substream_block(self, index, block_stream):
|
338
346
|
pass
|
@@ -352,18 +360,21 @@ class AppendBlobChunkUploader(_ChunkUploader):
|
|
352
360
|
cls=return_response_headers,
|
353
361
|
data_stream_total=self.total_size,
|
354
362
|
upload_stream_current=self.progress_total,
|
355
|
-
**self.request_options
|
356
|
-
|
363
|
+
**self.request_options,
|
364
|
+
)
|
365
|
+
self.current_length = int(self.response_headers["blob_append_offset"])
|
357
366
|
else:
|
358
|
-
self.request_options[
|
367
|
+
self.request_options["append_position_access_conditions"].append_position = (
|
359
368
|
self.current_length + chunk_offset
|
369
|
+
)
|
360
370
|
self.response_headers = await self.service.append_block(
|
361
371
|
body=chunk_data,
|
362
372
|
content_length=len(chunk_data),
|
363
373
|
cls=return_response_headers,
|
364
374
|
data_stream_total=self.total_size,
|
365
375
|
upload_stream_current=self.progress_total,
|
366
|
-
**self.request_options
|
376
|
+
**self.request_options,
|
377
|
+
)
|
367
378
|
|
368
379
|
async def _upload_substream_block(self, index, block_stream):
|
369
380
|
pass
|
@@ -379,11 +390,11 @@ class DataLakeFileChunkUploader(_ChunkUploader):
|
|
379
390
|
cls=return_response_headers,
|
380
391
|
data_stream_total=self.total_size,
|
381
392
|
upload_stream_current=self.progress_total,
|
382
|
-
**self.request_options
|
393
|
+
**self.request_options,
|
383
394
|
)
|
384
395
|
|
385
|
-
if not self.parallel and self.request_options.get(
|
386
|
-
self.request_options[
|
396
|
+
if not self.parallel and self.request_options.get("modified_access_conditions"):
|
397
|
+
self.request_options["modified_access_conditions"].if_match = self.response_headers["etag"]
|
387
398
|
|
388
399
|
async def _upload_substream_block(self, index, block_stream):
|
389
400
|
try:
|
@@ -394,7 +405,7 @@ class DataLakeFileChunkUploader(_ChunkUploader):
|
|
394
405
|
cls=return_response_headers,
|
395
406
|
data_stream_total=self.total_size,
|
396
407
|
upload_stream_current=self.progress_total,
|
397
|
-
**self.request_options
|
408
|
+
**self.request_options,
|
398
409
|
)
|
399
410
|
finally:
|
400
411
|
block_stream.close()
|
@@ -411,9 +422,9 @@ class FileChunkUploader(_ChunkUploader):
|
|
411
422
|
length,
|
412
423
|
data_stream_total=self.total_size,
|
413
424
|
upload_stream_current=self.progress_total,
|
414
|
-
**self.request_options
|
425
|
+
**self.request_options,
|
415
426
|
)
|
416
|
-
range_id = f
|
427
|
+
range_id = f"bytes={chunk_offset}-{chunk_end}"
|
417
428
|
return range_id, response
|
418
429
|
|
419
430
|
# TODO: Implement this method.
|
@@ -421,10 +432,11 @@ class FileChunkUploader(_ChunkUploader):
|
|
421
432
|
pass
|
422
433
|
|
423
434
|
|
424
|
-
class AsyncIterStreamer
|
435
|
+
class AsyncIterStreamer:
|
425
436
|
"""
|
426
437
|
File-like streaming object for AsyncGenerators.
|
427
438
|
"""
|
439
|
+
|
428
440
|
def __init__(self, generator: AsyncGenerator[Union[bytes, str], None], encoding: str = "UTF-8"):
|
429
441
|
self.iterator = generator.__aiter__()
|
430
442
|
self.leftover = b""
|
@@ -311,7 +311,9 @@ class _BlobSharedAccessHelper(_SharedAccessHelper):
|
|
311
311
|
self.get_value_to_append(QueryStringConstants.SIGNED_KEY_VERSION) +
|
312
312
|
self.get_value_to_append(QueryStringConstants.SIGNED_AUTHORIZED_OID) +
|
313
313
|
self.get_value_to_append(QueryStringConstants.SIGNED_UNAUTHORIZED_OID) +
|
314
|
-
self.get_value_to_append(QueryStringConstants.SIGNED_CORRELATION_ID)
|
314
|
+
self.get_value_to_append(QueryStringConstants.SIGNED_CORRELATION_ID) +
|
315
|
+
self.get_value_to_append(QueryStringConstants.SIGNED_KEY_DELEGATED_USER_TID) +
|
316
|
+
self.get_value_to_append(QueryStringConstants.SIGNED_DELEGATED_USER_OID))
|
315
317
|
else:
|
316
318
|
string_to_sign += self.get_value_to_append(QueryStringConstants.SIGNED_IDENTIFIER)
|
317
319
|
|
azure/storage/blob/_version.py
CHANGED
@@ -73,7 +73,7 @@ async def upload_blob_to_url(
|
|
73
73
|
entire blocks, and doing so defeats the purpose of the memory-efficient algorithm.
|
74
74
|
:keyword str encoding:
|
75
75
|
Encoding to use if text is supplied as input. Defaults to UTF-8.
|
76
|
-
:
|
76
|
+
:return: Blob-updated property dict (Etag and last modified)
|
77
77
|
:rtype: dict[str, Any]
|
78
78
|
"""
|
79
79
|
async with BlobClient.from_blob_url(blob_url, credential=credential) as client:
|
@@ -102,7 +102,7 @@ async def download_blob_from_url(
|
|
102
102
|
:param output:
|
103
103
|
Where the data should be downloaded to. This could be either a file path to write to,
|
104
104
|
or an open IO handle to write to.
|
105
|
-
:type output: str or
|
105
|
+
:type output: str or IO
|
106
106
|
:param credential:
|
107
107
|
The credentials with which to authenticate. This is optional if the
|
108
108
|
blob URL already has a SAS token or the blob is public. The value can be a SAS token string,
|
@@ -139,6 +139,7 @@ async def download_blob_from_url(
|
|
139
139
|
blob. Also note that if enabled, the memory-efficient upload algorithm
|
140
140
|
will not be used, because computing the MD5 hash requires buffering
|
141
141
|
entire blocks, and doing so defeats the purpose of the memory-efficient algorithm.
|
142
|
+
:return: None
|
142
143
|
:rtype: None
|
143
144
|
"""
|
144
145
|
overwrite = kwargs.pop('overwrite', False)
|