Skip to content

Commit 0a79e55

Browse files
authored
Merge pull request #318 from jofriedm-msft/dev
Return etag and lmt for create_from_*
2 parents d6fb054 + f014c98 commit 0a79e55

File tree

8 files changed

+260
-127
lines changed

8 files changed

+260
-127
lines changed

ChangeLog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
> See [BreakingChanges](BreakingChanges.md) for a detailed list of API breaks.
44
5+
## Version X.X.X:
6+
7+
### Blob:
8+
- create_from_* and and append_blob_from_* methods will return response_properties which contains the etag and last modified time.
9+
510
## Version 0.34.3:
611
- All: Made the socket timeout configurable. Increased the default socket timeout to 20 seconds.
712
- All: Fixed a bug where SAS tokens were being duplicated on retries

azure/storage/blob/_upload_chunking.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import sys
1616
from threading import Lock
1717
from time import sleep
18+
1819
from cryptography.hazmat.primitives.padding import PKCS7
1920
from .._common_conversion import _encode_base64
2021
from .._serialization import (
@@ -37,7 +38,7 @@ def _upload_blob_chunks(blob_service, container_name, blob_name,
3738
blob_size, block_size, stream, max_connections,
3839
progress_callback, validate_content, lease_id, uploader_class,
3940
maxsize_condition=None, if_match=None, timeout=None,
40-
content_encryption_key=None, initialization_vector=None):
41+
content_encryption_key=None, initialization_vector=None, resource_properties=None):
4142

4243
encryptor, padder = _get_blob_encryptor_and_padder(content_encryption_key, initialization_vector,
4344
uploader_class is not _PageBlobChunkUploader)
@@ -104,6 +105,10 @@ def _upload_blob_chunks(blob_service, container_name, blob_name,
104105
else:
105106
range_ids = [uploader.process_chunk(result) for result in uploader.get_chunk_streams()]
106107

108+
if resource_properties:
109+
resource_properties.last_modified = uploader.last_modified
110+
resource_properties.etag = uploader.etag
111+
107112
return range_ids
108113

109114
def _upload_blob_substream_blocks(blob_service, container_name, blob_name,
@@ -249,6 +254,10 @@ def _upload_substream_block_with_progress(self, block_id, block_stream):
249254
self._update_progress(len(block_stream))
250255
return range_id
251256

257+
def set_response_properties(self, resp):
258+
self.etag = resp.etag
259+
self.last_modified = resp.last_modified
260+
252261
class _BlockBlobChunkUploader(_BlobChunkUploader):
253262
def _upload_chunk(self, chunk_offset, chunk_data):
254263
block_id = url_quote(_encode_base64('{0:032d}'.format(chunk_offset)))
@@ -297,6 +306,8 @@ def _upload_chunk(self, chunk_start, chunk_data):
297306
if not self.parallel:
298307
self.if_match = resp.etag
299308

309+
self.set_response_properties(resp)
310+
300311
class _AppendBlobChunkUploader(_BlobChunkUploader):
301312
def _upload_chunk(self, chunk_offset, chunk_data):
302313
if not hasattr(self, 'current_length'):
@@ -323,6 +334,8 @@ def _upload_chunk(self, chunk_offset, chunk_data):
323334
timeout=self.timeout,
324335
)
325336

337+
self.set_response_properties(resp)
338+
326339
class _SubStream(IOBase):
327340
def __init__(self, wrapped_stream, stream_begin_index, length, lockObj):
328341
# Python 2.7: file-like objects created with open() typically support seek(), but are not

azure/storage/blob/appendblobservice.py

Lines changed: 52 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@
3333
_AppendBlobChunkUploader,
3434
_upload_blob_chunks,
3535
)
36-
from .models import _BlobTypes
36+
from .models import (
37+
_BlobTypes,
38+
ResourceProperties
39+
)
3740
from .._constants import (
3841
SERVICE_HOST_BASE,
3942
DEFAULT_PROTOCOL,
@@ -325,6 +328,8 @@ def append_blob_from_path(
325328
The timeout parameter is expressed in seconds. This method may make
326329
multiple calls to the Azure service and the timeout will apply to
327330
each call individually.
331+
:return: ETag and last modified properties for the Append Blob
332+
:rtype: :class:`~azure.storage.blob.models.ResourceProperties`
328333
'''
329334
_validate_not_none('container_name', container_name)
330335
_validate_not_none('blob_name', blob_name)
@@ -333,16 +338,16 @@ def append_blob_from_path(
333338

334339
count = path.getsize(file_path)
335340
with open(file_path, 'rb') as stream:
336-
self.append_blob_from_stream(
337-
container_name,
338-
blob_name,
339-
stream,
340-
count=count,
341-
validate_content=validate_content,
342-
maxsize_condition=maxsize_condition,
343-
progress_callback=progress_callback,
344-
lease_id=lease_id,
345-
timeout=timeout)
341+
return self.append_blob_from_stream(
342+
container_name,
343+
blob_name,
344+
stream,
345+
count=count,
346+
validate_content=validate_content,
347+
maxsize_condition=maxsize_condition,
348+
progress_callback=progress_callback,
349+
lease_id=lease_id,
350+
timeout=timeout)
346351

347352
def append_blob_from_bytes(
348353
self, container_name, blob_name, blob, index=0, count=None,
@@ -387,6 +392,8 @@ def append_blob_from_bytes(
387392
The timeout parameter is expressed in seconds. This method may make
388393
multiple calls to the Azure service and the timeout will apply to
389394
each call individually.
395+
:return: ETag and last modified properties for the Append Blob
396+
:rtype: :class:`~azure.storage.blob.models.ResourceProperties`
390397
'''
391398
_validate_not_none('container_name', container_name)
392399
_validate_not_none('blob_name', blob_name)
@@ -404,16 +411,16 @@ def append_blob_from_bytes(
404411
stream = BytesIO(blob)
405412
stream.seek(index)
406413

407-
self.append_blob_from_stream(
408-
container_name,
409-
blob_name,
410-
stream,
411-
count=count,
412-
validate_content=validate_content,
413-
maxsize_condition=maxsize_condition,
414-
lease_id=lease_id,
415-
progress_callback=progress_callback,
416-
timeout=timeout)
414+
return self.append_blob_from_stream(
415+
container_name,
416+
blob_name,
417+
stream,
418+
count=count,
419+
validate_content=validate_content,
420+
maxsize_condition=maxsize_condition,
421+
lease_id=lease_id,
422+
progress_callback=progress_callback,
423+
timeout=timeout)
417424

418425
def append_blob_from_text(
419426
self, container_name, blob_name, text, encoding='utf-8',
@@ -455,6 +462,8 @@ def append_blob_from_text(
455462
The timeout parameter is expressed in seconds. This method may make
456463
multiple calls to the Azure service and the timeout will apply to
457464
each call individually.
465+
:return: ETag and last modified properties for the Append Blob
466+
:rtype: :class:`~azure.storage.blob.models.ResourceProperties`
458467
'''
459468
_validate_not_none('container_name', container_name)
460469
_validate_not_none('blob_name', blob_name)
@@ -465,17 +474,17 @@ def append_blob_from_text(
465474
_validate_not_none('encoding', encoding)
466475
text = text.encode(encoding)
467476

468-
self.append_blob_from_bytes(
469-
container_name,
470-
blob_name,
471-
text,
472-
index=0,
473-
count=len(text),
474-
validate_content=validate_content,
475-
maxsize_condition=maxsize_condition,
476-
lease_id=lease_id,
477-
progress_callback=progress_callback,
478-
timeout=timeout)
477+
return self.append_blob_from_bytes(
478+
container_name,
479+
blob_name,
480+
text,
481+
index=0,
482+
count=len(text),
483+
validate_content=validate_content,
484+
maxsize_condition=maxsize_condition,
485+
lease_id=lease_id,
486+
progress_callback=progress_callback,
487+
timeout=timeout)
479488

480489
def append_blob_from_stream(
481490
self, container_name, blob_name, stream, count=None,
@@ -518,12 +527,18 @@ def append_blob_from_stream(
518527
The timeout parameter is expressed in seconds. This method may make
519528
multiple calls to the Azure service and the timeout will apply to
520529
each call individually.
530+
:return: ETag and last modified properties for the Append Blob
531+
:rtype: :class:`~azure.storage.blob.models.ResourceProperties`
521532
'''
522533
_validate_not_none('container_name', container_name)
523534
_validate_not_none('blob_name', blob_name)
524535
_validate_not_none('stream', stream)
525536
_validate_encryption_unsupported(self.require_encryption, self.key_encryption_key)
526537

538+
# _upload_blob_chunks returns the block ids for block blobs so resource_properties
539+
# is passed as a parameter to get the last_modified and etag for page and append blobs.
540+
# this info is not needed for block_blobs since _put_block_list is called after which gets this info
541+
resource_properties = ResourceProperties()
527542
_upload_blob_chunks(
528543
blob_service=self,
529544
container_name=container_name,
@@ -537,5 +552,8 @@ def append_blob_from_stream(
537552
lease_id=lease_id,
538553
uploader_class=_AppendBlobChunkUploader,
539554
maxsize_condition=maxsize_condition,
540-
timeout=timeout
541-
)
555+
timeout=timeout,
556+
resource_properties=resource_properties
557+
)
558+
559+
return resource_properties

azure/storage/blob/blockblobservice.py

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -384,29 +384,31 @@ def create_blob_from_path(
384384
The timeout parameter is expressed in seconds. This method may make
385385
multiple calls to the Azure service and the timeout will apply to
386386
each call individually.
387+
:return: ETag and last modified properties for the Block Blob
388+
:rtype: :class:`~azure.storage.blob.models.ResourceProperties`
387389
'''
388390
_validate_not_none('container_name', container_name)
389391
_validate_not_none('blob_name', blob_name)
390392
_validate_not_none('file_path', file_path)
391393

392394
count = path.getsize(file_path)
393395
with open(file_path, 'rb') as stream:
394-
self.create_blob_from_stream(
395-
container_name=container_name,
396-
blob_name=blob_name,
397-
stream=stream,
398-
count=count,
399-
content_settings=content_settings,
400-
metadata=metadata,
401-
validate_content=validate_content,
402-
lease_id=lease_id,
403-
progress_callback=progress_callback,
404-
max_connections=max_connections,
405-
if_modified_since=if_modified_since,
406-
if_unmodified_since=if_unmodified_since,
407-
if_match=if_match,
408-
if_none_match=if_none_match,
409-
timeout=timeout)
396+
return self.create_blob_from_stream(
397+
container_name=container_name,
398+
blob_name=blob_name,
399+
stream=stream,
400+
count=count,
401+
content_settings=content_settings,
402+
metadata=metadata,
403+
validate_content=validate_content,
404+
lease_id=lease_id,
405+
progress_callback=progress_callback,
406+
max_connections=max_connections,
407+
if_modified_since=if_modified_since,
408+
if_unmodified_since=if_unmodified_since,
409+
if_match=if_match,
410+
if_none_match=if_none_match,
411+
timeout=timeout)
410412

411413
def create_blob_from_stream(
412414
self, container_name, blob_name, stream, count=None,
@@ -492,6 +494,8 @@ def create_blob_from_stream(
492494
with your input stream.
493495
The SubStream class will attempt to buffer up to 4 MB internally to reduce the amount of
494496
seek and read calls to the underlying stream. This is particularly beneficial when uploading larger blocks.
497+
:return: ETag and last modified properties for the Block Blob
498+
:rtype: :class:`~azure.storage.blob.models.ResourceProperties`
495499
'''
496500
_validate_not_none('container_name', container_name)
497501
_validate_not_none('blob_name', blob_name)
@@ -508,7 +512,7 @@ def create_blob_from_stream(
508512
progress_callback(0, count)
509513

510514
data = stream.read(count)
511-
self._put_blob(
515+
resp = self._put_blob(
512516
container_name=container_name,
513517
blob_name=blob_name,
514518
blob=data,
@@ -524,6 +528,8 @@ def create_blob_from_stream(
524528

525529
if progress_callback:
526530
progress_callback(count, count)
531+
532+
return resp
527533
else:
528534
cek, iv, encryption_data = None, None, None
529535

@@ -568,7 +574,7 @@ def create_blob_from_stream(
568574
timeout=timeout,
569575
)
570576

571-
self._put_block_list(
577+
return self._put_block_list(
572578
container_name=container_name,
573579
blob_name=blob_name,
574580
block_list=block_ids,
@@ -653,6 +659,8 @@ def create_blob_from_bytes(
653659
The timeout parameter is expressed in seconds. This method may make
654660
multiple calls to the Azure service and the timeout will apply to
655661
each call individually.
662+
:return: ETag and last modified properties for the Block Blob
663+
:rtype: :class:`~azure.storage.blob.models.ResourceProperties`
656664
'''
657665
_validate_not_none('container_name', container_name)
658666
_validate_not_none('blob_name', blob_name)
@@ -669,7 +677,7 @@ def create_blob_from_bytes(
669677
stream = BytesIO(blob)
670678
stream.seek(index)
671679

672-
self.create_blob_from_stream(
680+
return self.create_blob_from_stream(
673681
container_name=container_name,
674682
blob_name=blob_name,
675683
stream=stream,
@@ -753,6 +761,8 @@ def create_blob_from_text(
753761
The timeout parameter is expressed in seconds. This method may make
754762
multiple calls to the Azure service and the timeout will apply to
755763
each call individually.
764+
:return: ETag and last modified properties for the Block Blob
765+
:rtype: :class:`~azure.storage.blob.models.ResourceProperties`
756766
'''
757767
_validate_not_none('container_name', container_name)
758768
_validate_not_none('blob_name', blob_name)
@@ -762,23 +772,23 @@ def create_blob_from_text(
762772
_validate_not_none('encoding', encoding)
763773
text = text.encode(encoding)
764774

765-
self.create_blob_from_bytes(
766-
container_name=container_name,
767-
blob_name=blob_name,
768-
blob=text,
769-
index=0,
770-
count=len(text),
771-
content_settings=content_settings,
772-
metadata=metadata,
773-
validate_content=validate_content,
774-
lease_id=lease_id,
775-
progress_callback=progress_callback,
776-
max_connections=max_connections,
777-
if_modified_since=if_modified_since,
778-
if_unmodified_since=if_unmodified_since,
779-
if_match=if_match,
780-
if_none_match=if_none_match,
781-
timeout=timeout)
775+
return self.create_blob_from_bytes(
776+
container_name=container_name,
777+
blob_name=blob_name,
778+
blob=text,
779+
index=0,
780+
count=len(text),
781+
content_settings=content_settings,
782+
metadata=metadata,
783+
validate_content=validate_content,
784+
lease_id=lease_id,
785+
progress_callback=progress_callback,
786+
max_connections=max_connections,
787+
if_modified_since=if_modified_since,
788+
if_unmodified_since=if_unmodified_since,
789+
if_match=if_match,
790+
if_none_match=if_none_match,
791+
timeout=timeout)
782792

783793
#-----Helper methods------------------------------------
784794
def _put_blob(self, container_name, blob_name, blob, content_settings=None,

0 commit comments

Comments
 (0)