azure-storage-blob 12.26.0b1__py3-none-any.whl → 12.27.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- azure/storage/blob/__init__.py +3 -2
- azure/storage/blob/_blob_client.py +43 -38
- azure/storage/blob/_blob_client_helpers.py +4 -3
- azure/storage/blob/_blob_service_client.py +16 -13
- azure/storage/blob/_container_client.py +25 -22
- azure/storage/blob/_deserialize.py +1 -1
- azure/storage/blob/_download.py +7 -7
- azure/storage/blob/_encryption.py +177 -184
- azure/storage/blob/_generated/_azure_blob_storage.py +3 -2
- azure/storage/blob/_generated/_configuration.py +2 -2
- azure/storage/blob/_generated/_utils/__init__.py +6 -0
- azure/storage/blob/_generated/{_serialization.py → _utils/serialization.py} +4 -22
- azure/storage/blob/_generated/aio/_azure_blob_storage.py +3 -2
- azure/storage/blob/_generated/aio/_configuration.py +2 -2
- azure/storage/blob/_generated/aio/operations/_append_blob_operations.py +6 -10
- azure/storage/blob/_generated/aio/operations/_blob_operations.py +35 -39
- azure/storage/blob/_generated/aio/operations/_block_blob_operations.py +9 -13
- azure/storage/blob/_generated/aio/operations/_container_operations.py +20 -24
- azure/storage/blob/_generated/aio/operations/_page_blob_operations.py +13 -17
- azure/storage/blob/_generated/aio/operations/_service_operations.py +10 -14
- azure/storage/blob/_generated/models/_models_py3.py +30 -9
- azure/storage/blob/_generated/operations/_append_blob_operations.py +11 -15
- azure/storage/blob/_generated/operations/_blob_operations.py +60 -64
- azure/storage/blob/_generated/operations/_block_blob_operations.py +16 -20
- azure/storage/blob/_generated/operations/_container_operations.py +39 -43
- azure/storage/blob/_generated/operations/_page_blob_operations.py +23 -27
- azure/storage/blob/_generated/operations/_service_operations.py +19 -23
- azure/storage/blob/_lease.py +1 -0
- azure/storage/blob/_list_blobs_helper.py +1 -1
- azure/storage/blob/_quick_query_helper.py +3 -3
- azure/storage/blob/_serialize.py +1 -0
- azure/storage/blob/_shared/__init__.py +7 -7
- azure/storage/blob/_shared/authentication.py +49 -32
- azure/storage/blob/_shared/avro/avro_io.py +44 -42
- azure/storage/blob/_shared/avro/avro_io_async.py +42 -41
- azure/storage/blob/_shared/avro/datafile.py +24 -21
- azure/storage/blob/_shared/avro/datafile_async.py +15 -15
- azure/storage/blob/_shared/avro/schema.py +196 -217
- azure/storage/blob/_shared/base_client.py +81 -59
- azure/storage/blob/_shared/base_client_async.py +58 -51
- azure/storage/blob/_shared/constants.py +1 -1
- azure/storage/blob/_shared/models.py +94 -92
- azure/storage/blob/_shared/parser.py +3 -3
- azure/storage/blob/_shared/policies.py +186 -147
- azure/storage/blob/_shared/policies_async.py +53 -65
- azure/storage/blob/_shared/request_handlers.py +50 -45
- azure/storage/blob/_shared/response_handlers.py +54 -45
- azure/storage/blob/_shared/shared_access_signature.py +65 -73
- azure/storage/blob/_shared/uploads.py +56 -49
- azure/storage/blob/_shared/uploads_async.py +70 -58
- azure/storage/blob/_version.py +1 -1
- azure/storage/blob/aio/__init__.py +3 -2
- azure/storage/blob/aio/_blob_client_async.py +53 -40
- azure/storage/blob/aio/_blob_service_client_async.py +13 -11
- azure/storage/blob/aio/_container_client_async.py +28 -25
- azure/storage/blob/aio/_download_async.py +7 -7
- azure/storage/blob/aio/_lease_async.py +1 -0
- azure/storage/blob/aio/_quick_query_helper_async.py +3 -3
- {azure_storage_blob-12.26.0b1.dist-info → azure_storage_blob-12.27.0b1.dist-info}/METADATA +3 -4
- azure_storage_blob-12.27.0b1.dist-info/RECORD +86 -0
- azure_storage_blob-12.26.0b1.dist-info/RECORD +0 -85
- {azure_storage_blob-12.26.0b1.dist-info → azure_storage_blob-12.27.0b1.dist-info}/LICENSE +0 -0
- {azure_storage_blob-12.26.0b1.dist-info → azure_storage_blob-12.27.0b1.dist-info}/WHEEL +0 -0
- {azure_storage_blob-12.26.0b1.dist-info → azure_storage_blob-12.27.0b1.dist-info}/top_level.txt +0 -0
@@ -20,7 +20,10 @@ from urllib.parse import parse_qs, quote
|
|
20
20
|
from azure.core.credentials import AzureSasCredential, AzureNamedKeyCredential, TokenCredential
|
21
21
|
from azure.core.exceptions import HttpResponseError
|
22
22
|
from azure.core.pipeline import Pipeline
|
23
|
-
from azure.core.pipeline.transport import
|
23
|
+
from azure.core.pipeline.transport import ( # pylint: disable=non-abstract-transport-import, no-name-in-module
|
24
|
+
HttpTransport,
|
25
|
+
RequestsTransport,
|
26
|
+
)
|
24
27
|
from azure.core.pipeline.policies import (
|
25
28
|
AzureSasCredentialPolicy,
|
26
29
|
ContentDecodePolicy,
|
@@ -73,8 +76,17 @@ class StorageAccountHostsMixin(object):
|
|
73
76
|
self,
|
74
77
|
parsed_url: Any,
|
75
78
|
service: str,
|
76
|
-
credential: Optional[
|
77
|
-
|
79
|
+
credential: Optional[
|
80
|
+
Union[
|
81
|
+
str,
|
82
|
+
Dict[str, str],
|
83
|
+
AzureNamedKeyCredential,
|
84
|
+
AzureSasCredential,
|
85
|
+
"AsyncTokenCredential",
|
86
|
+
TokenCredential,
|
87
|
+
]
|
88
|
+
] = None,
|
89
|
+
**kwargs: Any,
|
78
90
|
) -> None:
|
79
91
|
self._location_mode = kwargs.get("_location_mode", LocationMode.PRIMARY)
|
80
92
|
self._hosts = kwargs.get("_hosts", {})
|
@@ -83,12 +95,15 @@ class StorageAccountHostsMixin(object):
|
|
83
95
|
|
84
96
|
if service not in ["blob", "queue", "file-share", "dfs"]:
|
85
97
|
raise ValueError(f"Invalid service: {service}")
|
86
|
-
service_name = service.split(
|
98
|
+
service_name = service.split("-")[0]
|
87
99
|
account = parsed_url.netloc.split(f".{service_name}.core.")
|
88
100
|
|
89
101
|
self.account_name = account[0] if len(account) > 1 else None
|
90
|
-
if
|
91
|
-
|
102
|
+
if (
|
103
|
+
not self.account_name
|
104
|
+
and parsed_url.netloc.startswith("localhost")
|
105
|
+
or parsed_url.netloc.startswith("127.0.0.1")
|
106
|
+
):
|
92
107
|
self._is_localhost = True
|
93
108
|
self.account_name = parsed_url.path.strip("/")
|
94
109
|
|
@@ -106,7 +121,7 @@ class StorageAccountHostsMixin(object):
|
|
106
121
|
secondary_hostname = parsed_url.netloc.replace(account[0], account[0] + "-secondary")
|
107
122
|
if kwargs.get("secondary_hostname"):
|
108
123
|
secondary_hostname = kwargs["secondary_hostname"]
|
109
|
-
primary_hostname = (parsed_url.netloc + parsed_url.path).rstrip(
|
124
|
+
primary_hostname = (parsed_url.netloc + parsed_url.path).rstrip("/")
|
110
125
|
self._hosts = {LocationMode.PRIMARY: primary_hostname, LocationMode.SECONDARY: secondary_hostname}
|
111
126
|
|
112
127
|
self._sdk_moniker = f"storage-{service}/{VERSION}"
|
@@ -119,71 +134,76 @@ class StorageAccountHostsMixin(object):
|
|
119
134
|
def __exit__(self, *args):
|
120
135
|
self._client.__exit__(*args)
|
121
136
|
|
122
|
-
def close(self):
|
123
|
-
"""
|
137
|
+
def close(self) -> None:
|
138
|
+
"""This method is to close the sockets opened by the client.
|
124
139
|
It need not be used when using with a context manager.
|
125
140
|
"""
|
126
141
|
self._client.close()
|
127
142
|
|
128
143
|
@property
|
129
|
-
def url(self):
|
144
|
+
def url(self) -> str:
|
130
145
|
"""The full endpoint URL to this entity, including SAS token if used.
|
131
146
|
|
132
147
|
This could be either the primary endpoint,
|
133
148
|
or the secondary endpoint depending on the current :func:`location_mode`.
|
134
149
|
|
135
|
-
:
|
150
|
+
:return: The full endpoint URL to this entity, including SAS token if used.
|
136
151
|
:rtype: str
|
137
152
|
"""
|
138
|
-
return self._format_url(self._hosts[self._location_mode])
|
153
|
+
return self._format_url(self._hosts[self._location_mode]) # type: ignore
|
139
154
|
|
140
155
|
@property
|
141
|
-
def primary_endpoint(self):
|
156
|
+
def primary_endpoint(self) -> str:
|
142
157
|
"""The full primary endpoint URL.
|
143
158
|
|
159
|
+
:return: The full primary endpoint URL.
|
144
160
|
:rtype: str
|
145
161
|
"""
|
146
|
-
return self._format_url(self._hosts[LocationMode.PRIMARY])
|
162
|
+
return self._format_url(self._hosts[LocationMode.PRIMARY]) # type: ignore
|
147
163
|
|
148
164
|
@property
|
149
|
-
def primary_hostname(self):
|
165
|
+
def primary_hostname(self) -> str:
|
150
166
|
"""The hostname of the primary endpoint.
|
151
167
|
|
168
|
+
:return: The hostname of the primary endpoint.
|
152
169
|
:rtype: str
|
153
170
|
"""
|
154
171
|
return self._hosts[LocationMode.PRIMARY]
|
155
172
|
|
156
173
|
@property
|
157
|
-
def secondary_endpoint(self):
|
174
|
+
def secondary_endpoint(self) -> str:
|
158
175
|
"""The full secondary endpoint URL if configured.
|
159
176
|
|
160
177
|
If not available a ValueError will be raised. To explicitly specify a secondary hostname, use the optional
|
161
178
|
`secondary_hostname` keyword argument on instantiation.
|
162
179
|
|
180
|
+
:return: The full secondary endpoint URL.
|
163
181
|
:rtype: str
|
164
|
-
:raise ValueError:
|
182
|
+
:raise ValueError: If no secondary endpoint is configured.
|
165
183
|
"""
|
166
184
|
if not self._hosts[LocationMode.SECONDARY]:
|
167
185
|
raise ValueError("No secondary host configured.")
|
168
|
-
return self._format_url(self._hosts[LocationMode.SECONDARY])
|
186
|
+
return self._format_url(self._hosts[LocationMode.SECONDARY]) # type: ignore
|
169
187
|
|
170
188
|
@property
|
171
|
-
def secondary_hostname(self):
|
189
|
+
def secondary_hostname(self) -> Optional[str]:
|
172
190
|
"""The hostname of the secondary endpoint.
|
173
191
|
|
174
192
|
If not available this will be None. To explicitly specify a secondary hostname, use the optional
|
175
193
|
`secondary_hostname` keyword argument on instantiation.
|
176
194
|
|
195
|
+
:return: The hostname of the secondary endpoint, or None if not configured.
|
177
196
|
:rtype: Optional[str]
|
178
197
|
"""
|
179
198
|
return self._hosts[LocationMode.SECONDARY]
|
180
199
|
|
181
200
|
@property
|
182
|
-
def location_mode(self):
|
201
|
+
def location_mode(self) -> str:
|
183
202
|
"""The location mode that the client is currently using.
|
184
203
|
|
185
204
|
By default this will be "primary". Options include "primary" and "secondary".
|
186
205
|
|
206
|
+
:return: The current location mode.
|
187
207
|
:rtype: str
|
188
208
|
"""
|
189
209
|
|
@@ -206,11 +226,16 @@ class StorageAccountHostsMixin(object):
|
|
206
226
|
return self._client._config.version # pylint: disable=protected-access
|
207
227
|
|
208
228
|
def _format_query_string(
|
209
|
-
self,
|
210
|
-
|
229
|
+
self,
|
230
|
+
sas_token: Optional[str],
|
231
|
+
credential: Optional[
|
232
|
+
Union[str, Dict[str, str], "AzureNamedKeyCredential", "AzureSasCredential", TokenCredential]
|
233
|
+
],
|
211
234
|
snapshot: Optional[str] = None,
|
212
|
-
share_snapshot: Optional[str] = None
|
213
|
-
) -> Tuple[
|
235
|
+
share_snapshot: Optional[str] = None,
|
236
|
+
) -> Tuple[
|
237
|
+
str, Optional[Union[str, Dict[str, str], "AzureNamedKeyCredential", "AzureSasCredential", TokenCredential]]
|
238
|
+
]:
|
214
239
|
query_str = "?"
|
215
240
|
if snapshot:
|
216
241
|
query_str += f"snapshot={snapshot}&"
|
@@ -218,7 +243,8 @@ class StorageAccountHostsMixin(object):
|
|
218
243
|
query_str += f"sharesnapshot={share_snapshot}&"
|
219
244
|
if sas_token and isinstance(credential, AzureSasCredential):
|
220
245
|
raise ValueError(
|
221
|
-
"You cannot use AzureSasCredential when the resource URI also contains a Shared Access Signature."
|
246
|
+
"You cannot use AzureSasCredential when the resource URI also contains a Shared Access Signature."
|
247
|
+
)
|
222
248
|
if _is_credential_sastoken(credential):
|
223
249
|
credential = cast(str, credential)
|
224
250
|
query_str += credential.lstrip("?")
|
@@ -228,13 +254,16 @@ class StorageAccountHostsMixin(object):
|
|
228
254
|
return query_str.rstrip("?&"), credential
|
229
255
|
|
230
256
|
def _create_pipeline(
|
231
|
-
self,
|
232
|
-
|
257
|
+
self,
|
258
|
+
credential: Optional[
|
259
|
+
Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, TokenCredential]
|
260
|
+
] = None,
|
261
|
+
**kwargs: Any,
|
233
262
|
) -> Tuple[StorageConfiguration, Pipeline]:
|
234
263
|
self._credential_policy: Any = None
|
235
264
|
if hasattr(credential, "get_token"):
|
236
|
-
if kwargs.get(
|
237
|
-
audience = str(kwargs.pop(
|
265
|
+
if kwargs.get("audience"):
|
266
|
+
audience = str(kwargs.pop("audience")).rstrip("/") + DEFAULT_OAUTH_SCOPE
|
238
267
|
else:
|
239
268
|
audience = STORAGE_OAUTH_SCOPE
|
240
269
|
self._credential_policy = StorageBearerTokenCredentialPolicy(cast(TokenCredential, credential), audience)
|
@@ -268,22 +297,18 @@ class StorageAccountHostsMixin(object):
|
|
268
297
|
config.logging_policy,
|
269
298
|
StorageResponseHook(**kwargs),
|
270
299
|
DistributedTracingPolicy(**kwargs),
|
271
|
-
HttpLoggingPolicy(**kwargs)
|
300
|
+
HttpLoggingPolicy(**kwargs),
|
272
301
|
]
|
273
302
|
if kwargs.get("_additional_pipeline_policies"):
|
274
303
|
policies = policies + kwargs.get("_additional_pipeline_policies") # type: ignore
|
275
304
|
config.transport = transport # type: ignore
|
276
305
|
return config, Pipeline(transport, policies=policies)
|
277
306
|
|
278
|
-
def _batch_send(
|
279
|
-
self,
|
280
|
-
*reqs: "HttpRequest",
|
281
|
-
**kwargs: Any
|
282
|
-
) -> Iterator["HttpResponse"]:
|
307
|
+
def _batch_send(self, *reqs: "HttpRequest", **kwargs: Any) -> Iterator["HttpResponse"]:
|
283
308
|
"""Given a series of request, do a Storage batch call.
|
284
309
|
|
285
310
|
:param HttpRequest reqs: A collection of HttpRequest objects.
|
286
|
-
:
|
311
|
+
:return: An iterator of HttpResponse objects.
|
287
312
|
:rtype: Iterator[HttpResponse]
|
288
313
|
"""
|
289
314
|
# Pop it here, so requests doesn't feel bad about additional kwarg
|
@@ -292,25 +317,21 @@ class StorageAccountHostsMixin(object):
|
|
292
317
|
|
293
318
|
request = self._client._client.post( # pylint: disable=protected-access
|
294
319
|
url=(
|
295
|
-
f
|
320
|
+
f"{self.scheme}://{self.primary_hostname}/"
|
296
321
|
f"{kwargs.pop('path', '')}?{kwargs.pop('restype', '')}"
|
297
322
|
f"comp=batch{kwargs.pop('sas', '')}{kwargs.pop('timeout', '')}"
|
298
323
|
),
|
299
324
|
headers={
|
300
|
-
|
301
|
-
"Content-Type": "multipart/mixed; boundary=" + _get_batch_request_delimiter(batch_id, False, False)
|
302
|
-
}
|
325
|
+
"x-ms-version": self.api_version,
|
326
|
+
"Content-Type": "multipart/mixed; boundary=" + _get_batch_request_delimiter(batch_id, False, False),
|
327
|
+
},
|
303
328
|
)
|
304
329
|
|
305
330
|
policies = [StorageHeadersPolicy()]
|
306
331
|
if self._credential_policy:
|
307
332
|
policies.append(self._credential_policy)
|
308
333
|
|
309
|
-
request.set_multipart_mixed(
|
310
|
-
*reqs,
|
311
|
-
policies=policies,
|
312
|
-
enforce_https=False
|
313
|
-
)
|
334
|
+
request.set_multipart_mixed(*reqs, policies=policies, enforce_https=False)
|
314
335
|
|
315
336
|
Pipeline._prepare_multipart_mixed_request(request) # pylint: disable=protected-access
|
316
337
|
body = serialize_batch_body(request.multipart_mixed_info[0], batch_id)
|
@@ -318,9 +339,7 @@ class StorageAccountHostsMixin(object):
|
|
318
339
|
|
319
340
|
temp = request.multipart_mixed_info
|
320
341
|
request.multipart_mixed_info = None
|
321
|
-
pipeline_response = self._pipeline.run(
|
322
|
-
request, **kwargs
|
323
|
-
)
|
342
|
+
pipeline_response = self._pipeline.run(request, **kwargs)
|
324
343
|
response = pipeline_response.http_response
|
325
344
|
request.multipart_mixed_info = temp
|
326
345
|
|
@@ -332,8 +351,7 @@ class StorageAccountHostsMixin(object):
|
|
332
351
|
parts = list(response.parts())
|
333
352
|
if any(p for p in parts if not 200 <= p.status_code < 300):
|
334
353
|
error = PartialBatchErrorException(
|
335
|
-
message="There is a partial failure in the batch operation.",
|
336
|
-
response=response, parts=parts
|
354
|
+
message="There is a partial failure in the batch operation.", response=response, parts=parts
|
337
355
|
)
|
338
356
|
raise error
|
339
357
|
return iter(parts)
|
@@ -347,6 +365,7 @@ class TransportWrapper(HttpTransport):
|
|
347
365
|
by a `get_client` method does not close the outer transport for the parent
|
348
366
|
when used in a context manager.
|
349
367
|
"""
|
368
|
+
|
350
369
|
def __init__(self, transport):
|
351
370
|
self._transport = transport
|
352
371
|
|
@@ -368,7 +387,9 @@ class TransportWrapper(HttpTransport):
|
|
368
387
|
|
369
388
|
def _format_shared_key_credential(
|
370
389
|
account_name: Optional[str],
|
371
|
-
credential: Optional[
|
390
|
+
credential: Optional[
|
391
|
+
Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "AsyncTokenCredential", TokenCredential]
|
392
|
+
] = None,
|
372
393
|
) -> Any:
|
373
394
|
if isinstance(credential, str):
|
374
395
|
if not account_name:
|
@@ -388,8 +409,12 @@ def _format_shared_key_credential(
|
|
388
409
|
def parse_connection_str(
|
389
410
|
conn_str: str,
|
390
411
|
credential: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, TokenCredential]],
|
391
|
-
service: str
|
392
|
-
) -> Tuple[
|
412
|
+
service: str,
|
413
|
+
) -> Tuple[
|
414
|
+
str,
|
415
|
+
Optional[str],
|
416
|
+
Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, TokenCredential]],
|
417
|
+
]:
|
393
418
|
conn_str = conn_str.rstrip(";")
|
394
419
|
conn_settings_list = [s.split("=", 1) for s in conn_str.split(";")]
|
395
420
|
if any(len(tup) != 2 for tup in conn_settings_list):
|
@@ -411,14 +436,11 @@ def parse_connection_str(
|
|
411
436
|
if endpoints["secondary"] in conn_settings:
|
412
437
|
raise ValueError("Connection string specifies only secondary endpoint.")
|
413
438
|
try:
|
414
|
-
primary =(
|
439
|
+
primary = (
|
415
440
|
f"{conn_settings['DEFAULTENDPOINTSPROTOCOL']}://"
|
416
441
|
f"{conn_settings['ACCOUNTNAME']}.{service}.{conn_settings['ENDPOINTSUFFIX']}"
|
417
442
|
)
|
418
|
-
secondary =
|
419
|
-
f"{conn_settings['ACCOUNTNAME']}-secondary."
|
420
|
-
f"{service}.{conn_settings['ENDPOINTSUFFIX']}"
|
421
|
-
)
|
443
|
+
secondary = f"{conn_settings['ACCOUNTNAME']}-secondary." f"{service}.{conn_settings['ENDPOINTSUFFIX']}"
|
422
444
|
except KeyError:
|
423
445
|
pass
|
424
446
|
|
@@ -438,7 +460,7 @@ def parse_connection_str(
|
|
438
460
|
|
439
461
|
|
440
462
|
def create_configuration(**kwargs: Any) -> StorageConfiguration:
|
441
|
-
|
463
|
+
# Backwards compatibility if someone is not passing sdk_moniker
|
442
464
|
if not kwargs.get("sdk_moniker"):
|
443
465
|
kwargs["sdk_moniker"] = f"storage-{kwargs.pop('storage_sdk')}/{VERSION}"
|
444
466
|
config = StorageConfiguration(**kwargs)
|
@@ -64,18 +64,26 @@ class AsyncStorageAccountHostsMixin(object):
|
|
64
64
|
async def __aexit__(self, *args):
|
65
65
|
await self._client.__aexit__(*args)
|
66
66
|
|
67
|
-
async def close(self):
|
68
|
-
"""
|
67
|
+
async def close(self) -> None:
|
68
|
+
"""This method is to close the sockets opened by the client.
|
69
69
|
It need not be used when using with a context manager.
|
70
|
+
|
71
|
+
:return: None
|
72
|
+
:rtype: None
|
70
73
|
"""
|
71
74
|
await self._client.close()
|
72
75
|
|
73
76
|
def _format_query_string(
|
74
|
-
self,
|
75
|
-
|
77
|
+
self,
|
78
|
+
sas_token: Optional[str],
|
79
|
+
credential: Optional[
|
80
|
+
Union[str, Dict[str, str], "AzureNamedKeyCredential", "AzureSasCredential", AsyncTokenCredential]
|
81
|
+
],
|
76
82
|
snapshot: Optional[str] = None,
|
77
|
-
share_snapshot: Optional[str] = None
|
78
|
-
) -> Tuple[
|
83
|
+
share_snapshot: Optional[str] = None,
|
84
|
+
) -> Tuple[
|
85
|
+
str, Optional[Union[str, Dict[str, str], "AzureNamedKeyCredential", "AzureSasCredential", AsyncTokenCredential]]
|
86
|
+
]:
|
79
87
|
query_str = "?"
|
80
88
|
if snapshot:
|
81
89
|
query_str += f"snapshot={snapshot}&"
|
@@ -83,7 +91,8 @@ class AsyncStorageAccountHostsMixin(object):
|
|
83
91
|
query_str += f"sharesnapshot={share_snapshot}&"
|
84
92
|
if sas_token and isinstance(credential, AzureSasCredential):
|
85
93
|
raise ValueError(
|
86
|
-
"You cannot use AzureSasCredential when the resource URI also contains a Shared Access Signature."
|
94
|
+
"You cannot use AzureSasCredential when the resource URI also contains a Shared Access Signature."
|
95
|
+
)
|
87
96
|
if _is_credential_sastoken(credential):
|
88
97
|
query_str += credential.lstrip("?") # type: ignore [union-attr]
|
89
98
|
credential = None
|
@@ -92,35 +101,40 @@ class AsyncStorageAccountHostsMixin(object):
|
|
92
101
|
return query_str.rstrip("?&"), credential
|
93
102
|
|
94
103
|
def _create_pipeline(
|
95
|
-
self,
|
96
|
-
|
104
|
+
self,
|
105
|
+
credential: Optional[
|
106
|
+
Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, AsyncTokenCredential]
|
107
|
+
] = None,
|
108
|
+
**kwargs: Any,
|
97
109
|
) -> Tuple[StorageConfiguration, AsyncPipeline]:
|
98
110
|
self._credential_policy: Optional[
|
99
|
-
Union[AsyncStorageBearerTokenCredentialPolicy,
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
audience = str(kwargs.pop('audience')).rstrip('/') + DEFAULT_OAUTH_SCOPE
|
111
|
+
Union[AsyncStorageBearerTokenCredentialPolicy, SharedKeyCredentialPolicy, AzureSasCredentialPolicy]
|
112
|
+
] = None
|
113
|
+
if hasattr(credential, "get_token"):
|
114
|
+
if kwargs.get("audience"):
|
115
|
+
audience = str(kwargs.pop("audience")).rstrip("/") + DEFAULT_OAUTH_SCOPE
|
105
116
|
else:
|
106
117
|
audience = STORAGE_OAUTH_SCOPE
|
107
118
|
self._credential_policy = AsyncStorageBearerTokenCredentialPolicy(
|
108
|
-
|
119
|
+
cast(AsyncTokenCredential, credential), audience
|
120
|
+
)
|
109
121
|
elif isinstance(credential, SharedKeyCredentialPolicy):
|
110
122
|
self._credential_policy = credential
|
111
123
|
elif isinstance(credential, AzureSasCredential):
|
112
124
|
self._credential_policy = AzureSasCredentialPolicy(credential)
|
113
125
|
elif credential is not None:
|
114
126
|
raise TypeError(f"Unsupported credential: {type(credential)}")
|
115
|
-
config = kwargs.get(
|
116
|
-
if kwargs.get(
|
117
|
-
return config, kwargs[
|
118
|
-
transport = kwargs.get(
|
127
|
+
config = kwargs.get("_configuration") or create_configuration(**kwargs)
|
128
|
+
if kwargs.get("_pipeline"):
|
129
|
+
return config, kwargs["_pipeline"]
|
130
|
+
transport = kwargs.get("transport")
|
119
131
|
kwargs.setdefault("connection_timeout", CONNECTION_TIMEOUT)
|
120
132
|
kwargs.setdefault("read_timeout", READ_TIMEOUT)
|
121
133
|
if not transport:
|
122
134
|
try:
|
123
|
-
from azure.core.pipeline.transport import
|
135
|
+
from azure.core.pipeline.transport import ( # pylint: disable=non-abstract-transport-import
|
136
|
+
AioHttpTransport,
|
137
|
+
)
|
124
138
|
except ImportError as exc:
|
125
139
|
raise ImportError("Unable to create async transport. Please check aiohttp is installed.") from exc
|
126
140
|
transport = AioHttpTransport(**kwargs)
|
@@ -143,53 +157,41 @@ class AsyncStorageAccountHostsMixin(object):
|
|
143
157
|
HttpLoggingPolicy(**kwargs),
|
144
158
|
]
|
145
159
|
if kwargs.get("_additional_pipeline_policies"):
|
146
|
-
policies = policies + kwargs.get("_additional_pipeline_policies") #type: ignore
|
147
|
-
config.transport = transport
|
148
|
-
return config, AsyncPipeline(transport, policies=policies)
|
160
|
+
policies = policies + kwargs.get("_additional_pipeline_policies") # type: ignore
|
161
|
+
config.transport = transport # type: ignore
|
162
|
+
return config, AsyncPipeline(transport, policies=policies) # type: ignore
|
149
163
|
|
150
|
-
async def _batch_send(
|
151
|
-
self,
|
152
|
-
*reqs: "HttpRequest",
|
153
|
-
**kwargs: Any
|
154
|
-
) -> AsyncList["HttpResponse"]:
|
164
|
+
async def _batch_send(self, *reqs: "HttpRequest", **kwargs: Any) -> AsyncList["HttpResponse"]:
|
155
165
|
"""Given a series of request, do a Storage batch call.
|
156
166
|
|
157
167
|
:param HttpRequest reqs: A collection of HttpRequest objects.
|
158
|
-
:
|
168
|
+
:return: An AsyncList of HttpResponse objects.
|
159
169
|
:rtype: AsyncList[HttpResponse]
|
160
170
|
"""
|
161
171
|
# Pop it here, so requests doesn't feel bad about additional kwarg
|
162
172
|
raise_on_any_failure = kwargs.pop("raise_on_any_failure", True)
|
163
173
|
request = self._client._client.post( # pylint: disable=protected-access
|
164
174
|
url=(
|
165
|
-
f
|
175
|
+
f"{self.scheme}://{self.primary_hostname}/"
|
166
176
|
f"{kwargs.pop('path', '')}?{kwargs.pop('restype', '')}"
|
167
177
|
f"comp=batch{kwargs.pop('sas', '')}{kwargs.pop('timeout', '')}"
|
168
178
|
),
|
169
|
-
headers={
|
170
|
-
'x-ms-version': self.api_version
|
171
|
-
}
|
179
|
+
headers={"x-ms-version": self.api_version},
|
172
180
|
)
|
173
181
|
|
174
182
|
policies = [StorageHeadersPolicy()]
|
175
183
|
if self._credential_policy:
|
176
184
|
policies.append(self._credential_policy) # type: ignore
|
177
185
|
|
178
|
-
request.set_multipart_mixed(
|
179
|
-
*reqs,
|
180
|
-
policies=policies,
|
181
|
-
enforce_https=False
|
182
|
-
)
|
186
|
+
request.set_multipart_mixed(*reqs, policies=policies, enforce_https=False)
|
183
187
|
|
184
|
-
pipeline_response = await self._pipeline.run(
|
185
|
-
request, **kwargs
|
186
|
-
)
|
188
|
+
pipeline_response = await self._pipeline.run(request, **kwargs)
|
187
189
|
response = pipeline_response.http_response
|
188
190
|
|
189
191
|
try:
|
190
192
|
if response.status_code not in [202]:
|
191
193
|
raise HttpResponseError(response=response)
|
192
|
-
parts = response.parts()
|
194
|
+
parts = response.parts() # Return an AsyncIterator
|
193
195
|
if raise_on_any_failure:
|
194
196
|
parts_list = []
|
195
197
|
async for part in parts:
|
@@ -197,7 +199,8 @@ class AsyncStorageAccountHostsMixin(object):
|
|
197
199
|
if any(p for p in parts_list if not 200 <= p.status_code < 300):
|
198
200
|
error = PartialBatchErrorException(
|
199
201
|
message="There is a partial failure in the batch operation.",
|
200
|
-
response=response,
|
202
|
+
response=response,
|
203
|
+
parts=parts_list,
|
201
204
|
)
|
202
205
|
raise error
|
203
206
|
return AsyncList(parts_list)
|
@@ -205,11 +208,16 @@ class AsyncStorageAccountHostsMixin(object):
|
|
205
208
|
except HttpResponseError as error:
|
206
209
|
process_storage_error(error)
|
207
210
|
|
211
|
+
|
208
212
|
def parse_connection_str(
|
209
213
|
conn_str: str,
|
210
214
|
credential: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, AsyncTokenCredential]],
|
211
|
-
service: str
|
212
|
-
) -> Tuple[
|
215
|
+
service: str,
|
216
|
+
) -> Tuple[
|
217
|
+
str,
|
218
|
+
Optional[str],
|
219
|
+
Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, AsyncTokenCredential]],
|
220
|
+
]:
|
213
221
|
conn_str = conn_str.rstrip(";")
|
214
222
|
conn_settings_list = [s.split("=", 1) for s in conn_str.split(";")]
|
215
223
|
if any(len(tup) != 2 for tup in conn_settings_list):
|
@@ -231,14 +239,11 @@ def parse_connection_str(
|
|
231
239
|
if endpoints["secondary"] in conn_settings:
|
232
240
|
raise ValueError("Connection string specifies only secondary endpoint.")
|
233
241
|
try:
|
234
|
-
primary =(
|
242
|
+
primary = (
|
235
243
|
f"{conn_settings['DEFAULTENDPOINTSPROTOCOL']}://"
|
236
244
|
f"{conn_settings['ACCOUNTNAME']}.{service}.{conn_settings['ENDPOINTSUFFIX']}"
|
237
245
|
)
|
238
|
-
secondary =
|
239
|
-
f"{conn_settings['ACCOUNTNAME']}-secondary."
|
240
|
-
f"{service}.{conn_settings['ENDPOINTSUFFIX']}"
|
241
|
-
)
|
246
|
+
secondary = f"{conn_settings['ACCOUNTNAME']}-secondary." f"{service}.{conn_settings['ENDPOINTSUFFIX']}"
|
242
247
|
except KeyError:
|
243
248
|
pass
|
244
249
|
|
@@ -256,11 +261,13 @@ def parse_connection_str(
|
|
256
261
|
secondary = secondary.replace(".blob.", ".dfs.")
|
257
262
|
return primary, secondary, credential
|
258
263
|
|
264
|
+
|
259
265
|
class AsyncTransportWrapper(AsyncHttpTransport):
|
260
266
|
"""Wrapper class that ensures that an inner client created
|
261
267
|
by a `get_client` method does not close the outer transport for the parent
|
262
268
|
when used in a context manager.
|
263
269
|
"""
|
270
|
+
|
264
271
|
def __init__(self, async_transport):
|
265
272
|
self._transport = async_transport
|
266
273
|
|