Skip to content

Commit 0a20c26

Browse files
[Storage] Incremental copy through page blob does not accept None values for seal_blob and blob_tags_string (#41124)
1 parent c2ac0a7 commit 0a20c26

File tree

5 files changed

+74
-67
lines changed

5 files changed

+74
-67
lines changed

sdk/storage/azure-storage-blob/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
### Features Added
66

7+
### Bugs Fixed
8+
- Fixed an issue where `BlobClient`'s `start_copy_from_url` with `incremental_copy=True` results in `TypeError`.
9+
710
## 12.26.0b1 (2025-05-06)
811

912
### Features Added

sdk/storage/azure-storage-blob/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "python",
44
"TagPrefix": "python/storage/azure-storage-blob",
5-
"Tag": "python/storage/azure-storage-blob_56ef3e2a11"
5+
"Tag": "python/storage/azure-storage-blob_01573d6981"
66
}

sdk/storage/azure-storage-blob/azure/storage/blob/_blob_client_helpers.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -658,19 +658,20 @@ def _start_copy_from_url_options( # pylint:disable=too-many-statements
658658

659659
options = {
660660
'copy_source': source_url,
661-
'seal_blob': kwargs.pop('seal_destination_blob', None),
662661
'timeout': timeout,
663662
'modified_access_conditions': dest_mod_conditions,
664-
'blob_tags_string': blob_tags_string,
665663
'headers': headers,
666664
'cls': return_response_headers,
667665
}
666+
668667
if not incremental_copy:
669668
source_mod_conditions = get_source_conditions(kwargs)
670669
dest_access_conditions = get_access_conditions(kwargs.pop('destination_lease', None))
671670
options['source_modified_access_conditions'] = source_mod_conditions
672671
options['lease_access_conditions'] = dest_access_conditions
673672
options['tier'] = tier.value if tier else None
673+
options['seal_blob'] = kwargs.pop('seal_destination_blob', None)
674+
options['blob_tags_string'] = blob_tags_string
674675
options.update(kwargs)
675676
return options
676677

sdk/storage/azure-storage-blob/tests/test_page_blob.py

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1981,49 +1981,51 @@ def test_create_blob_with_md5_large(self, **kwargs):
19811981

19821982
# Assert
19831983

1984-
@pytest.mark.skip(reason="Requires further investigation. Failing for unexpected kwarg seal_blob")
19851984
@BlobPreparer()
1985+
@recorded_by_proxy
19861986
def test_incremental_copy_blob(self, **kwargs):
19871987
storage_account_name = kwargs.pop("storage_account_name")
19881988
storage_account_key = kwargs.pop("storage_account_key")
19891989

19901990
bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), credential=storage_account_key, max_page_size=4 * 1024)
19911991
self._setup(bsc)
1992-
source_blob = self._create_blob(bsc, length=2048)
1993-
data = self.get_random_bytes(512)
1994-
resp1 = source_blob.upload_page(data, offset=0, length=512)
1995-
resp2 = source_blob.upload_page(data, offset=1024, length=512)
1996-
source_snapshot_blob = source_blob.create_snapshot()
1997-
1998-
snapshot_blob = BlobClient.from_blob_url(
1999-
source_blob.url, credential=source_blob.credential, snapshot=source_snapshot_blob)
2000-
sas_token = self.generate_sas(
2001-
generate_blob_sas,
2002-
snapshot_blob.account_name,
2003-
snapshot_blob.container_name,
2004-
snapshot_blob.blob_name,
2005-
snapshot=snapshot_blob.snapshot,
2006-
account_key=snapshot_blob.credential.account_key,
2007-
permission=BlobSasPermissions(read=True),
2008-
expiry=datetime.utcnow() + timedelta(hours=1),
2009-
)
2010-
sas_blob = BlobClient.from_blob_url(snapshot_blob.url, credential=sas_token)
20111992

2012-
# Act
2013-
dest_blob = bsc.get_blob_client(self.container_name, 'dest_blob')
2014-
copy = dest_blob.start_copy_from_url(sas_blob.url, incremental_copy=True)
1993+
try:
1994+
source_blob = self._create_blob(bsc, length=2048)
1995+
data = self.get_random_bytes(512)
1996+
resp1 = source_blob.upload_page(data, offset=0, length=512)
1997+
resp2 = source_blob.upload_page(data, offset=1024, length=512)
1998+
source_snapshot_blob = source_blob.create_snapshot()
1999+
2000+
snapshot_blob = BlobClient.from_blob_url(
2001+
source_blob.url, credential=source_blob.credential, snapshot=source_snapshot_blob)
2002+
sas_token = self.generate_sas(
2003+
generate_blob_sas,
2004+
snapshot_blob.account_name,
2005+
snapshot_blob.container_name,
2006+
snapshot_blob.blob_name,
2007+
snapshot=snapshot_blob.snapshot,
2008+
account_key=snapshot_blob.credential.account_key,
2009+
permission=BlobSasPermissions(read=True),
2010+
expiry=datetime.utcnow() + timedelta(hours=1),
2011+
)
2012+
sas_blob = BlobClient.from_blob_url(snapshot_blob.url, credential=sas_token)
20152013

2016-
# Assert
2017-
assert copy is not None
2018-
assert copy['copy_id'] is not None
2019-
assert copy['copy_status'] == 'pending'
2014+
# Act
2015+
dest_blob = bsc.get_blob_client(self.container_name, 'dest_blob')
2016+
copy = dest_blob.start_copy_from_url(sas_blob.url, incremental_copy=True)
20202017

2021-
copy_blob = self._wait_for_async_copy(dest_blob)
2022-
assert copy_blob.copy.status == 'success'
2023-
assert copy_blob.copy.destination_snapshot is not None
2018+
# Assert
2019+
assert copy is not None
2020+
assert copy['copy_id'] is not None
2021+
assert copy['copy_status'] == 'pending'
20242022

2025-
# strip off protocol
2026-
assert copy_blob.copy.source.endswith(sas_blob.url[5:])
2023+
copy_blob = self._wait_for_async_copy(dest_blob)
2024+
assert copy_blob.copy.status == 'success'
2025+
assert copy_blob.copy.destination_snapshot is not None
2026+
finally:
2027+
bsc.delete_container(self.container_name)
2028+
bsc.delete_container(self.source_container_name)
20272029

20282030
@BlobPreparer()
20292031
@recorded_by_proxy

sdk/storage/azure-storage-blob/tests/test_page_blob_async.py

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1965,7 +1965,6 @@ async def test_create_blob_with_md5_large(self, **kwargs):
19651965

19661966
# Assert
19671967

1968-
@pytest.mark.skip(reason="Requires further investigation. Failing for unexpected kwarg seal_blob")
19691968
@BlobPreparer()
19701969
@recorded_by_proxy_async
19711970
async def test_incremental_copy_blob(self, **kwargs):
@@ -1974,42 +1973,44 @@ async def test_incremental_copy_blob(self, **kwargs):
19741973

19751974
bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), credential=storage_account_key, max_page_size=4 * 1024)
19761975
await self._setup(bsc)
1977-
source_blob = await self._create_blob(bsc, 2048)
1978-
data = self.get_random_bytes(512)
1979-
resp1 = await source_blob.upload_page(data, offset=0, length=512)
1980-
resp2 = await source_blob.upload_page(data, offset=1024, length=512)
1981-
source_snapshot_blob = await source_blob.create_snapshot()
1982-
1983-
snapshot_blob = BlobClient.from_blob_url(
1984-
source_blob.url, credential=source_blob.credential, snapshot=source_snapshot_blob)
1985-
sas_token = self.generate_sas(
1986-
generate_blob_sas,
1987-
snapshot_blob.account_name,
1988-
snapshot_blob.container_name,
1989-
snapshot_blob.blob_name,
1990-
snapshot=snapshot_blob.snapshot,
1991-
account_key=snapshot_blob.credential.account_key,
1992-
permission=BlobSasPermissions(read=True),
1993-
expiry=datetime.utcnow() + timedelta(hours=1),
1994-
)
1995-
sas_blob = BlobClient.from_blob_url(snapshot_blob.url, credential=sas_token)
19961976

1977+
try:
1978+
source_blob = await self._create_blob(bsc, 2048)
1979+
data = self.get_random_bytes(512)
1980+
resp1 = await source_blob.upload_page(data, offset=0, length=512)
1981+
resp2 = await source_blob.upload_page(data, offset=1024, length=512)
1982+
source_snapshot_blob = await source_blob.create_snapshot()
1983+
1984+
snapshot_blob = BlobClient.from_blob_url(
1985+
source_blob.url, credential=source_blob.credential, snapshot=source_snapshot_blob)
1986+
sas_token = self.generate_sas(
1987+
generate_blob_sas,
1988+
snapshot_blob.account_name,
1989+
snapshot_blob.container_name,
1990+
snapshot_blob.blob_name,
1991+
snapshot=snapshot_blob.snapshot,
1992+
account_key=snapshot_blob.credential.account_key,
1993+
permission=BlobSasPermissions(read=True),
1994+
expiry=datetime.utcnow() + timedelta(hours=1),
1995+
)
1996+
sas_blob = BlobClient.from_blob_url(snapshot_blob.url, credential=sas_token)
19971997

1998-
# Act
1999-
dest_blob = bsc.get_blob_client(self.container_name, 'dest_blob')
2000-
copy = await dest_blob.start_copy_from_url(sas_blob.url, incremental_copy=True)
20011998

2002-
# Assert
2003-
assert copy is not None
2004-
assert copy['copy_id'] is not None
2005-
assert copy['copy_status'] == 'pending'
1999+
# Act
2000+
dest_blob = bsc.get_blob_client(self.container_name, 'dest_blob')
2001+
copy = await dest_blob.start_copy_from_url(sas_blob.url, incremental_copy=True)
20062002

2007-
copy_blob = await self._wait_for_async_copy(dest_blob)
2008-
assert copy_blob.copy.status == 'success'
2009-
assert copy_blob.copy.destination_snapshot is not None
2003+
# Assert
2004+
assert copy is not None
2005+
assert copy['copy_id'] is not None
2006+
assert copy['copy_status'] == 'pending'
20102007

2011-
# strip off protocol
2012-
assert copy_blob.copy.source.endswith(sas_blob.url[5:])
2008+
copy_blob = await self._wait_for_async_copy(dest_blob)
2009+
assert copy_blob.copy.status == 'success'
2010+
assert copy_blob.copy.destination_snapshot is not None
2011+
finally:
2012+
await bsc.delete_container(self.container_name)
2013+
await bsc.delete_container(self.source_container_name)
20132014

20142015
@BlobPreparer()
20152016
@recorded_by_proxy_async

0 commit comments

Comments
 (0)