From c38cebc03f5b51f496642deffb65b18554c899e5 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Tue, 9 Jun 2026 15:59:47 -0400 Subject: [PATCH 1/3] Generated code --- .../_generated/aio/operations/_block_blob_operations.py | 6 ++++++ .../blob/_generated/operations/_block_blob_operations.py | 6 ++++++ .../azure-storage-blob/azure/storage/blob/_serialize.py | 1 + 3 files changed, 13 insertions(+) diff --git a/sdk/storage/azure-storage-blob/azure/storage/blob/_generated/aio/operations/_block_blob_operations.py b/sdk/storage/azure-storage-blob/azure/storage/blob/_generated/aio/operations/_block_blob_operations.py index cd5bfa6bff2a..c399c616740e 100644 --- a/sdk/storage/azure-storage-blob/azure/storage/blob/_generated/aio/operations/_block_blob_operations.py +++ b/sdk/storage/azure-storage-blob/azure/storage/blob/_generated/aio/operations/_block_blob_operations.py @@ -265,6 +265,9 @@ async def upload( # pylint: disable=too-many-locals response_headers["ETag"] = self._deserialize("str", response.headers.get("ETag")) response_headers["Last-Modified"] = self._deserialize("rfc-1123", response.headers.get("Last-Modified")) response_headers["Content-MD5"] = self._deserialize("bytearray", response.headers.get("Content-MD5")) + response_headers["x-ms-content-crc64"] = self._deserialize( + "bytearray", response.headers.get("x-ms-content-crc64") + ) response_headers["x-ms-client-request-id"] = self._deserialize( "str", response.headers.get("x-ms-client-request-id") ) @@ -522,6 +525,9 @@ async def put_blob_from_url( # pylint: disable=too-many-locals response_headers["ETag"] = self._deserialize("str", response.headers.get("ETag")) response_headers["Last-Modified"] = self._deserialize("rfc-1123", response.headers.get("Last-Modified")) response_headers["Content-MD5"] = self._deserialize("bytearray", response.headers.get("Content-MD5")) + response_headers["x-ms-content-crc64"] = self._deserialize( + "bytearray", response.headers.get("x-ms-content-crc64") + ) response_headers["x-ms-client-request-id"] = self._deserialize( "str", response.headers.get("x-ms-client-request-id") ) diff --git a/sdk/storage/azure-storage-blob/azure/storage/blob/_generated/operations/_block_blob_operations.py b/sdk/storage/azure-storage-blob/azure/storage/blob/_generated/operations/_block_blob_operations.py index a80c4bb9852b..c0bdfcbf0438 100644 --- a/sdk/storage/azure-storage-blob/azure/storage/blob/_generated/operations/_block_blob_operations.py +++ b/sdk/storage/azure-storage-blob/azure/storage/blob/_generated/operations/_block_blob_operations.py @@ -924,6 +924,9 @@ def upload( # pylint: disable=inconsistent-return-statements,too-many-locals response_headers["ETag"] = self._deserialize("str", response.headers.get("ETag")) response_headers["Last-Modified"] = self._deserialize("rfc-1123", response.headers.get("Last-Modified")) response_headers["Content-MD5"] = self._deserialize("bytearray", response.headers.get("Content-MD5")) + response_headers["x-ms-content-crc64"] = self._deserialize( + "bytearray", response.headers.get("x-ms-content-crc64") + ) response_headers["x-ms-client-request-id"] = self._deserialize( "str", response.headers.get("x-ms-client-request-id") ) @@ -1181,6 +1184,9 @@ def put_blob_from_url( # pylint: disable=inconsistent-return-statements,too-man response_headers["ETag"] = self._deserialize("str", response.headers.get("ETag")) response_headers["Last-Modified"] = self._deserialize("rfc-1123", response.headers.get("Last-Modified")) response_headers["Content-MD5"] = self._deserialize("bytearray", response.headers.get("Content-MD5")) + response_headers["x-ms-content-crc64"] = self._deserialize( + "bytearray", response.headers.get("x-ms-content-crc64") + ) response_headers["x-ms-client-request-id"] = self._deserialize( "str", response.headers.get("x-ms-client-request-id") ) diff --git a/sdk/storage/azure-storage-blob/azure/storage/blob/_serialize.py b/sdk/storage/azure-storage-blob/azure/storage/blob/_serialize.py index a18129ab5c8c..8538f9344212 100644 --- a/sdk/storage/azure-storage-blob/azure/storage/blob/_serialize.py +++ b/sdk/storage/azure-storage-blob/azure/storage/blob/_serialize.py @@ -65,6 +65,7 @@ "2026-02-06", "2026-04-06", "2026-06-06", + "2026-10-06", ] From c1cf2d5c7dc841f52a119b8497603f6637bdbb66 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Tue, 9 Jun 2026 17:26:14 -0400 Subject: [PATCH 2/3] Feature: MD5/CRC64 Combined Return --- sdk/storage/azure-storage-blob/CHANGELOG.md | 5 +++++ sdk/storage/azure-storage-blob/tests/test_append_blob.py | 4 ++++ .../azure-storage-blob/tests/test_append_blob_async.py | 4 ++++ sdk/storage/azure-storage-blob/tests/test_block_blob.py | 8 ++++++-- .../azure-storage-blob/tests/test_block_blob_async.py | 8 ++++++-- .../azure-storage-blob/tests/test_block_blob_sync_copy.py | 4 +++- .../tests/test_block_blob_sync_copy_async.py | 4 +++- sdk/storage/azure-storage-blob/tests/test_page_blob.py | 6 +++++- .../azure-storage-blob/tests/test_page_blob_async.py | 6 +++++- 9 files changed, 41 insertions(+), 8 deletions(-) diff --git a/sdk/storage/azure-storage-blob/CHANGELOG.md b/sdk/storage/azure-storage-blob/CHANGELOG.md index f1eee53168a8..98b012ea394a 100644 --- a/sdk/storage/azure-storage-blob/CHANGELOG.md +++ b/sdk/storage/azure-storage-blob/CHANGELOG.md @@ -3,6 +3,11 @@ ## 12.31.0b1 (Unreleased) ### Features Added +- The service-calculated CRC64 is now surfaced as `content_crc64` +on the response of `BlobClient` upload operations +(`stage_block`, `stage_block_from_url`, `upload_page`, `upload_pages_from_url`, +`append_block`, `append_block_from_url`, `upload_blob`, and `upload_blob_from_url`) +in addition to `content_md5` when a content MD5 is provided with the request. ## 12.30.0 (2026-06-08) diff --git a/sdk/storage/azure-storage-blob/tests/test_append_blob.py b/sdk/storage/azure-storage-blob/tests/test_append_blob.py index b1f038bcb378..38e47c4fea4d 100644 --- a/sdk/storage/azure-storage-blob/tests/test_append_blob.py +++ b/sdk/storage/azure-storage-blob/tests/test_append_blob.py @@ -279,6 +279,8 @@ def test_append_block_with_md5(self, **kwargs): assert resp["blob_committed_block_count"] == 1 assert resp["etag"] is not None assert resp["last_modified"] is not None + assert resp["content_md5"] is not None + assert resp["content_crc64"] is not None # Assert @@ -410,6 +412,8 @@ def test_append_block_from_url_and_validate_content_md5(self, **kwargs): assert resp.get("blob_committed_block_count") == 1 assert resp.get("etag") is not None assert resp.get("last_modified") is not None + assert resp.get("content_md5") is not None + assert resp.get("content_crc64") is not None # Assert the destination blob is constructed correctly destination_blob_properties = destination_blob_client.get_blob_properties() diff --git a/sdk/storage/azure-storage-blob/tests/test_append_blob_async.py b/sdk/storage/azure-storage-blob/tests/test_append_blob_async.py index e708472b62a1..6ab209e5982b 100644 --- a/sdk/storage/azure-storage-blob/tests/test_append_blob_async.py +++ b/sdk/storage/azure-storage-blob/tests/test_append_blob_async.py @@ -301,6 +301,8 @@ async def test_append_block_with_md5(self, **kwargs): assert resp["blob_committed_block_count"] == 1 assert resp["etag"] is not None assert resp["last_modified"] is not None + assert resp["content_md5"] is not None + assert resp["content_crc64"] is not None # Assert @@ -409,6 +411,8 @@ async def test_append_block_from_url_and_validate_content_md5(self, **kwargs): assert resp.get("blob_committed_block_count") == 1 assert resp.get("etag") is not None assert resp.get("last_modified") is not None + assert resp.get("content_md5") is not None + assert resp.get("content_crc64") is not None # Assert the destination blob is constructed correctly destination_blob_properties = await destination_blob_client.get_blob_properties() diff --git a/sdk/storage/azure-storage-blob/tests/test_block_blob.py b/sdk/storage/azure-storage-blob/tests/test_block_blob.py index eff4b8f2bab3..6a4069af0a85 100644 --- a/sdk/storage/azure-storage-blob/tests/test_block_blob.py +++ b/sdk/storage/azure-storage-blob/tests/test_block_blob.py @@ -585,9 +585,11 @@ def test_upload_blob_from_url_with_source_content_md5(self, **kwargs): new_blob = self.bsc.get_blob_client(self.container_name, blob_name) # Assert - new_blob.upload_blob_from_url( + resp = new_blob.upload_blob_from_url( source_blob_url, include_source_blob_properties=True, source_content_md5=source_md5 ) + assert resp.get("content_md5") is not None + assert resp.get("content_crc64") is not None with pytest.raises(HttpResponseError): new_blob.upload_blob_from_url( source_blob_url, include_source_blob_properties=False, source_content_md5=bad_source_md5 @@ -718,9 +720,11 @@ def test_put_block_with_md5(self, **kwargs): blob = self._create_blob() # Act - blob.stage_block(1, b"block", validate_content=True) + resp = blob.stage_block(1, b"block", validate_content=True) # Assert + assert resp.get("content_md5") is not None + assert resp.get("content_crc64") is not None @BlobPreparer() @recorded_by_proxy diff --git a/sdk/storage/azure-storage-blob/tests/test_block_blob_async.py b/sdk/storage/azure-storage-blob/tests/test_block_blob_async.py index 05ea750b255f..b5dbc367c01e 100644 --- a/sdk/storage/azure-storage-blob/tests/test_block_blob_async.py +++ b/sdk/storage/azure-storage-blob/tests/test_block_blob_async.py @@ -618,9 +618,11 @@ async def test_upload_blob_from_url_with_source_content_md5(self, **kwargs): new_blob = self.bsc.get_blob_client(self.container_name, blob_name) # Assert - await new_blob.upload_blob_from_url( + resp = await new_blob.upload_blob_from_url( source_blob_url, include_source_blob_properties=True, source_content_md5=source_md5 ) + assert resp.get("content_md5") is not None + assert resp.get("content_crc64") is not None with pytest.raises(HttpResponseError): await new_blob.upload_blob_from_url( source_blob_url, include_source_blob_properties=False, source_content_md5=bad_source_md5 @@ -803,9 +805,11 @@ async def test_put_block_with_md5(self, **kwargs): blob = await self._create_blob() # Act - await blob.stage_block(1, b"block", validate_content=True) + resp = await blob.stage_block(1, b"block", validate_content=True) # Assert + assert resp.get("content_md5") is not None + assert resp.get("content_crc64") is not None @BlobPreparer() @recorded_by_proxy_async diff --git a/sdk/storage/azure-storage-blob/tests/test_block_blob_sync_copy.py b/sdk/storage/azure-storage-blob/tests/test_block_blob_sync_copy.py index 6333bae1888b..3bfff4ac6a4f 100644 --- a/sdk/storage/azure-storage-blob/tests/test_block_blob_sync_copy.py +++ b/sdk/storage/azure-storage-blob/tests/test_block_blob_sync_copy.py @@ -205,13 +205,15 @@ def test_put_block_from_url_and_validate_content_md5(self, **kwargs): src_md5 = calculate_content_md5(self.source_blob_data) # Act part 1: put block from url with md5 validation - dest_blob.stage_block_from_url( + resp = dest_blob.stage_block_from_url( block_id=1, source_url=self.source_blob_url, source_content_md5=src_md5, source_offset=0, source_length=8 * 1024, ) + assert resp.get("content_md5") is not None + assert resp.get("content_crc64") is not None # Assert block was staged committed, uncommitted = dest_blob.get_block_list("all") diff --git a/sdk/storage/azure-storage-blob/tests/test_block_blob_sync_copy_async.py b/sdk/storage/azure-storage-blob/tests/test_block_blob_sync_copy_async.py index 9c87c0885637..f1c930625121 100644 --- a/sdk/storage/azure-storage-blob/tests/test_block_blob_sync_copy_async.py +++ b/sdk/storage/azure-storage-blob/tests/test_block_blob_sync_copy_async.py @@ -167,13 +167,15 @@ async def test_put_block_from_url_and_vldte_content_md5(self, **kwargs): src_md5 = calculate_content_md5(self.source_blob_data) # Act part 1: put block from url with md5 validation - await dest_blob.stage_block_from_url( + resp = await dest_blob.stage_block_from_url( block_id=1, source_url=self.source_blob_url, source_content_md5=src_md5, source_offset=0, source_length=8 * 1024, ) + assert resp.get("content_md5") is not None + assert resp.get("content_crc64") is not None # Assert block was staged committed, uncommitted = await dest_blob.get_block_list("all") diff --git a/sdk/storage/azure-storage-blob/tests/test_page_blob.py b/sdk/storage/azure-storage-blob/tests/test_page_blob.py index e29a68591899..d02c4d41407a 100644 --- a/sdk/storage/azure-storage-blob/tests/test_page_blob.py +++ b/sdk/storage/azure-storage-blob/tests/test_page_blob.py @@ -417,9 +417,11 @@ def test_update_page_with_md5(self, **kwargs): # Act data = self.get_random_bytes(512) - blob.upload_page(data, offset=0, length=512, validate_content=True) + resp = blob.upload_page(data, offset=0, length=512, validate_content=True) # Assert + assert resp.get("content_md5") is not None + assert resp.get("content_crc64") is not None @BlobPreparer() @recorded_by_proxy @@ -752,6 +754,8 @@ def test_upload_pages_from_url_and_validate_content_md5(self, **kwargs): ) assert resp.get("etag") is not None assert resp.get("last_modified") is not None + assert resp.get("content_md5") is not None + assert resp.get("content_crc64") is not None # Assert the destination blob is constructed correctly blob_properties = destination_blob_client.get_blob_properties() diff --git a/sdk/storage/azure-storage-blob/tests/test_page_blob_async.py b/sdk/storage/azure-storage-blob/tests/test_page_blob_async.py index 9131d2c7072e..6e7f8523dce1 100644 --- a/sdk/storage/azure-storage-blob/tests/test_page_blob_async.py +++ b/sdk/storage/azure-storage-blob/tests/test_page_blob_async.py @@ -451,8 +451,10 @@ async def test_update_page_with_md5(self, **kwargs): # Act data = self.get_random_bytes(512) - await blob.upload_page(data, offset=0, length=512, validate_content=True) + resp = await blob.upload_page(data, offset=0, length=512, validate_content=True) # Assert + assert resp.get("content_md5") is not None + assert resp.get("content_crc64") is not None @BlobPreparer() @recorded_by_proxy_async @@ -697,6 +699,8 @@ async def test_upload_pages_from_url_and_validate_content_md5(self, **kwargs): ) assert resp.get("etag") is not None assert resp.get("last_modified") is not None + assert resp.get("content_md5") is not None + assert resp.get("content_crc64") is not None # Assert the destination blob is constructed correctly blob_properties = await destination_blob_client.get_blob_properties() From b382d49becd119ea993237dc33b0f27d82855432 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Tue, 9 Jun 2026 17:39:44 -0400 Subject: [PATCH 3/3] Recorded tests --- sdk/storage/azure-storage-blob/assets.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/storage/azure-storage-blob/assets.json b/sdk/storage/azure-storage-blob/assets.json index 93dd0f1e298c..4f70313e5eae 100644 --- a/sdk/storage/azure-storage-blob/assets.json +++ b/sdk/storage/azure-storage-blob/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "python", "TagPrefix": "python/storage/azure-storage-blob", - "Tag": "python/storage/azure-storage-blob_b09e37b521" + "Tag": "python/storage/azure-storage-blob_3903d1bfe0" }