Skip to content

Commit 9a6721d

Browse files
[Storage] Add 'create_if_not_exists()' for BlobContainerClient, FileShareClient, QueueClient, FileSystemClient (Azure#23574)
1 parent e8006fe commit 9a6721d

File tree

36 files changed

+1626
-35
lines changed

36 files changed

+1626
-35
lines changed

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,7 @@
33
## 12.10.1 (Unreleased)
44

55
### Features Added
6-
7-
### Breaking Changes
8-
9-
### Bugs Fixed
10-
11-
### Other Changes
6+
- Added support for `create_if_not_exists()` for `BlobContainerClient`
127

138
## 12.10.0 (2022-03-08)
149

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

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import six
2222

2323
from azure.core import MatchConditions
24-
from azure.core.exceptions import HttpResponseError, ResourceNotFoundError
24+
from azure.core.exceptions import HttpResponseError, ResourceNotFoundError, ResourceExistsError
2525
from azure.core.paging import ItemPaged
2626
from azure.core.tracing.decorator import distributed_trace
2727
from azure.core.pipeline import Pipeline
@@ -303,6 +303,34 @@ def create_container(self, metadata=None, public_access=None, **kwargs):
303303
except HttpResponseError as error:
304304
process_storage_error(error)
305305

306+
@distributed_trace
307+
def create_if_not_exists(self, **kwargs):
308+
# type: (**Any) -> None
309+
"""
310+
Creates a new container under the specified account. If the container
311+
with the same name already exists, it is not changed.
312+
313+
:keyword Dict[str, str] metadata:
314+
A dict with name_value pairs to associate with the
315+
container as metadata. Example:{'Category':'test'}
316+
:keyword ~azure.storage.blob.PublicAccess public_access:
317+
Possible values include: 'container', 'blob'.
318+
:keyword container_encryption_scope:
319+
Specifies the default encryption scope to set on the container and use for
320+
all future writes.
321+
322+
.. versionadded:: 12.2.0
323+
324+
:paramtype container_encryption_scope: dict or ~azure.storage.blob.ContainerEncryptionScope
325+
:keyword int timeout:
326+
The timeout parameter is expressed in seconds.
327+
:rtype: None
328+
"""
329+
try:
330+
return self.create_container(**kwargs)
331+
except ResourceExistsError:
332+
return None
333+
306334
@distributed_trace
307335
def _rename_container(self, new_name, **kwargs):
308336
# type: (str, **Any) -> ContainerClient

sdk/storage/azure-storage-blob/azure/storage/blob/aio/_container_client_async.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
TYPE_CHECKING
1212
)
1313

14-
from azure.core.exceptions import HttpResponseError, ResourceNotFoundError
14+
from azure.core.exceptions import HttpResponseError, ResourceNotFoundError, ResourceExistsError
1515
from azure.core.tracing.decorator import distributed_trace
1616
from azure.core.tracing.decorator_async import distributed_trace_async
1717
from azure.core.async_paging import AsyncItemPaged
@@ -168,6 +168,34 @@ async def create_container(self, metadata=None, public_access=None, **kwargs):
168168
except HttpResponseError as error:
169169
process_storage_error(error)
170170

171+
@distributed_trace_async
172+
async def create_if_not_exists(self, **kwargs):
173+
# type: (**Any) -> None
174+
"""
175+
Creates a new container under the specified account. If the container
176+
with the same name already exists, it is not changed.
177+
178+
:keyword Dict[str, str] metadata:
179+
A dict with name_value pairs to associate with the
180+
container as metadata. Example:{'Category':'test'}
181+
:keyword ~azure.storage.blob.PublicAccess public_access:
182+
Possible values include: 'container', 'blob'.
183+
:keyword container_encryption_scope:
184+
Specifies the default encryption scope to set on the container and use for
185+
all future writes.
186+
187+
.. versionadded:: 12.2.0
188+
189+
:paramtype container_encryption_scope: dict or ~azure.storage.blob.ContainerEncryptionScope
190+
:keyword int timeout:
191+
The timeout parameter is expressed in seconds.
192+
:rtype: None
193+
"""
194+
try:
195+
return await self.create_container(**kwargs)
196+
except ResourceExistsError:
197+
return None
198+
171199
@distributed_trace_async
172200
async def _rename_container(self, new_name, **kwargs):
173201
# type: (str, **Any) -> ContainerClient
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
interactions:
2+
- request:
3+
body: null
4+
headers:
5+
Accept:
6+
- application/xml
7+
Accept-Encoding:
8+
- gzip, deflate
9+
Connection:
10+
- keep-alive
11+
Content-Length:
12+
- '0'
13+
User-Agent:
14+
- azsdk-python-storage-blob/12.10.1 Python/3.10.2 (Windows-10-10.0.19044-SP0)
15+
x-ms-date:
16+
- Fri, 18 Mar 2022 18:56:41 GMT
17+
x-ms-version:
18+
- '2021-04-10'
19+
method: PUT
20+
uri: https://storagename.blob.core.windows.net/container785a1eaa?restype=container
21+
response:
22+
body:
23+
string: ''
24+
headers:
25+
content-length:
26+
- '0'
27+
date:
28+
- Fri, 18 Mar 2022 18:56:40 GMT
29+
etag:
30+
- '"0x8DA0911096F64AA"'
31+
last-modified:
32+
- Fri, 18 Mar 2022 18:56:40 GMT
33+
server:
34+
- Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
35+
x-ms-version:
36+
- '2021-04-10'
37+
status:
38+
code: 201
39+
message: Created
40+
- request:
41+
body: null
42+
headers:
43+
Accept:
44+
- application/xml
45+
Accept-Encoding:
46+
- gzip, deflate
47+
Connection:
48+
- keep-alive
49+
Content-Length:
50+
- '0'
51+
User-Agent:
52+
- azsdk-python-storage-blob/12.10.1 Python/3.10.2 (Windows-10-10.0.19044-SP0)
53+
x-ms-date:
54+
- Fri, 18 Mar 2022 18:56:42 GMT
55+
x-ms-version:
56+
- '2021-04-10'
57+
method: PUT
58+
uri: https://storagename.blob.core.windows.net/container785a1eaa?restype=container
59+
response:
60+
body:
61+
string: "\uFEFF<?xml version=\"1.0\" encoding=\"utf-8\"?><Error><Code>ContainerAlreadyExists</Code><Message>The
62+
specified container already exists.\nRequestId:6c8fa979-201e-008a-05f9-3a8173000000\nTime:2022-03-18T18:56:40.4555247Z</Message></Error>"
63+
headers:
64+
content-length:
65+
- '230'
66+
content-type:
67+
- application/xml
68+
date:
69+
- Fri, 18 Mar 2022 18:56:40 GMT
70+
server:
71+
- Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
72+
x-ms-error-code:
73+
- ContainerAlreadyExists
74+
x-ms-version:
75+
- '2021-04-10'
76+
status:
77+
code: 409
78+
message: The specified container already exists.
79+
version: 1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
interactions:
2+
- request:
3+
body: null
4+
headers:
5+
Accept:
6+
- application/xml
7+
Accept-Encoding:
8+
- gzip, deflate
9+
Connection:
10+
- keep-alive
11+
Content-Length:
12+
- '0'
13+
User-Agent:
14+
- azsdk-python-storage-blob/12.10.1 Python/3.10.2 (Windows-10-10.0.19044-SP0)
15+
x-ms-date:
16+
- Fri, 18 Mar 2022 18:56:48 GMT
17+
x-ms-version:
18+
- '2021-04-10'
19+
method: PUT
20+
uri: https://storagename.blob.core.windows.net/containerd8c72002?restype=container
21+
response:
22+
body:
23+
string: ''
24+
headers:
25+
content-length:
26+
- '0'
27+
date:
28+
- Fri, 18 Mar 2022 18:56:46 GMT
29+
etag:
30+
- '"0x8DA09110D2893BD"'
31+
last-modified:
32+
- Fri, 18 Mar 2022 18:56:46 GMT
33+
server:
34+
- Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
35+
x-ms-version:
36+
- '2021-04-10'
37+
status:
38+
code: 201
39+
message: Created
40+
version: 1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
interactions:
2+
- request:
3+
body: null
4+
headers:
5+
Accept:
6+
- application/xml
7+
User-Agent:
8+
- azsdk-python-storage-blob/12.10.1 Python/3.10.2 (Windows-10-10.0.19044-SP0)
9+
x-ms-date:
10+
- Fri, 18 Mar 2022 19:01:59 GMT
11+
x-ms-version:
12+
- '2021-04-10'
13+
method: PUT
14+
uri: https://storagename.blob.core.windows.net/acontainer399c2127?restype=container
15+
response:
16+
body:
17+
string: ''
18+
headers:
19+
content-length: '0'
20+
date: Fri, 18 Mar 2022 19:01:56 GMT
21+
etag: '"0x8DA0911C6843F7F"'
22+
last-modified: Fri, 18 Mar 2022 19:01:57 GMT
23+
server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
24+
x-ms-version: '2021-04-10'
25+
status:
26+
code: 201
27+
message: Created
28+
url: https://vincenttrancanary.blob.core.windows.net/acontainer399c2127?restype=container
29+
- request:
30+
body: null
31+
headers:
32+
Accept:
33+
- application/xml
34+
User-Agent:
35+
- azsdk-python-storage-blob/12.10.1 Python/3.10.2 (Windows-10-10.0.19044-SP0)
36+
x-ms-date:
37+
- Fri, 18 Mar 2022 19:01:59 GMT
38+
x-ms-version:
39+
- '2021-04-10'
40+
method: PUT
41+
uri: https://storagename.blob.core.windows.net/acontainer399c2127?restype=container
42+
response:
43+
body:
44+
string: "\uFEFF<?xml version=\"1.0\" encoding=\"utf-8\"?><Error><Code>ContainerAlreadyExists</Code><Message>The
45+
specified container already exists.\nRequestId:6b597129-701e-0001-51fa-3a851e000000\nTime:2022-03-18T19:01:57.6645912Z</Message></Error>"
46+
headers:
47+
content-length: '230'
48+
content-type: application/xml
49+
date: Fri, 18 Mar 2022 19:01:56 GMT
50+
server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
51+
x-ms-error-code: ContainerAlreadyExists
52+
x-ms-version: '2021-04-10'
53+
status:
54+
code: 409
55+
message: The specified container already exists.
56+
url: https://vincenttrancanary.blob.core.windows.net/acontainer399c2127?restype=container
57+
version: 1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
interactions:
2+
- request:
3+
body: null
4+
headers:
5+
Accept:
6+
- application/xml
7+
User-Agent:
8+
- azsdk-python-storage-blob/12.10.1 Python/3.10.2 (Windows-10-10.0.19044-SP0)
9+
x-ms-date:
10+
- Fri, 18 Mar 2022 19:01:52 GMT
11+
x-ms-version:
12+
- '2021-04-10'
13+
method: PUT
14+
uri: https://storagename.blob.core.windows.net/acontainera180227f?restype=container
15+
response:
16+
body:
17+
string: ''
18+
headers:
19+
content-length: '0'
20+
date: Fri, 18 Mar 2022 19:01:50 GMT
21+
etag: '"0x8DA0911C28A275B"'
22+
last-modified: Fri, 18 Mar 2022 19:01:50 GMT
23+
server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
24+
x-ms-version: '2021-04-10'
25+
status:
26+
code: 201
27+
message: Created
28+
url: https://vincenttrancanary.blob.core.windows.net/acontainera180227f?restype=container
29+
version: 1

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,31 @@ def test_create_container_with_already_existing_container_fail_on_exist(self, st
8080
# Assert
8181
self.assertTrue(created)
8282

83+
@BlobPreparer()
84+
def test_create_container_if_not_exists_without_existing_container(self, storage_account_name, storage_account_key):
85+
bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), storage_account_key)
86+
container_name = self._get_container_reference()
87+
88+
# Act
89+
container = bsc.get_container_client(container_name)
90+
created = container.create_if_not_exists()
91+
92+
# Assert
93+
self.assertTrue(created)
94+
95+
@BlobPreparer()
96+
def test_create_container_if_not_exists_with_existing_container(self, storage_account_name, storage_account_key):
97+
bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), storage_account_key)
98+
container_name = self._get_container_reference()
99+
100+
# Act
101+
container = bsc.get_container_client(container_name)
102+
container.create_container()
103+
created = container.create_if_not_exists()
104+
105+
# Assert
106+
self.assertIsNone(created)
107+
83108
@BlobPreparer()
84109
def test_create_container_with_public_access_container(self, storage_account_name, storage_account_key):
85110
bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), storage_account_key)

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,33 @@ async def test_create_container(self, storage_account_name, storage_account_key)
9898
# Assert
9999
self.assertTrue(created)
100100

101+
@BlobPreparer()
102+
@AsyncStorageTestCase.await_prepared_test
103+
async def test_create_container_if_not_exists_without_existing_container(self, storage_account_name, storage_account_key):
104+
bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), storage_account_key)
105+
container_name = self._get_container_reference()
106+
107+
# Act
108+
container = bsc.get_container_client(container_name)
109+
created = await container.create_if_not_exists()
110+
111+
# Assert
112+
self.assertTrue(created)
113+
114+
@BlobPreparer()
115+
@AsyncStorageTestCase.await_prepared_test
116+
async def test_create_container_if_not_exists_with_existing_container(self, storage_account_name, storage_account_key):
117+
bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), storage_account_key)
118+
container_name = self._get_container_reference()
119+
120+
# Act
121+
container = bsc.get_container_client(container_name)
122+
await container.create_container()
123+
created = await container.create_if_not_exists()
124+
125+
# Assert
126+
self.assertIsNone(created)
127+
101128
@BlobPreparer()
102129
@AsyncStorageTestCase.await_prepared_test
103130
async def test_create_cntnr_w_existing_cntnr_fail_on_exist(self, storage_account_name, storage_account_key):

sdk/storage/azure-storage-file-datalake/CHANGELOG.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@
33
## 12.6.1 (Unreleased)
44

55
### Features Added
6-
7-
### Breaking Changes
6+
- Added support for `create_if_not_exists()` for `FileSystemClient`
87

98
### Bugs Fixed
10-
11-
### Other Changes
9+
- Updated `create_file_system()` docstring to have the correct return-type of `None`
1210

1311
## 12.6.0 (2022-03-08)
1412

0 commit comments

Comments
 (0)