Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
1b0d881
Decompression for Blob w/o Content Validation
weirongw23-msft Oct 22, 2025
086dca2
Added tests for decompress=False for chunking downloads
weirongw23-msft Oct 30, 2025
d40e39b
Updated docstrings
weirongw23-msft Oct 30, 2025
6c1ab3d
Encryption changes
weirongw23-msft Oct 31, 2025
55d0a57
Merge branch 'main' into weirongw23/decompress
weirongw23-msft Nov 1, 2025
a45d445
File Share + ADLS download decompression test
weirongw23-msft Nov 1, 2025
6eb4830
Typo
weirongw23-msft Nov 1, 2025
6baaee5
Fixed file share async test
weirongw23-msft Nov 3, 2025
6899be1
Fixed common blob tests
weirongw23-msft Nov 3, 2025
e8f49c9
Fixed blob tests
weirongw23-msft Nov 3, 2025
20be727
Blooper
weirongw23-msft Nov 3, 2025
982f3d1
Merge branch 'main' into weirongw23/decompress
weirongw23-msft Nov 3, 2025
1ba69bd
Multi-part download for Datalake
weirongw23-msft Nov 4, 2025
347bdf3
Multi-part download for File Share
weirongw23-msft Nov 4, 2025
0c7824f
Merge branch 'main' into weirongw23/decompress
weirongw23-msft Nov 4, 2025
c2f5634
Fixed decompression implementation for blob client-side encryption
weirongw23-msft Nov 6, 2025
9b6be82
Fixed datalake tests
weirongw23-msft Nov 6, 2025
7abfac8
Fixed File Share tests
weirongw23-msft Nov 6, 2025
5a1de8f
Merge branch 'main' into weirongw23/decompress
weirongw23-msft Nov 13, 2025
3c97843
PR feedback
weirongw23-msft Nov 13, 2025
744beae
Changelogs
weirongw23-msft Nov 14, 2025
fe77e3e
Merge branch 'main' into weirongw23/decompress
weirongw23-msft Nov 14, 2025
b58cddd
Typo
weirongw23-msft Nov 14, 2025
9390763
Merge branch 'main' into weirongw23/decompress
weirongw23-msft Nov 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,8 @@ def download_blob(
function(current: int, total: int) where current is the number of bytes transferred
so far, and total is the total size of the download.
:paramtype progress_hook: Callable[[int, int], None]
:keyword bool decompress: If True, any compressed content, identified by the Content-Type header, will be
decompressed automatically before being returned. Default value is True.
:keyword int timeout:
Sets the server-side timeout for the operation in seconds. For more details see
https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin):
max_concurrency: int = 1,
encoding: str,
progress_hook: Optional[Callable[[int, int], None]] = None,
decompress: Optional[bool] = None,
timeout: Optional[int] = None,
**kwargs: Any
) -> StorageStreamDownloader[str]: ...
Expand All @@ -227,6 +228,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin):
max_concurrency: int = 1,
encoding: None = None,
progress_hook: Optional[Callable[[int, int], None]] = None,
decompress: Optional[bool] = None,
timeout: Optional[int] = None,
**kwargs: Any
) -> StorageStreamDownloader[bytes]: ...
Expand All @@ -248,6 +250,7 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin):
max_concurrency: int = 1,
encoding: Optional[str] = None,
progress_hook: Optional[Callable[[int, int], None]] = None,
decompress: Optional[bool] = None,
timeout: Optional[int] = None,
**kwargs: Any
) -> Union[StorageStreamDownloader[str], StorageStreamDownloader[bytes]]: ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,10 @@ def __len__(self):
def _get_encryption_data_request(self) -> None:
# Save current request cls
download_cls = self._request_options.pop('cls', None)

# Temporarily removing this for the get properties request
decompress = self._request_options.pop('decompress', None)

# Adjust cls for get_properties
self._request_options['cls'] = deserialize_blob_properties

Expand All @@ -434,6 +438,9 @@ def _get_encryption_data_request(self) -> None:
# Restore cls for download
self._request_options['cls'] = download_cls

if decompress is not None:
self._request_options['decompress'] = decompress

@property
def _download_complete(self):
if is_encryption_v2(self._encryption_data):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,8 @@ async def download_blob(
function(current: int, total: int) where current is the number of bytes transferred
so far, and total is the total size of the download.
:paramtype progress_hook: Callable[[int, int], Awaitable[None]]
:keyword bool decompress: If True, any compressed content, identified by the Content-Type header, will be
decompressed automatically before being returned. Default value is True.
:keyword int timeout:
Sets the server-side timeout for the operation in seconds. For more details see
https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ class BlobClient( # type: ignore[misc]
max_concurrency: int = 1,
encoding: str,
progress_hook: Optional[Callable[[int, int], Awaitable[None]]] = None,
decompress: Optional[bool] = None,
timeout: Optional[int] = None,
**kwargs: Any
) -> StorageStreamDownloader[str]: ...
Expand All @@ -229,6 +230,7 @@ class BlobClient( # type: ignore[misc]
max_concurrency: int = 1,
encoding: None = None,
progress_hook: Optional[Callable[[int, int], Awaitable[None]]] = None,
decompress: Optional[bool] = None,
timeout: Optional[int] = None,
**kwargs: Any
) -> StorageStreamDownloader[bytes]: ...
Expand All @@ -250,6 +252,7 @@ class BlobClient( # type: ignore[misc]
max_concurrency: int = 1,
encoding: Optional[str] = None,
progress_hook: Optional[Callable[[int, int], Awaitable[None]]] = None,
decompress: Optional[bool] = None,
timeout: Optional[int] = None,
**kwargs: Any
) -> Union[StorageStreamDownloader[str], StorageStreamDownloader[bytes]]: ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,10 @@ def __len__(self):
async def _get_encryption_data_request(self) -> None:
# Save current request cls
download_cls = self._request_options.pop('cls', None)

# Temporarily removing this for the get properties request
decompress = self._request_options.pop('decompress', None)

# Adjust cls for get_properties
self._request_options['cls'] = deserialize_blob_properties

Expand All @@ -304,6 +308,9 @@ async def _get_encryption_data_request(self) -> None:
# Restore cls for download
self._request_options['cls'] = download_cls

if decompress is not None:
self._request_options['decompress'] = decompress

async def _setup(self) -> None:
if self._encryption_options.get("key") is not None or self._encryption_options.get("resolver") is not None:
await self._get_encryption_data_request()
Expand Down
23 changes: 23 additions & 0 deletions sdk/storage/azure-storage-blob/tests/test_common_blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -3595,4 +3595,27 @@ def test_upload_blob_partial_stream_chunked(self, **kwargs):
result = blob.download_blob().readall()
assert result == data[:length]

@pytest.mark.live_test_only
@BlobPreparer()
def test_download_blob_decompress(self, **kwargs):
storage_account_name = kwargs.pop("storage_account_name")
storage_account_key = kwargs.pop("storage_account_key")

# Arrange
self._setup(storage_account_name, storage_account_key)
blob_name = self._get_blob_reference()
blob = self.bsc.get_blob_client(self.container_name, blob_name)
compressed_data = b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcaH\xcd\xc9\xc9WH+\xca\xcfUH\xaf\xca,\x00\x00\x00\x00\xff\xff\x03\x00d\xaa\x8e\xb5\x0f\x00\x00\x00'
decompressed_data = b"hello from gzip"
content_settings = ContentSettings(content_encoding='gzip')

# Act / Assert
blob.upload_blob(data=compressed_data, content_settings=content_settings)

result = blob.download_blob(decompress=True).readall()
assert result == decompressed_data

result = blob.download_blob(decompress=False).readall()
assert result == compressed_data

# ------------------------------------------------------------------------------
25 changes: 25 additions & 0 deletions sdk/storage/azure-storage-blob/tests/test_common_blob_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -3526,4 +3526,29 @@ async def test_upload_blob_partial_stream_chunked(self, **kwargs):
result = await (await blob.download_blob()).readall()
assert result == data[:length]

@pytest.mark.live_test_only
@BlobPreparer()
async def test_download_blob_decompress(self, **kwargs):
storage_account_name = kwargs.pop("storage_account_name")
storage_account_key = kwargs.pop("storage_account_key")

# Arrange
await self._setup(storage_account_name, storage_account_key)
blob_name = self._get_blob_reference()
blob = self.bsc.get_blob_client(self.container_name, blob_name)
compressed_data = b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcaH\xcd\xc9\xc9WH+\xca\xcfUH\xaf\xca,\x00\x00\x00\x00\xff\xff\x03\x00d\xaa\x8e\xb5\x0f\x00\x00\x00'
decompressed_data = b"hello from gzip"
content_settings = ContentSettings(content_encoding='gzip')

# Act / Assert
await blob.upload_blob(data=compressed_data, content_settings=content_settings)

downloaded = await blob.download_blob(decompress=True)
result = await downloaded.readall()
assert result == decompressed_data

downloaded = await blob.download_blob(decompress=False)
result = await downloaded.readall()
assert result == compressed_data

# ------------------------------------------------------------------------------